1use rustc_hir::def_id::DefId;
9use rustc_middle::ty::TyCtxt;
10
11use super::types::{
12 ContractExpr, ContractPlace, ContractProjection, NumericOp, NumericPredicate,
13 NumericUnaryOp, PlaceBase, Property, PropertyArg, PropertyKind, RelOp,
14};
15
16impl<'tcx> ContractPlace<'tcx> {
17 pub fn display_user_friendly(
18 &self,
19 tcx: TyCtxt<'tcx>,
20 struct_def_id: Option<DefId>,
21 fn_def_id: Option<DefId>,
22 ) -> String {
23 let has_projections = !self.projections.is_empty();
24
25 let base_str = match self.base {
26 PlaceBase::Return => {
27 if has_projections {
28 String::new()
29 } else {
30 "return".to_string()
31 }
32 }
33 PlaceBase::Arg(idx) => {
34 if let Some(fn_def_id) = fn_def_id
35 && tcx.is_mir_available(fn_def_id)
36 {
37 let mir_local = idx + 1;
38 let body = tcx.optimized_mir(fn_def_id);
39 if mir_local < body.local_decls.len() {
40 let local = rustc_middle::mir::Local::from_usize(mir_local);
41 let span = body.local_decls[local].source_info.span;
42 if let Ok(snippet) = tcx.sess.source_map().span_to_snippet(span)
43 && !snippet.is_empty()
44 {
45 return snippet;
46 }
47 }
48 }
49 format!("arg{}", idx)
50 }
51 PlaceBase::Local(n) => {
52 if n == 0 {
53 "return".to_string()
54 } else if let Some(fn_def_id) = fn_def_id
55 && tcx.is_mir_available(fn_def_id)
56 {
57 let body = tcx.optimized_mir(fn_def_id);
58 if n < body.local_decls.len() {
59 let local = rustc_middle::mir::Local::from_usize(n);
60 let span = body.local_decls[local].source_info.span;
61 if let Ok(snippet) = tcx.sess.source_map().span_to_snippet(span) {
62 snippet
63 } else {
64 format!("arg{}", n)
65 }
66 } else {
67 format!("arg{}", n)
68 }
69 } else {
70 format!("arg{}", n)
71 }
72 }
73 };
74
75 let base_str = base_str
76 .strip_prefix("&mut ")
77 .unwrap_or(&base_str)
78 .to_string();
79 let base_str = base_str.strip_prefix("&").unwrap_or(&base_str).to_string();
80
81 if self.projections.is_empty() {
82 return base_str;
83 }
84
85 let mut result = base_str;
86 for projection in &self.projections {
87 match projection {
88 ContractProjection::Field { index, ty: _ } => {
89 let field_name =
90 crate::helpers::name::resolve_field_name(tcx, index, struct_def_id);
91 if result.is_empty() {
92 result = field_name;
93 } else {
94 result.push_str(&format!(".{}", field_name));
95 }
96 }
97 ContractProjection::Downcast { .. } => {
98 result.push_str(".unwrap_some()");
99 }
100 ContractProjection::IterElements => {
101 result.push_str(".iter()");
102 }
103 }
104 }
105 result
106 }
107}
108
109impl<'tcx> NumericPredicate<'tcx> {
110 pub fn display_user_friendly(
111 &self,
112 tcx: TyCtxt<'tcx>,
113 struct_def_id: Option<DefId>,
114 fn_def_id: Option<DefId>,
115 ) -> String {
116 let op_str = match self.op {
117 RelOp::Eq => "==",
118 RelOp::Ne => "!=",
119 RelOp::Lt => "<",
120 RelOp::Le => "<=",
121 RelOp::Gt => ">",
122 RelOp::Ge => ">=",
123 };
124 format!(
125 "{} {} {}",
126 display_expr_user_friendly(&self.lhs, tcx, struct_def_id, fn_def_id),
127 op_str,
128 display_expr_user_friendly(&self.rhs, tcx, struct_def_id, fn_def_id),
129 )
130 }
131}
132
133pub fn display_expr_user_friendly<'tcx>(
134 expr: &ContractExpr<'tcx>,
135 tcx: TyCtxt<'tcx>,
136 struct_def_id: Option<DefId>,
137 fn_def_id: Option<DefId>,
138) -> String {
139 match expr {
140 ContractExpr::Const(n) => format!("{n}"),
141 ContractExpr::ConstParam { name, .. } => name.clone(),
142 ContractExpr::Place(p) => p.display_user_friendly(tcx, struct_def_id, fn_def_id),
143 ContractExpr::SizeOf(ty) => format!("size_of({ty})"),
144 ContractExpr::AlignOf(ty) => format!("align_of({ty})"),
145 ContractExpr::Len(e) => {
146 format!(
147 "len({})",
148 display_expr_user_friendly(e, tcx, struct_def_id, fn_def_id)
149 )
150 }
151 ContractExpr::IndexAccess { slice, index } => {
152 format!(
153 "index_access({}, {})",
154 display_expr_user_friendly(slice, tcx, struct_def_id, fn_def_id),
155 display_expr_user_friendly(index, tcx, struct_def_id, fn_def_id),
156 )
157 }
158 ContractExpr::Binary { op, lhs, rhs } => {
159 let op_str = match op {
160 NumericOp::Add => "+",
161 NumericOp::Sub => "-",
162 NumericOp::Mul => "*",
163 NumericOp::Div => "/",
164 NumericOp::Rem => "%",
165 NumericOp::BitAnd => "&",
166 NumericOp::BitOr => "|",
167 NumericOp::BitXor => "^",
168 };
169 format!(
170 "{} {} {}",
171 display_expr_user_friendly(lhs, tcx, struct_def_id, fn_def_id),
172 op_str,
173 display_expr_user_friendly(rhs, tcx, struct_def_id, fn_def_id),
174 )
175 }
176 ContractExpr::Unary { op, expr } => {
177 let op_str = match op {
178 NumericUnaryOp::Not => "!",
179 NumericUnaryOp::Neg => "-",
180 };
181 format!(
182 "{}{}",
183 op_str,
184 display_expr_user_friendly(expr, tcx, struct_def_id, fn_def_id),
185 )
186 }
187 ContractExpr::Min { a, b } => {
188 format!(
189 "min({}, {})",
190 display_expr_user_friendly(a, tcx, struct_def_id, fn_def_id),
191 display_expr_user_friendly(b, tcx, struct_def_id, fn_def_id),
192 )
193 }
194 ContractExpr::Max { a, b } => {
195 format!(
196 "max({}, {})",
197 display_expr_user_friendly(a, tcx, struct_def_id, fn_def_id),
198 display_expr_user_friendly(b, tcx, struct_def_id, fn_def_id),
199 )
200 }
201 ContractExpr::If {
202 cond,
203 then_expr,
204 else_expr,
205 } => {
206 format!(
207 "if {} {{ {} }} else {{ {} }}",
208 cond.display_user_friendly(tcx, struct_def_id, fn_def_id),
209 display_expr_user_friendly(then_expr, tcx, struct_def_id, fn_def_id),
210 display_expr_user_friendly(else_expr, tcx, struct_def_id, fn_def_id),
211 )
212 }
213 _ => format!("{:?}", expr),
214 }
215}
216
217impl<'tcx> PropertyArg<'tcx> {
218 pub fn display_for_report(
219 &self,
220 tcx: TyCtxt<'tcx>,
221 struct_def_id: Option<DefId>,
222 fn_def_id: Option<DefId>,
223 ) -> String {
224 match self {
225 PropertyArg::Ty(ty) => format!("{}", ty),
226 PropertyArg::Expr(expr) => {
227 display_expr_user_friendly(expr, tcx, struct_def_id, fn_def_id)
228 }
229 PropertyArg::Predicates(preds) => {
230 let p: Vec<_> = preds
231 .iter()
232 .map(|pred| pred.display_user_friendly(tcx, struct_def_id, fn_def_id))
233 .collect();
234 p.join(" && ")
235 }
236 PropertyArg::Ident(s) => s.clone(),
237 }
238 }
239}
240
241impl<'tcx> Property<'tcx> {
242 pub fn display_for_report(
243 &self,
244 tcx: TyCtxt<'tcx>,
245 struct_def_id: Option<DefId>,
246 fn_def_id: Option<DefId>,
247 ) -> String {
248 if let Some(name) = self.origin_name() {
251 let args = self.origin_args().map(|a| a.join(", ")).unwrap_or_default();
252 return format!("{name}({args})");
253 }
254
255 let kind_str = match self.kind() {
256 Some(k) => format!("{k:?}"),
257 None => "Or".to_string(),
258 };
259
260 if matches!(self.kind(), Some(PropertyKind::InBound))
261 && matches!(
262 self.args().first(),
263 Some(PropertyArg::Expr(ContractExpr::IndexAccess { .. }))
264 )
265 {
266 if let Some(PropertyArg::Expr(ContractExpr::IndexAccess { slice, index })) =
267 self.args().first()
268 {
269 let slice_str = display_expr_user_friendly(slice, tcx, struct_def_id, fn_def_id);
270 let index_str = display_expr_user_friendly(index, tcx, struct_def_id, fn_def_id);
271 return format!("{}({}, {})", kind_str, slice_str, index_str);
272 }
273 }
274
275 if matches!(self.kind(), Some(PropertyKind::ValidNum))
276 && let Some(PropertyArg::Predicates(preds)) = self.args().first()
277 {
278 let inner: Vec<String> = preds
279 .iter()
280 .map(|pred| pred.display_user_friendly(tcx, struct_def_id, fn_def_id))
281 .collect();
282 if inner.is_empty() {
283 return format!("{}", kind_str);
284 }
285 return format!("{}({})", kind_str, inner.join(", "));
286 }
287
288 let args: Vec<String> = self
289 .args()
290 .iter()
291 .map(|arg| arg.display_for_report(tcx, struct_def_id, fn_def_id))
292 .collect();
293 if args.is_empty() {
294 kind_str
295 } else {
296 format!("{}({})", kind_str, args.join(", "))
297 }
298 }
299}