1use rustc_hir::def_id::DefId;
8use rustc_middle::ty::TyCtxt;
9use syn::Expr;
10
11use crate::helpers::name::access_ident_recursive;
12
13use super::spec;
14use super::types::*;
15
16impl<'tcx> Property<'tcx> {
17 fn parse_from_spec(
20 tcx: TyCtxt<'tcx>,
21 def_id: DefId,
22 spec: &spec::PropertySpec,
23 exprs: &[Expr],
24 ) -> Self {
25 let mut prop = match spec.build {
26 spec::BuildKind::Uniform => Self::build_uniform(tcx, def_id, spec, exprs),
27 spec::BuildKind::Size => Self::build_size(tcx, def_id, exprs),
28 spec::BuildKind::Allocated => Self::build_allocated(tcx, def_id, exprs),
29 spec::BuildKind::InBound => Self::build_inbound(tcx, def_id, exprs),
30 spec::BuildKind::NonOverlap => Self::build_nonoverlap(tcx, def_id, exprs),
31 spec::BuildKind::ValidNum => Self::build_validnum(tcx, def_id, exprs),
32 spec::BuildKind::Pinned => Self::build_pinned(tcx, def_id, exprs),
33 spec::BuildKind::SplitTransmute => Self::build_split_transmute(tcx, def_id, exprs),
34 spec::BuildKind::Targets => Self::build_targets(spec, tcx, def_id, exprs),
35 spec::BuildKind::ContainNoType => Self::build_contain_no_type(tcx, def_id, exprs),
36 spec::BuildKind::TobeSpecified => Self::new_simple(PropertyKind::Unknown),
37 };
38 prop.set_contract_kind(spec.contract_kind);
42 prop
43 }
44
45 fn resolve_arg(
52 tcx: TyCtxt<'tcx>,
53 def_id: DefId,
54 tag: &str,
55 arg_kind: spec::ArgKind,
56 expr: &Expr,
57 ) -> Option<PropertyArg<'tcx>> {
58 match arg_kind {
59 spec::ArgKind::Target => Some(super::resolve::parse_target_arg(tcx, def_id, expr)),
60 spec::ArgKind::Ty => {
61 super::resolve::parse_type(tcx, def_id, expr, tag).map(PropertyArg::Ty)
62 }
63 spec::ArgKind::Expr => Some(PropertyArg::Expr(super::resolve::expr_to_pest(
64 tcx, def_id, expr,
65 ))),
66 spec::ArgKind::Ident => {
67 let s = access_ident_recursive(expr).map(|(name, _)| name)?;
68 Some(PropertyArg::Ident(s))
69 }
70 }
71 }
72
73 fn build_uniform(
75 tcx: TyCtxt<'tcx>,
76 def_id: DefId,
77 spec: &spec::PropertySpec,
78 exprs: &[Expr],
79 ) -> Self {
80 let Some(form) = spec.forms.iter().find(|f| f.len() == exprs.len()) else {
81 let expected: Vec<usize> = spec.forms.iter().map(|f| f.len()).collect();
82 rap_error!(
83 "Wrong args length for {:?} Tag! expected one of {expected:?}, got {}",
84 spec.tag,
85 exprs.len()
86 );
87 return Self::new_simple(PropertyKind::Unknown);
88 };
89 let mut args: Vec<PropertyArg<'tcx>> = Vec::with_capacity(exprs.len());
90 for (expr, &arg_kind) in exprs.iter().zip(form.iter()) {
91 let Some(arg) = Self::resolve_arg(tcx, def_id, spec.tag, arg_kind, expr) else {
92 return Self::new_simple(PropertyKind::Unknown);
93 };
94 args.push(arg);
95 }
96 Self::new_atom(spec.kind, args)
97 }
98
99 pub(crate) fn new(tcx: TyCtxt<'tcx>, def_id: DefId, name: &str, exprs: &[Expr]) -> Self {
100 match spec::find_spec(name) {
101 Some(spec) => Self::parse_from_spec(tcx, def_id, spec, exprs),
102 None => Self::new_simple(PropertyKind::Unknown),
103 }
104 }
105
106 fn build_size(tcx: TyCtxt<'tcx>, def_id: DefId, exprs: &[Expr]) -> Self {
109 match exprs {
110 [ty_expr, const_expr] => {
111 let Some(ty) = super::resolve::parse_type(tcx, def_id, ty_expr, "Size") else {
112 return Self::new_simple(PropertyKind::Unknown);
113 };
114 if let Some((ident, _)) = access_ident_recursive(const_expr) {
115 if ident == "sized" || ident == "unsized" {
116 return Self::new_atom(
117 PropertyKind::Size,
118 vec![PropertyArg::Ty(ty), PropertyArg::Ident(ident)],
119 );
120 }
121 }
122 let c = super::resolve::expr_to_pest(tcx, def_id, const_expr);
123 Self::new_atom(
124 PropertyKind::Size,
125 vec![PropertyArg::Ty(ty), PropertyArg::Expr(c)],
126 )
127 }
128 _ => {
129 rap_error!(
130 "Wrong args length for Size Tag! expected 2, got {}",
131 exprs.len()
132 );
133 Self::new_simple(PropertyKind::Unknown)
134 }
135 }
136 }
137
138 fn build_allocated(tcx: TyCtxt<'tcx>, def_id: DefId, exprs: &[Expr]) -> Self {
139 match exprs {
140 [target] => Self::new_atom(
141 PropertyKind::Allocated,
142 vec![super::resolve::parse_target_arg(tcx, def_id, target)],
143 ),
144 [target_expr, ty_expr, len_expr] => {
145 let target = super::resolve::parse_target_arg(tcx, def_id, target_expr);
146 let Some(ty) = super::resolve::parse_type(tcx, def_id, ty_expr, "Allocated") else {
147 return Self::new_simple(PropertyKind::Unknown);
148 };
149 let length = super::resolve::expr_to_pest(tcx, def_id, len_expr);
150 Self::new_atom(
151 PropertyKind::Allocated,
152 vec![target, PropertyArg::Ty(ty), PropertyArg::Expr(length)],
153 )
154 }
155 [target_expr, ty_expr, len_expr, allocator_expr] => {
156 let target = super::resolve::parse_target_arg(tcx, def_id, target_expr);
157 let Some(ty) = super::resolve::parse_type(tcx, def_id, ty_expr, "Allocated") else {
158 return Self::new_simple(PropertyKind::Unknown);
159 };
160 let length = super::resolve::expr_to_pest(tcx, def_id, len_expr);
161 let allocator = access_ident_recursive(allocator_expr)
162 .map(|(name, _)| name)
163 .unwrap_or_else(|| "global".to_string());
164 Self::new_atom(
165 PropertyKind::Allocated,
166 vec![
167 target,
168 PropertyArg::Ty(ty),
169 PropertyArg::Expr(length),
170 PropertyArg::Ident(allocator),
171 ],
172 )
173 }
174 _ => {
175 rap_error!(
176 "Wrong args length for Allocated Tag! expected 1, 3 or 4, got {}",
177 exprs.len()
178 );
179 Self::new_simple(PropertyKind::Unknown)
180 }
181 }
182 }
183
184 fn build_inbound(tcx: TyCtxt<'tcx>, def_id: DefId, exprs: &[Expr]) -> Self {
185 match exprs {
186 [_target, ty_expr, len_expr] => {
187 let target = super::resolve::parse_target_arg(tcx, def_id, &exprs[0]);
188 let Some(ty) = super::resolve::parse_type(tcx, def_id, ty_expr, "InBound") else {
189 return Self::new_simple(PropertyKind::Unknown);
190 };
191 let length = super::resolve::expr_to_pest(tcx, def_id, len_expr);
192 Self::new_atom(
193 PropertyKind::InBound,
194 vec![target, PropertyArg::Ty(ty), PropertyArg::Expr(length)],
195 )
196 }
197 [target, index_expr] => {
198 let slice = super::resolve::expr_to_pest(tcx, def_id, target);
199 let index = super::resolve::expr_to_pest(tcx, def_id, index_expr);
200 if matches!(slice, ContractExpr::Unknown) || matches!(index, ContractExpr::Unknown)
201 {
202 return Self::new_simple(PropertyKind::Unknown);
203 }
204 let for_each = super::place::detect_array_for_each(tcx, def_id, index_expr);
206 let mut prop = Self::new_atom(
207 PropertyKind::InBound,
208 vec![PropertyArg::Expr(ContractExpr::IndexAccess {
209 slice: Box::new(slice),
210 index: Box::new(index),
211 })],
212 );
213 prop.set_for_each(for_each);
214 prop
215 }
216 _ => {
217 rap_error!(
218 "Wrong args length for InBound Tag! expected 2 or 3, got {}",
219 exprs.len()
220 );
221 Self::new_simple(PropertyKind::Unknown)
222 }
223 }
224 }
225
226 fn build_nonoverlap(tcx: TyCtxt<'tcx>, def_id: DefId, exprs: &[Expr]) -> Self {
227 match exprs {
228 [indices] => {
229 let target = super::resolve::parse_target_arg(tcx, def_id, indices);
230 Self::new_atom(PropertyKind::NonOverlap, vec![target])
231 }
232 [a, b, ty_expr, count_expr] => {
233 let Some(ty) = super::resolve::parse_type(tcx, def_id, ty_expr, "NonOverlap")
234 else {
235 return Self::new_simple(PropertyKind::Unknown);
236 };
237 let left = super::resolve::parse_target_arg(tcx, def_id, a);
238 let right = super::resolve::parse_target_arg(tcx, def_id, b);
239 let count = super::resolve::expr_to_pest(tcx, def_id, count_expr);
240 Self::new_atom(
241 PropertyKind::NonOverlap,
242 vec![left, right, PropertyArg::Ty(ty), PropertyArg::Expr(count)],
243 )
244 }
245 _ => {
246 rap_error!(
247 "Wrong args length for NonOverlap Tag! expected 1 or 4, got {}",
248 exprs.len()
249 );
250 Self::new_simple(PropertyKind::Unknown)
251 }
252 }
253 }
254
255 fn build_validnum(tcx: TyCtxt<'tcx>, def_id: DefId, exprs: &[Expr]) -> Self {
256 let predicates = super::resolve::parse_valid_num(tcx, def_id, exprs);
257 if predicates.is_empty() {
258 Self::new_simple(PropertyKind::Unknown)
259 } else {
260 Self::new_atom(
261 PropertyKind::ValidNum,
262 vec![PropertyArg::Predicates(predicates)],
263 )
264 }
265 }
266
267 fn build_targets(
268 spec: &spec::PropertySpec,
269 tcx: TyCtxt<'tcx>,
270 def_id: DefId,
271 exprs: &[Expr],
272 ) -> Self {
273 let args = exprs
274 .iter()
275 .map(|expr| super::resolve::parse_target_arg(tcx, def_id, expr))
276 .collect();
277 Self::new_atom(spec.kind, args)
278 }
279
280 fn build_pinned(tcx: TyCtxt<'tcx>, def_id: DefId, exprs: &[Expr]) -> Self {
281 match exprs {
282 [ptr_expr, lifetime_expr] => {
283 let target = super::resolve::parse_target_arg(tcx, def_id, ptr_expr);
284 let Some((lifetime, _)) = access_ident_recursive(lifetime_expr) else {
285 return Self::new_simple(PropertyKind::Unknown);
286 };
287 Self::new_atom(
288 PropertyKind::Pinned,
289 vec![target, PropertyArg::Ident(lifetime)],
290 )
291 }
292 _ => {
293 rap_error!(
294 "Wrong args length for Pinned Tag! expected 2, got {}",
295 exprs.len()
296 );
297 Self::new_simple(PropertyKind::Unknown)
298 }
299 }
300 }
301
302 fn build_split_transmute(tcx: TyCtxt<'tcx>, def_id: DefId, exprs: &[Expr]) -> Self {
303 if !Self::check_arg_length(exprs.len(), 2, "SplitTransmute") {
304 return Self::new_simple(PropertyKind::Unknown);
305 }
306 let src_elem = super::resolve::unwrap_array_expr(tcx, def_id, &exprs[0]);
307 let dst_elem = super::resolve::unwrap_array_expr(tcx, def_id, &exprs[1]);
308 let (Some(src_elem), Some(dst_elem)) = (src_elem, dst_elem) else {
309 return Self::new_simple(PropertyKind::Unknown);
310 };
311 Self::new_atom(
312 PropertyKind::SplitTransmute,
313 vec![PropertyArg::Ty(src_elem), PropertyArg::Ty(dst_elem)],
314 )
315 }
316
317 fn build_contain_no_type(tcx: TyCtxt<'tcx>, def_id: DefId, exprs: &[Expr]) -> Self {
318 if exprs.len() < 2 {
319 rap_error!(
320 "Wrong args length for ContainNoType Tag! expected at least 2 (ty, ident...), got {}",
321 exprs.len()
322 );
323 return Self::new_simple(PropertyKind::Unknown);
324 }
325 let Some(ty) = super::resolve::parse_type(tcx, def_id, &exprs[0], "ContainNoType") else {
326 return Self::new_simple(PropertyKind::Unknown);
327 };
328 let mut args = vec![PropertyArg::Ty(ty)];
329 for expr in &exprs[1..] {
330 let Some((name, _)) = access_ident_recursive(expr) else {
331 return Self::new_simple(PropertyKind::Unknown);
332 };
333 args.push(PropertyArg::Ident(name));
334 }
335 Self::new_atom(PropertyKind::ContainNoType, args)
336 }
337
338 fn new_simple(kind: PropertyKind) -> Self {
339 Self::new_atom(kind, Vec::new())
340 }
341
342 pub(crate) fn parse_list(
347 tcx: TyCtxt<'tcx>,
348 def_id: DefId,
349 name: &str,
350 exprs: &[Expr],
351 ) -> Vec<Self> {
352 if let Some(props) = super::compound::expand_compound(tcx, def_id, name, exprs) {
355 return props;
356 }
357 let mut props = if name == "any" {
358 Self::parse_any(tcx, def_id, exprs)
359 } else {
360 vec![Self::new(tcx, def_id, name, exprs)]
361 };
362 for prop in &mut props {
363 if let Property::Atom(atom) = prop {
364 if atom.for_each.is_none() {
365 for arg in &mut atom.args {
366 atom.for_each = super::place::strip_for_each(arg);
367 if atom.for_each.is_some() {
368 break;
369 }
370 }
371 }
372 }
373 }
374 props
375 }
376
377 fn parse_any(tcx: TyCtxt<'tcx>, def_id: DefId, exprs: &[Expr]) -> Vec<Self> {
392 if exprs.len() < 2 {
393 rap_error!(
394 "any(...) requires at least 2 disjuncts, got {}",
395 exprs.len()
396 );
397 return vec![Self::new_simple(PropertyKind::Unknown)];
398 }
399
400 let mut disjuncts: Vec<Vec<(String, Vec<Expr>)>> = Vec::with_capacity(exprs.len());
401 for expr in exprs {
402 let Some(parts) = Self::disjunct_parts(expr) else {
403 rap_error!(
404 "any(...) disjuncts must be property applications or (P1, P2, ...) groups"
405 );
406 return vec![Self::new_simple(PropertyKind::Unknown)];
407 };
408 disjuncts.push(parts);
409 }
410
411 let mut or_disjuncts: Vec<Self> = Vec::with_capacity(disjuncts.len());
412 for parts in disjuncts {
413 let mut conjuncts: Vec<Self> = Vec::new();
414 for (name, args) in parts {
415 conjuncts.extend(Self::parse_list(tcx, def_id, &name, &args));
416 }
417 or_disjuncts.push(Self::conjunction(conjuncts));
418 }
419 vec![Self::new_or(or_disjuncts)]
420 }
421
422 fn disjunct_parts(expr: &Expr) -> Option<Vec<(String, Vec<Expr>)>> {
425 match expr {
426 Expr::Tuple(tuple) => tuple.elems.iter().map(Self::call_parts).collect(),
427 Expr::Paren(paren) => Self::call_parts(&paren.expr).map(|parts| vec![parts]),
428 _ => Self::call_parts(expr).map(|parts| vec![parts]),
429 }
430 }
431
432 fn call_parts(expr: &Expr) -> Option<(String, Vec<Expr>)> {
434 let Expr::Call(call) = expr else {
435 return None;
436 };
437 let Expr::Path(path) = call.func.as_ref() else {
438 return None;
439 };
440 let name = path.path.get_ident()?.to_string();
441 Some((name, call.args.iter().cloned().collect()))
442 }
443
444 fn check_arg_length(expr_len: usize, required_len: usize, sp: &str) -> bool {
445 if expr_len != required_len {
446 rap_error!(
447 "Wrong args length for {:?} Tag! expected {required_len}, got {expr_len}",
448 sp
449 );
450 return false;
451 }
452 true
453 }
454}