1use rustc_hir::def_id::DefId;
2use rustc_middle::mir::BinOp as MirBinOp;
3use rustc_middle::ty::{GenericParamDefKind, Ty, TyCtxt};
4use safety_parser::syn::{
5 BinOp as SynBinOp, Expr, GenericArgument, Lit, PathArguments, Type, UnOp,
6};
7
8use super::helpers::{
9 access_ident_recursive, match_ty_with_ident, parse_expr_into_local_and_ty,
10 parse_expr_into_number,
11};
12
13#[derive(Clone, Debug)]
14pub enum PlaceBase {
15 Return,
16 Arg(usize),
17 Local(usize),
18}
19
20#[derive(Clone, Debug)]
21pub enum ContractProjection<'tcx> {
22 Field { index: usize, ty: Option<Ty<'tcx>> },
23}
24
25#[derive(Clone, Debug)]
26pub struct ContractPlace<'tcx> {
27 pub base: PlaceBase,
28 pub projections: Vec<ContractProjection<'tcx>>,
29}
30
31impl<'tcx> ContractPlace<'tcx> {
32 pub fn local(base: usize, fields: Vec<(usize, Ty<'tcx>)>) -> Self {
33 Self {
34 base: if base == 0 {
35 PlaceBase::Return
36 } else {
37 PlaceBase::Local(base)
38 },
39 projections: fields
40 .into_iter()
41 .map(|(index, ty)| ContractProjection::Field {
42 index,
43 ty: Some(ty),
44 })
45 .collect(),
46 }
47 }
48
49 pub fn arg(index: usize) -> Self {
50 Self {
51 base: PlaceBase::Arg(index),
52 projections: Vec::new(),
53 }
54 }
55
56 pub fn local_base(&self) -> Option<usize> {
57 match self.base {
58 PlaceBase::Return => Some(0),
59 PlaceBase::Local(local) => Some(local),
60 PlaceBase::Arg(_) => None,
61 }
62 }
63
64 pub fn field_indices(&self) -> Vec<usize> {
65 self.projections
66 .iter()
67 .map(|projection| match projection {
68 ContractProjection::Field { index, .. } => *index,
69 })
70 .collect()
71 }
72}
73
74#[derive(Clone, Copy, Debug)]
75pub enum NumericOp {
76 Add,
77 Sub,
78 Mul,
79 Div,
80 Rem,
81 BitAnd,
82 BitOr,
83 BitXor,
84}
85
86impl NumericOp {
87 fn from_syn(op: &SynBinOp) -> Option<Self> {
88 match op {
89 SynBinOp::Add(_) => Some(Self::Add),
90 SynBinOp::Sub(_) => Some(Self::Sub),
91 SynBinOp::Mul(_) => Some(Self::Mul),
92 SynBinOp::Div(_) => Some(Self::Div),
93 SynBinOp::Rem(_) => Some(Self::Rem),
94 SynBinOp::BitAnd(_) => Some(Self::BitAnd),
95 SynBinOp::BitOr(_) => Some(Self::BitOr),
96 SynBinOp::BitXor(_) => Some(Self::BitXor),
97 _ => None,
98 }
99 }
100}
101
102#[derive(Clone, Copy, Debug)]
103pub enum NumericUnaryOp {
104 Not,
105 Neg,
106}
107
108impl NumericUnaryOp {
109 fn from_syn(op: &UnOp) -> Option<Self> {
110 match op {
111 UnOp::Not(_) => Some(Self::Not),
112 UnOp::Neg(_) => Some(Self::Neg),
113 _ => None,
114 }
115 }
116}
117
118#[derive(Clone, Debug)]
119pub enum ContractExpr<'tcx> {
120 Place(ContractPlace<'tcx>),
121 Const(u128),
122 ConstParam {
123 index: u32,
124 name: String,
125 },
126 SizeOf(Ty<'tcx>),
127 AlignOf(Ty<'tcx>),
128 Len(Box<ContractExpr<'tcx>>),
129 IndexAccess {
130 slice: Box<ContractExpr<'tcx>>,
131 index: Box<ContractExpr<'tcx>>,
132 },
133 Binary {
134 op: NumericOp,
135 lhs: Box<ContractExpr<'tcx>>,
136 rhs: Box<ContractExpr<'tcx>>,
137 },
138 Unary {
139 op: NumericUnaryOp,
140 expr: Box<ContractExpr<'tcx>>,
141 },
142 Unknown,
143}
144
145impl<'tcx> ContractExpr<'tcx> {
146 pub fn new_var(base: usize) -> Self {
147 Self::Place(ContractPlace::local(base, Vec::new()))
148 }
149
150 pub fn new_value(value: usize) -> Self {
151 Self::Const(value as u128)
152 }
153
154 pub fn new_unknown() -> Self {
155 Self::Unknown
156 }
157
158 pub fn get_var_base(&self) -> Option<usize> {
159 match self {
160 Self::Place(place) => place.local_base(),
161 _ => None,
162 }
163 }
164}
165
166#[derive(Clone, Copy, Debug)]
167pub enum RelOp {
168 Eq,
169 Ne,
170 Lt,
171 Le,
172 Gt,
173 Ge,
174}
175
176impl RelOp {
177 pub fn from_mir(op: MirBinOp) -> Option<Self> {
178 match op {
179 MirBinOp::Eq => Some(Self::Eq),
180 MirBinOp::Ne => Some(Self::Ne),
181 MirBinOp::Lt => Some(Self::Lt),
182 MirBinOp::Le => Some(Self::Le),
183 MirBinOp::Gt => Some(Self::Gt),
184 MirBinOp::Ge => Some(Self::Ge),
185 _ => None,
186 }
187 }
188
189 fn from_syn(op: &SynBinOp) -> Option<Self> {
190 match op {
191 SynBinOp::Eq(_) => Some(Self::Eq),
192 SynBinOp::Ne(_) => Some(Self::Ne),
193 SynBinOp::Lt(_) => Some(Self::Lt),
194 SynBinOp::Le(_) => Some(Self::Le),
195 SynBinOp::Gt(_) => Some(Self::Gt),
196 SynBinOp::Ge(_) => Some(Self::Ge),
197 _ => None,
198 }
199 }
200
201 pub fn reversed(self) -> Self {
202 match self {
203 Self::Eq => Self::Eq,
204 Self::Ne => Self::Ne,
205 Self::Lt => Self::Gt,
206 Self::Le => Self::Ge,
207 Self::Gt => Self::Lt,
208 Self::Ge => Self::Le,
209 }
210 }
211}
212
213#[derive(Clone, Debug)]
214pub struct NumericPredicate<'tcx> {
215 pub lhs: ContractExpr<'tcx>,
216 pub op: RelOp,
217 pub rhs: ContractExpr<'tcx>,
218}
219
220impl<'tcx> NumericPredicate<'tcx> {
221 pub fn new(lhs: ContractExpr<'tcx>, op: RelOp, rhs: ContractExpr<'tcx>) -> Self {
222 Self { lhs, op, rhs }
223 }
224
225 pub fn from_mir_locals(lhs: usize, rhs: usize, op: MirBinOp) -> Option<Self> {
226 RelOp::from_mir(op)
227 .map(|rel| Self::new(ContractExpr::new_var(lhs), rel, ContractExpr::new_var(rhs)))
228 }
229}
230
231#[derive(Clone, Debug, PartialEq)]
232pub enum PropertyKind {
233 Align,
234 Size,
235 NoPadding,
236 NonNull,
237 Allocated,
238 InBound,
239 NonOverlap,
240 ValidNum,
241 ValidString,
242 ValidCStr,
243 Init,
244 Unwrap,
245 Typed,
246 Owning,
247 Alias,
248 Alive,
249 Pinned,
250 NonVolatile,
251 Opened,
252 Trait,
253 Unreachable,
254 ValidPtr,
255 ValidSlice,
256 Deref,
257 Ptr2Ref,
258 Layout,
259 ValidTransmute,
260 NonSize,
261 Nullable,
262 Unknown,
263}
264
265#[derive(Clone, Debug)]
266pub enum PropertyArg<'tcx> {
267 Place(ContractPlace<'tcx>),
268 Ty(Ty<'tcx>),
269 Expr(ContractExpr<'tcx>),
270 Predicates(Vec<NumericPredicate<'tcx>>),
271 Ident(String),
272}
273
274#[derive(Clone, Debug)]
275pub struct Property<'tcx> {
276 pub kind: PropertyKind,
277 pub args: Vec<PropertyArg<'tcx>>,
278}
279
280impl<'tcx> Property<'tcx> {
281 pub fn new(tcx: TyCtxt<'tcx>, def_id: DefId, name: &str, exprs: &[Expr]) -> Self {
282 match name {
283 "Align" => {
284 if !Self::check_arg_length(exprs.len(), 2, "Align") {
285 return Self::new_simple(PropertyKind::Unknown);
286 }
287 let target = Self::parse_target_arg(tcx, def_id, &exprs[0]);
288 let Some(ty) = Self::parse_type(tcx, def_id, &exprs[1], "Align") else {
289 return Self::new_simple(PropertyKind::Unknown);
290 };
291 Self::new_with_args(PropertyKind::Align, vec![target, PropertyArg::Ty(ty)])
292 }
293 "Size" => Self::new_with_target(PropertyKind::Size, tcx, def_id, exprs),
294 "NoPadding" => Self::new_with_target(PropertyKind::NoPadding, tcx, def_id, exprs),
295 "NonNull" => Self::new_with_target(PropertyKind::NonNull, tcx, def_id, exprs),
296 "Allocated" => match exprs {
297 [target] => Self::new_with_args(
298 PropertyKind::Allocated,
299 vec![Self::parse_target_arg(tcx, def_id, target)],
300 ),
301 [target_expr, ty_expr, len_expr] => {
302 let target = Self::parse_target_arg(tcx, def_id, target_expr);
303 let Some(ty) = Self::parse_type(tcx, def_id, ty_expr, "Allocated") else {
304 return Self::new_simple(PropertyKind::Unknown);
305 };
306 let length = Self::parse_contract_expr(tcx, def_id, len_expr, "Allocated");
307 Self::new_with_args(
308 PropertyKind::Allocated,
309 vec![target, PropertyArg::Ty(ty), PropertyArg::Expr(length)],
310 )
311 }
312 _ => {
313 Self::check_arg_length(exprs.len(), 3, "Allocated");
314 Self::new_simple(PropertyKind::Unknown)
315 }
316 },
317 "InBound" | "InBounded" => match exprs {
318 [expr] => {
319 let expr = Self::parse_contract_expr(tcx, def_id, expr, "InBound");
320 if matches!(expr, ContractExpr::IndexAccess { .. }) {
321 Self::new_with_args(PropertyKind::InBound, vec![PropertyArg::Expr(expr)])
322 } else {
323 Self::new_simple(PropertyKind::Unknown)
324 }
325 }
326 [_target, ty_expr, len_expr] => {
327 let target = Self::parse_target_arg(tcx, def_id, &exprs[0]);
328 let Some(ty) = Self::parse_type(tcx, def_id, ty_expr, "InBound") else {
329 return Self::new_simple(PropertyKind::Unknown);
330 };
331 let length = Self::parse_contract_expr(tcx, def_id, len_expr, "InBound");
332 Self::new_with_args(
333 PropertyKind::InBound,
334 vec![target, PropertyArg::Ty(ty), PropertyArg::Expr(length)],
335 )
336 }
337 [target, len_expr] => {
338 let Some(ty) = Self::parse_target_type(tcx, def_id, target) else {
339 return Self::new_simple(PropertyKind::Unknown);
340 };
341 let target = Self::parse_target_arg(tcx, def_id, target);
342 let length = Self::parse_contract_expr(tcx, def_id, len_expr, "InBound");
343 Self::new_with_args(
344 PropertyKind::InBound,
345 vec![target, PropertyArg::Ty(ty), PropertyArg::Expr(length)],
346 )
347 }
348 _ => {
349 Self::check_arg_length(exprs.len(), 3, "InBound");
350 Self::new_simple(PropertyKind::Unknown)
351 }
352 },
353 "NonOverlap" => match exprs {
354 [indices] => {
355 let target = Self::parse_target_arg(tcx, def_id, indices);
356 Self::new_with_args(PropertyKind::NonOverlap, vec![target])
357 }
358 _ => Self::new_with_targets(PropertyKind::NonOverlap, tcx, def_id, exprs),
359 },
360 "ValidNum" => {
361 let predicates = Self::parse_valid_num(tcx, def_id, exprs);
362 if predicates.is_empty() {
363 Self::new_simple(PropertyKind::Unknown)
364 } else {
365 Self::new_with_args(
366 PropertyKind::ValidNum,
367 vec![PropertyArg::Predicates(predicates)],
368 )
369 }
370 }
371 "ValidString" => Self::new_with_target(PropertyKind::ValidString, tcx, def_id, exprs),
372 "ValidCStr" => Self::new_with_target(PropertyKind::ValidCStr, tcx, def_id, exprs),
373 "Init" => {
374 if !Self::check_arg_length(exprs.len(), 3, "Init") {
375 return Self::new_simple(PropertyKind::Unknown);
376 }
377 let target = Self::parse_target_arg(tcx, def_id, &exprs[0]);
378 let Some(ty) = Self::parse_type(tcx, def_id, &exprs[1], "Init") else {
379 return Self::new_simple(PropertyKind::Unknown);
380 };
381 let length = Self::parse_contract_expr(tcx, def_id, &exprs[2], "Init");
382 Self::new_with_args(
383 PropertyKind::Init,
384 vec![target, PropertyArg::Ty(ty), PropertyArg::Expr(length)],
385 )
386 }
387 "Unwrap" => Self::new_with_target(PropertyKind::Unwrap, tcx, def_id, exprs),
388 "Typed" => {
389 if !Self::check_arg_length(exprs.len(), 2, "Typed") {
390 return Self::new_simple(PropertyKind::Unknown);
391 }
392 let target = Self::parse_target_arg(tcx, def_id, &exprs[0]);
393 let Some(ty) = Self::parse_type(tcx, def_id, &exprs[1], "Typed") else {
394 return Self::new_simple(PropertyKind::Unknown);
395 };
396 Self::new_with_args(PropertyKind::Typed, vec![target, PropertyArg::Ty(ty)])
397 }
398 "Owning" => Self::new_with_target(PropertyKind::Owning, tcx, def_id, exprs),
399 "Alias" => Self::new_with_target(PropertyKind::Alias, tcx, def_id, exprs),
400 "Alive" => Self::new_with_target(PropertyKind::Alive, tcx, def_id, exprs),
401 "Pinned" => Self::new_with_target(PropertyKind::Pinned, tcx, def_id, exprs),
402 "NonVolatile" => Self::new_with_target(PropertyKind::NonVolatile, tcx, def_id, exprs),
403 "Opened" => Self::new_with_target(PropertyKind::Opened, tcx, def_id, exprs),
404 "Trait" => Self::new_with_target(PropertyKind::Trait, tcx, def_id, exprs),
405 "Unreachable" => Self::new_with_target(PropertyKind::Unreachable, tcx, def_id, exprs),
406 "ValidPtr" => {
407 if !Self::check_arg_length(exprs.len(), 3, "ValidPtr") {
408 return Self::new_simple(PropertyKind::Unknown);
409 }
410 let target = Self::parse_target_arg(tcx, def_id, &exprs[0]);
411 let Some(ty) = Self::parse_type(tcx, def_id, &exprs[1], "ValidPtr") else {
412 return Self::new_simple(PropertyKind::Unknown);
413 };
414 let length = Self::parse_contract_expr(tcx, def_id, &exprs[2], "ValidPtr");
415 Self::new_with_args(
416 PropertyKind::ValidPtr,
417 vec![target, PropertyArg::Ty(ty), PropertyArg::Expr(length)],
418 )
419 }
420 "ValidSlice" => match exprs {
421 [target_expr, ty_expr] => {
422 let target = Self::parse_target_arg(tcx, def_id, target_expr);
423 let Some(ty) = Self::parse_type(tcx, def_id, ty_expr, "ValidSlice") else {
424 return Self::new_simple(PropertyKind::Unknown);
425 };
426 Self::new_with_args(PropertyKind::ValidSlice, vec![target, PropertyArg::Ty(ty)])
427 }
428 [target_expr] => {
429 let Some(ty) = Self::parse_target_type(tcx, def_id, target_expr) else {
430 return Self::new_simple(PropertyKind::Unknown);
431 };
432 let target = Self::parse_target_arg(tcx, def_id, target_expr);
433 Self::new_with_args(PropertyKind::ValidSlice, vec![target, PropertyArg::Ty(ty)])
434 }
435 _ => {
436 Self::check_arg_length(exprs.len(), 2, "ValidSlice");
437 Self::new_simple(PropertyKind::Unknown)
438 }
439 },
440 "Deref" => match exprs {
441 [_target, ty_expr, len_expr] => {
442 let target = Self::parse_target_arg(tcx, def_id, &exprs[0]);
443 let Some(ty) = Self::parse_type(tcx, def_id, ty_expr, "Deref") else {
444 return Self::new_simple(PropertyKind::Unknown);
445 };
446 let length = Self::parse_contract_expr(tcx, def_id, len_expr, "Deref");
447 Self::new_with_args(
448 PropertyKind::Deref,
449 vec![target, PropertyArg::Ty(ty), PropertyArg::Expr(length)],
450 )
451 }
452 [target, len_expr] => {
453 let Some(ty) = Self::parse_target_type(tcx, def_id, target) else {
454 return Self::new_simple(PropertyKind::Unknown);
455 };
456 let target = Self::parse_target_arg(tcx, def_id, target);
457 let length = Self::parse_contract_expr(tcx, def_id, len_expr, "Deref");
458 Self::new_with_args(
459 PropertyKind::Deref,
460 vec![target, PropertyArg::Ty(ty), PropertyArg::Expr(length)],
461 )
462 }
463 _ => Self::new_with_target(PropertyKind::Deref, tcx, def_id, exprs),
464 },
465 "Ptr2Ref" | "ValidPtr2Ref" => {
466 Self::new_with_target(PropertyKind::Ptr2Ref, tcx, def_id, exprs)
467 }
468 "Layout" => Self::new_with_target(PropertyKind::Layout, tcx, def_id, exprs),
469 "ValidTransmute" => {
470 if !Self::check_arg_length(exprs.len(), 2, "ValidTransmute") {
471 return Self::new_simple(PropertyKind::Unknown);
472 }
473 let Some(src_ty) = Self::parse_type(tcx, def_id, &exprs[0], "ValidTransmute") else {
474 return Self::new_simple(PropertyKind::Unknown);
475 };
476 let Some(dst_ty) = Self::parse_type(tcx, def_id, &exprs[1], "ValidTransmute") else {
477 return Self::new_simple(PropertyKind::Unknown);
478 };
479 Self::new_with_args(
480 PropertyKind::ValidTransmute,
481 vec![PropertyArg::Ty(src_ty), PropertyArg::Ty(dst_ty)],
482 )
483 }
484 "NonSize" => Self::new_simple(PropertyKind::NonSize),
485 "Null" => Self::new_with_target(PropertyKind::Nullable, tcx, def_id, exprs),
486 _ => Self::new_simple(PropertyKind::Unknown),
487 }
488 }
489
490 pub fn new_partial_order(lhs: usize, rhs: usize, op: MirBinOp) -> Self {
491 if let Some(predicate) = NumericPredicate::from_mir_locals(lhs, rhs, op) {
492 Self::new_with_args(
493 PropertyKind::ValidNum,
494 vec![PropertyArg::Predicates(vec![predicate])],
495 )
496 } else {
497 Self::new_simple(PropertyKind::Unknown)
498 }
499 }
500
501 pub fn new_obj_boundary(ty: Ty<'tcx>, len: ContractExpr<'tcx>) -> Self {
502 Self::new_with_args(
503 PropertyKind::InBound,
504 vec![
505 PropertyArg::Expr(ContractExpr::Unknown),
506 PropertyArg::Ty(ty),
507 PropertyArg::Expr(len),
508 ],
509 )
510 }
511
512 fn new_simple(kind: PropertyKind) -> Self {
513 Self {
514 kind,
515 args: Vec::new(),
516 }
517 }
518
519 fn new_with_args(kind: PropertyKind, args: Vec<PropertyArg<'tcx>>) -> Self {
520 Self { kind, args }
521 }
522
523 fn new_with_target(
524 kind: PropertyKind,
525 tcx: TyCtxt<'tcx>,
526 def_id: DefId,
527 exprs: &[Expr],
528 ) -> Self {
529 let args = exprs
530 .first()
531 .map(|expr| Self::parse_target_arg(tcx, def_id, expr))
532 .into_iter()
533 .collect();
534 Self { kind, args }
535 }
536
537 fn new_with_targets(
538 kind: PropertyKind,
539 tcx: TyCtxt<'tcx>,
540 def_id: DefId,
541 exprs: &[Expr],
542 ) -> Self {
543 let args = exprs
544 .iter()
545 .map(|expr| Self::parse_target_arg(tcx, def_id, expr))
546 .collect();
547 Self { kind, args }
548 }
549
550 fn check_arg_length(expr_len: usize, required_len: usize, sp: &str) -> bool {
551 if expr_len != required_len {
552 rap_error!(
553 "Wrong args length for {:?} Tag! expected {required_len}, got {expr_len}",
554 sp
555 );
556 return false;
557 }
558 true
559 }
560
561 fn parse_type(tcx: TyCtxt<'tcx>, def_id: DefId, expr: &Expr, sp: &str) -> Option<Ty<'tcx>> {
562 let ty_ident_full = access_ident_recursive(expr);
563 if ty_ident_full.is_none() {
564 rap_debug!("Incorrect expression for the type of {:?} Tag!", sp);
565 return None;
566 }
567 let ty_ident = ty_ident_full.unwrap().0;
568 let ty = match_ty_with_ident(tcx, def_id, ty_ident);
569 if ty.is_none() {
570 rap_debug!("Cannot get type in {:?} Tag!", sp);
571 }
572 ty
573 }
574
575 fn parse_target_type(tcx: TyCtxt<'tcx>, def_id: DefId, expr: &Expr) -> Option<Ty<'tcx>> {
576 parse_expr_into_local_and_ty(tcx, def_id, expr).map(|(_, _, ty)| ty)
577 }
578
579 fn parse_target_arg(tcx: TyCtxt<'tcx>, def_id: DefId, expr: &Expr) -> PropertyArg<'tcx> {
580 Self::parse_contract_place(tcx, def_id, expr)
581 .map(PropertyArg::Place)
582 .unwrap_or_else(|| {
583 PropertyArg::Expr(Self::parse_contract_expr(tcx, def_id, expr, "target"))
584 })
585 }
586
587 fn parse_contract_expr(
588 tcx: TyCtxt<'tcx>,
589 def_id: DefId,
590 expr: &Expr,
591 sp: &str,
592 ) -> ContractExpr<'tcx> {
593 match expr {
594 Expr::Paren(paren) => Self::parse_contract_expr(tcx, def_id, &paren.expr, sp),
595 Expr::Group(group) => Self::parse_contract_expr(tcx, def_id, &group.expr, sp),
596 Expr::Lit(expr_lit) => match &expr_lit.lit {
597 Lit::Int(lit_int) => lit_int
598 .base10_parse::<u128>()
599 .map(ContractExpr::Const)
600 .unwrap_or(ContractExpr::Unknown),
601 _ => ContractExpr::Unknown,
602 },
603 Expr::Call(expr_call) => {
604 if let Some(expr) = Self::parse_index_access_expr(tcx, def_id, expr_call) {
605 return expr;
606 }
607 if let Some(expr) = Self::parse_len_expr(tcx, def_id, expr_call) {
608 return expr;
609 }
610 if let Some(expr) = Self::parse_layout_expr(tcx, def_id, expr_call) {
611 return expr;
612 }
613 ContractExpr::Unknown
614 }
615 Expr::Field(expr_field)
617 if matches!(&expr_field.member, safety_parser::syn::Member::Named(ident) if ident == "len") =>
618 {
619 ContractExpr::Len(Box::new(Self::parse_contract_expr(
620 tcx,
621 def_id,
622 &expr_field.base,
623 sp,
624 )))
625 }
626 Expr::Unary(expr_unary) => {
627 let Some(op) = NumericUnaryOp::from_syn(&expr_unary.op) else {
628 return ContractExpr::Unknown;
629 };
630 ContractExpr::Unary {
631 op,
632 expr: Box::new(Self::parse_contract_expr(tcx, def_id, &expr_unary.expr, sp)),
633 }
634 }
635 Expr::Binary(expr_binary) => {
636 let Some(op) = NumericOp::from_syn(&expr_binary.op) else {
637 return ContractExpr::Unknown;
638 };
639 ContractExpr::Binary {
640 op,
641 lhs: Box::new(Self::parse_contract_expr(
642 tcx,
643 def_id,
644 &expr_binary.left,
645 sp,
646 )),
647 rhs: Box::new(Self::parse_contract_expr(
648 tcx,
649 def_id,
650 &expr_binary.right,
651 sp,
652 )),
653 }
654 }
655 _ => {
656 if let Some(place) = Self::parse_contract_place(tcx, def_id, expr) {
657 ContractExpr::Place(place)
658 } else if let Some(expr) = Self::parse_const_param(tcx, def_id, expr) {
659 expr
660 } else if let Some(value) = Self::parse_builtin_const(tcx, expr) {
661 ContractExpr::Const(value)
662 } else if let Some(value) = parse_expr_into_number(expr) {
663 ContractExpr::new_value(value)
664 } else {
665 rap_debug!(
666 "Numeric expression in {:?} could not be resolved: {:?}",
667 sp,
668 expr
669 );
670 ContractExpr::Unknown
671 }
672 }
673 }
674 }
675
676 fn parse_index_access_expr(
677 tcx: TyCtxt<'tcx>,
678 def_id: DefId,
679 expr_call: &safety_parser::syn::ExprCall,
680 ) -> Option<ContractExpr<'tcx>> {
681 let Expr::Path(func_path) = expr_call.func.as_ref() else {
682 return None;
683 };
684 let name = func_path.path.segments.last()?.ident.to_string();
685 if name != "index_access" || expr_call.args.len() != 2 {
686 return None;
687 }
688
689 let mut args = expr_call.args.iter();
690 let slice = args.next()?;
691 let index = args.next()?;
692 Some(ContractExpr::IndexAccess {
693 slice: Box::new(Self::parse_contract_expr(
694 tcx,
695 def_id,
696 slice,
697 "index_access",
698 )),
699 index: Box::new(Self::parse_contract_expr(
700 tcx,
701 def_id,
702 index,
703 "index_access",
704 )),
705 })
706 }
707
708 fn parse_len_expr(
709 tcx: TyCtxt<'tcx>,
710 def_id: DefId,
711 expr_call: &safety_parser::syn::ExprCall,
712 ) -> Option<ContractExpr<'tcx>> {
713 let Expr::Path(func_path) = expr_call.func.as_ref() else {
714 return None;
715 };
716 let name = func_path.path.segments.last()?.ident.to_string();
717 if name != "len" || expr_call.args.len() != 1 {
718 return None;
719 }
720 let target = expr_call.args.first()?;
721 Some(ContractExpr::Len(Box::new(Self::parse_contract_expr(
722 tcx, def_id, target, "len",
723 ))))
724 }
725
726 fn parse_layout_expr(
727 tcx: TyCtxt<'tcx>,
728 def_id: DefId,
729 expr_call: &safety_parser::syn::ExprCall,
730 ) -> Option<ContractExpr<'tcx>> {
731 let Expr::Path(func_path) = expr_call.func.as_ref() else {
732 return None;
733 };
734 let last = func_path.path.segments.last()?;
735 let name = last.ident.to_string();
736 if name != "size_of" && name != "align_of" {
737 return None;
738 }
739
740 let ty = if let Some(arg) = expr_call.args.first() {
741 Self::parse_type_opt(tcx, def_id, arg)
742 } else {
743 Self::parse_turbofish_type(tcx, def_id, &last.arguments, "ValidNum")
744 }?;
745
746 Some(match name.as_str() {
747 "size_of" => ContractExpr::SizeOf(ty),
748 "align_of" => ContractExpr::AlignOf(ty),
749 _ => return None,
750 })
751 }
752
753 fn parse_turbofish_type(
754 tcx: TyCtxt<'tcx>,
755 def_id: DefId,
756 arguments: &PathArguments,
757 sp: &str,
758 ) -> Option<Ty<'tcx>> {
759 let PathArguments::AngleBracketed(args) = arguments else {
760 return None;
761 };
762 args.args.iter().find_map(|arg| match arg {
763 GenericArgument::Type(ty) => Self::parse_syn_type(tcx, def_id, ty, sp),
764 _ => None,
765 })
766 }
767
768 fn parse_type_opt(tcx: TyCtxt<'tcx>, def_id: DefId, expr: &Expr) -> Option<Ty<'tcx>> {
769 if let Expr::Path(expr_path) = expr
770 && let Some(segment) = expr_path.path.segments.last()
771 {
772 return match_ty_with_ident(tcx, def_id, segment.ident.to_string());
773 }
774 let ty_ident = access_ident_recursive(expr)?.0;
775 match_ty_with_ident(tcx, def_id, ty_ident)
776 }
777
778 fn parse_syn_type(tcx: TyCtxt<'tcx>, def_id: DefId, ty: &Type, sp: &str) -> Option<Ty<'tcx>> {
779 let Type::Path(type_path) = ty else {
780 return None;
781 };
782 let ident = type_path.path.segments.last()?.ident.to_string();
783 match_ty_with_ident(tcx, def_id, ident).or_else(|| {
784 rap_debug!("Cannot get type in {:?} Tag from {:?}", sp, type_path);
785 None
786 })
787 }
788
789 fn parse_builtin_const(tcx: TyCtxt<'tcx>, expr: &Expr) -> Option<u128> {
790 let Expr::Path(expr_path) = expr else {
791 return None;
792 };
793 let mut segments = expr_path.path.segments.iter();
794 let first = segments.next()?.ident.to_string();
795 let second = segments.next()?.ident.to_string();
796 if segments.next().is_some() || second != "MAX" {
797 return None;
798 }
799
800 let pointer_bits = tcx.data_layout.pointer_size().bits();
801 match first.as_str() {
802 "isize" => Some((1_u128 << (pointer_bits - 1)) - 1),
803 "usize" => Some((1_u128 << pointer_bits) - 1),
804 _ => None,
805 }
806 }
807
808 fn parse_const_param(
809 tcx: TyCtxt<'tcx>,
810 def_id: DefId,
811 expr: &Expr,
812 ) -> Option<ContractExpr<'tcx>> {
813 let Expr::Path(expr_path) = expr else {
814 return None;
815 };
816 let ident = expr_path.path.get_ident()?.to_string();
817 let mut generics = Some(tcx.generics_of(def_id));
818 while let Some(current) = generics {
819 if let Some(param) = current.own_params.iter().find(|param| {
820 matches!(param.kind, GenericParamDefKind::Const { .. })
821 && param.name.as_str() == ident
822 }) {
823 return Some(ContractExpr::ConstParam {
824 index: param.index,
825 name: ident,
826 });
827 }
828 generics = current.parent.map(|parent| tcx.generics_of(parent));
829 }
830 None
831 }
832
833 fn parse_contract_place(
834 tcx: TyCtxt<'tcx>,
835 def_id: DefId,
836 expr: &Expr,
837 ) -> Option<ContractPlace<'tcx>> {
838 if let Some((base, fields, _ty)) = parse_expr_into_local_and_ty(tcx, def_id, expr) {
839 return Some(ContractPlace::local(base, fields));
840 }
841 Self::parse_named_place(expr)
842 }
843
844 fn parse_named_place(expr: &Expr) -> Option<ContractPlace<'tcx>> {
845 if let Expr::Path(expr_path) = expr {
846 if let Some(ident) = expr_path.path.get_ident() {
847 let s = ident.to_string();
848 if let Some(num_str) = s.strip_prefix("Arg_") {
849 if let Ok(idx) = num_str.parse::<usize>() {
850 return Some(ContractPlace::arg(idx));
851 }
852 }
853 if s == "return" {
854 return Some(ContractPlace {
855 base: PlaceBase::Return,
856 projections: Vec::new(),
857 });
858 }
859 }
860 }
861 None
862 }
863
864 fn parse_valid_num(
865 tcx: TyCtxt<'tcx>,
866 def_id: DefId,
867 exprs: &[Expr],
868 ) -> Vec<NumericPredicate<'tcx>> {
869 match exprs {
870 [] => Vec::new(),
871 [expr] => Self::parse_numeric_predicate(tcx, def_id, expr)
872 .into_iter()
873 .collect(),
874 [value, range, ..] => {
875 if let Some(predicates) = Self::parse_interval_predicates(tcx, def_id, value, range)
876 {
877 predicates
878 } else {
879 Self::parse_numeric_predicate(tcx, def_id, value)
880 .into_iter()
881 .collect()
882 }
883 }
884 }
885 }
886
887 fn parse_numeric_predicate(
888 tcx: TyCtxt<'tcx>,
889 def_id: DefId,
890 expr: &Expr,
891 ) -> Option<NumericPredicate<'tcx>> {
892 if let Expr::Binary(expr_binary) = expr {
893 if let Some(op) = RelOp::from_syn(&expr_binary.op) {
894 return Some(NumericPredicate::new(
895 Self::parse_contract_expr(tcx, def_id, &expr_binary.left, "ValidNum"),
896 op,
897 Self::parse_contract_expr(tcx, def_id, &expr_binary.right, "ValidNum"),
898 ));
899 }
900 }
901
902 Some(NumericPredicate::new(
903 Self::parse_contract_expr(tcx, def_id, expr, "ValidNum"),
904 RelOp::Ne,
905 ContractExpr::Const(0),
906 ))
907 }
908
909 fn parse_interval_predicates(
910 tcx: TyCtxt<'tcx>,
911 def_id: DefId,
912 value: &Expr,
913 range: &Expr,
914 ) -> Option<Vec<NumericPredicate<'tcx>>> {
915 match range {
916 Expr::Array(array) if array.elems.len() == 2 => {
917 let mut elems = array.elems.iter();
918 let lower = elems.next().unwrap();
919 let upper = elems.next().unwrap();
920 Some(Self::build_interval_predicates(
921 tcx, def_id, value, lower, true, upper, true,
922 ))
923 }
924 Expr::Lit(expr_lit) => {
925 let Lit::Str(range_lit) = &expr_lit.lit else {
926 return None;
927 };
928 Self::parse_string_interval(tcx, def_id, value, &range_lit.value())
929 }
930 _ => None,
931 }
932 }
933
934 fn parse_string_interval(
935 tcx: TyCtxt<'tcx>,
936 def_id: DefId,
937 value: &Expr,
938 raw_range: &str,
939 ) -> Option<Vec<NumericPredicate<'tcx>>> {
940 let trimmed = raw_range.trim();
941 if trimmed.len() < 5 {
942 return None;
943 }
944
945 let lower_inclusive = trimmed.starts_with('[');
946 let upper_inclusive = trimmed.ends_with(']');
947 if !(lower_inclusive || trimmed.starts_with('('))
948 || !(upper_inclusive || trimmed.ends_with(')'))
949 {
950 return None;
951 }
952
953 let body = &trimmed[1..trimmed.len() - 1];
954 let (lower_raw, upper_raw) = body.split_once(',')?;
955 let lower = safety_parser::syn::parse_str::<Expr>(lower_raw.trim()).ok()?;
956 let upper = safety_parser::syn::parse_str::<Expr>(upper_raw.trim()).ok()?;
957
958 Some(Self::build_interval_predicates(
959 tcx,
960 def_id,
961 value,
962 &lower,
963 lower_inclusive,
964 &upper,
965 upper_inclusive,
966 ))
967 }
968
969 fn build_interval_predicates(
970 tcx: TyCtxt<'tcx>,
971 def_id: DefId,
972 value: &Expr,
973 lower: &Expr,
974 lower_inclusive: bool,
975 upper: &Expr,
976 upper_inclusive: bool,
977 ) -> Vec<NumericPredicate<'tcx>> {
978 let value_expr = Self::parse_contract_expr(tcx, def_id, value, "ValidNum");
979 let lower_expr = Self::parse_contract_expr(tcx, def_id, lower, "ValidNum");
980 let upper_expr = Self::parse_contract_expr(tcx, def_id, upper, "ValidNum");
981 vec![
982 NumericPredicate::new(
983 lower_expr,
984 if lower_inclusive {
985 RelOp::Le
986 } else {
987 RelOp::Lt
988 },
989 value_expr.clone(),
990 ),
991 NumericPredicate::new(
992 value_expr,
993 if upper_inclusive {
994 RelOp::Le
995 } else {
996 RelOp::Lt
997 },
998 upper_expr,
999 ),
1000 ]
1001 }
1002}