rapx/analysis/senryx/contracts/
property.rs1use crate::{
2 analysis::utils::fn_info::{
3 access_ident_recursive, match_ty_with_ident, parse_expr_into_local_and_ty,
4 parse_expr_into_number,
5 },
6 rap_error,
7};
8use rustc_hir::def_id::DefId;
9use rustc_middle::mir::BinOp;
10use rustc_middle::ty::Ty;
11use rustc_middle::ty::TyCtxt;
12use safety_parser::{safety::TagNameType, syn::Expr};
13
14#[derive(Clone, Debug)]
15pub enum CisRangeItem {
16 Var(usize, Vec<usize>),
17 Value(usize),
18 Unknown,
19}
20
21impl CisRangeItem {
22 pub fn get_var_base(&self) -> Option<usize> {
23 match self {
24 Self::Var(base, _) => Some(*base),
25 _ => None,
26 }
27 }
28
29 pub fn new_var(base: usize) -> Self {
30 Self::Var(base, Vec::new())
31 }
32
33 pub fn new_value(value: usize) -> Self {
34 Self::Value(value)
35 }
36
37 pub fn new_unknown() -> Self {
38 Self::Unknown
39 }
40}
41
42#[derive(Clone, Debug)]
43pub struct CisRange {
44 pub bin_op: BinOp,
45 pub range: CisRangeItem,
46}
47
48impl CisRange {
49 pub fn new(bin_op: BinOp, range: CisRangeItem) -> Self {
50 Self { bin_op, range }
51 }
52}
53
54#[derive(Clone, Debug)]
55pub enum PropertyContract<'tcx> {
56 Align(Ty<'tcx>),
58 Size(),
59 NoPadding,
60 NonNull,
61 Allocated(Ty<'tcx>, CisRangeItem),
63 InBound(Ty<'tcx>, CisRangeItem),
65 NonOverlap,
66 ValidNum(CisRange),
67 ValidString,
68 ValidCStr,
69 Init(Ty<'tcx>, CisRangeItem),
71 Unwrap,
72 Typed(Ty<'tcx>),
73 Owning,
74 Alias,
75 Alive,
76 Pinned,
77 NonVolatile,
78 Opened,
79 Trait,
80 Unreachable,
81 ValidPtr(Ty<'tcx>, CisRangeItem),
82 Deref,
83 Ptr2Ref,
84 Layout,
85 Unknown,
86}
87
88impl<'tcx> PropertyContract<'tcx> {
89 pub fn new(tcx: TyCtxt<'tcx>, def_id: DefId, name: &str, exprs: &Vec<Expr>) -> Self {
90 match name {
91 "Align" => {
92 Self::check_arg_length(exprs.len(), 2, "Align");
93 let ty = Self::parse_type(tcx, def_id, &exprs[1], "Align");
94 Self::Align(ty)
95 }
96 "Size" => Self::Size(),
97 "NoPadding" => Self::NoPadding,
98 "NonNull" => Self::NonNull,
99 "Allocated" => {
100 Self::check_arg_length(exprs.len(), 3, "Allocated");
101 let ty = Self::parse_type(tcx, def_id, &exprs[1], "Allocated");
102 let length = Self::parse_length(tcx, def_id, &exprs[2], "Allocated");
103 Self::Allocated(ty, length)
104 }
105 "InBound" => {
106 Self::check_arg_length(exprs.len(), 3, "InBound");
107 let ty = Self::parse_type(tcx, def_id, &exprs[1], "InBound");
108 let length = Self::parse_length(tcx, def_id, &exprs[2], "InBound");
109 Self::InBound(ty, length)
110 }
111 "NonOverlap" => Self::NonOverlap,
112 "ValidNum" => {
113 Self::check_arg_length(exprs.len(), 1, "ValidNum");
114 let bin_op = BinOp::Ne;
115 let length = Self::parse_length(tcx, def_id, &exprs[0], "ValidNum");
116 return Self::ValidNum(CisRange::new(bin_op, length));
117 }
118 "ValidString" => Self::ValidString,
119 "ValidCStr" => Self::ValidCStr,
120 "Init" => {
121 Self::check_arg_length(exprs.len(), 3, "Init");
122 let ty = Self::parse_type(tcx, def_id, &exprs[1], "Init");
123 let length = Self::parse_length(tcx, def_id, &exprs[2], "Init");
124 Self::Init(ty, length)
125 }
126 "Unwrap" => Self::Unwrap,
127 "Typed" => {
128 Self::check_arg_length(exprs.len(), 2, "Typed");
129 let ty = Self::parse_type(tcx, def_id, &exprs[1], "Typed");
130 Self::Typed(ty)
131 }
132 "Owning" => Self::Owning,
133 "Alias" => Self::Alias,
134 "Alive" => Self::Alive,
135 "Pinned" => Self::Pinned,
136 "NonVolatile" => Self::NonVolatile,
137 "Opened" => Self::Opened,
138 "Trait" => Self::Trait,
139 "Unreachable" => Self::Unreachable,
140 "ValidPtr" => {
141 Self::check_arg_length(exprs.len(), 3, "ValidPtr");
142 let ty = Self::parse_type(tcx, def_id, &exprs[1], "ValidPtr");
143 let length = Self::parse_length(tcx, def_id, &exprs[2], "ValidPtr");
144 Self::ValidPtr(ty, length)
145 }
146 "Deref" => Self::Deref,
147 "Ptr2Ref" => Self::Ptr2Ref,
148 "Layout" => Self::Layout,
149 _ => Self::Unknown,
150 }
151 }
152
153 pub fn new_patial_order(p: usize, op: BinOp) -> Self {
154 Self::ValidNum(CisRange::new(op, CisRangeItem::Var(p, Vec::new())))
155 }
156
157 pub fn new_obj_boundary(ty: Ty<'tcx>, len: CisRangeItem) -> Self {
158 Self::InBound(ty, len)
159 }
160
161 fn check_arg_length(expr_len: usize, required_len: usize, sp: &str) -> bool {
163 if expr_len != required_len {
164 panic!("Wrong args length for {:?} Tag!", sp);
165 }
166 true
167 }
168
169 fn parse_type(tcx: TyCtxt<'tcx>, def_id: DefId, expr: &Expr, sp: &str) -> Ty<'tcx> {
171 let ty_ident_full = access_ident_recursive(expr);
172 if ty_ident_full.is_none() {
173 rap_error!("Incorrect expression for the type of {:?} Tag!", sp);
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_error!("Cannot get type in {:?} Tag!", sp);
179 }
180 return ty.unwrap();
181 }
182
183 fn parse_length(tcx: TyCtxt<'tcx>, def_id: DefId, expr: &Expr, sp: &str) -> CisRangeItem {
185 if let Some(var_len) = parse_expr_into_local_and_ty(tcx, def_id, expr) {
186 CisRangeItem::Var(var_len.0, var_len.1.iter().map(|(x, _)| *x).collect())
187 } else if parse_expr_into_number(expr).is_some() {
188 CisRangeItem::Value(parse_expr_into_number(expr).unwrap())
189 } else {
190 rap_error!("Range length error in {:?} Tag!", sp);
191 CisRangeItem::Unknown
192 }
193 }
194}
195
196#[derive(Debug, Clone)]
197pub struct ContractualInvariantState<'tcx> {
198 pub contracts: Vec<PropertyContract<'tcx>>,
199}
200
201impl<'tcx> ContractualInvariantState<'tcx> {
202 pub fn new_default() -> Self {
203 Self {
204 contracts: Vec::new(),
205 }
206 }
207
208 pub fn add_contract(&mut self, contract: PropertyContract<'tcx>) {
209 self.contracts.push(contract);
210 }
211
212 pub fn get_contracts_length(&self) -> usize {
213 self.contracts.len()
214 }
215}