1pub mod contract_verification;
2pub mod contracts;
3pub mod lifetime_analysis;
4pub mod lifetime_shim;
5pub mod memory_safety;
6pub mod memory_shim;
7pub mod properties;
8pub mod quickcheck;
9
10use anyhow::Result;
11use depyler_core::hir::HirFunction;
12use serde::{Deserialize, Serialize};
13
14#[derive(Debug, Clone, Serialize, Deserialize)]
15pub struct PropertyVerifier {
16 pub enable_quickcheck: bool,
17 pub enable_contracts: bool,
18 pub test_iterations: usize,
19}
20
21#[derive(Debug, Clone, Serialize, Deserialize)]
22pub struct VerificationResult {
23 pub property: String,
24 pub status: PropertyStatus,
25 pub confidence: f64,
26 pub method: VerificationMethod,
27 pub counterexamples: Vec<TestCase>,
28}
29
30#[derive(Debug, Clone, Serialize, Deserialize)]
31pub enum PropertyStatus {
32 Proven,
33 HighConfidence,
34 Likely,
35 Unknown,
36 Violated(String),
37}
38
39#[derive(Debug, Clone, Serialize, Deserialize)]
40pub enum VerificationMethod {
41 Exhaustive,
42 PropertyTesting,
43 StaticAnalysis,
44 StructuralInduction,
45 Heuristic,
46}
47
48#[derive(Debug, Clone, Serialize, Deserialize)]
49pub struct TestCase {
50 pub inputs: Vec<serde_json::Value>,
51 pub expected_output: Option<serde_json::Value>,
52 pub actual_output: Option<serde_json::Value>,
53 pub error: Option<String>,
54}
55
56impl Default for PropertyVerifier {
57 fn default() -> Self {
58 Self {
59 enable_quickcheck: true,
60 enable_contracts: true,
61 test_iterations: 1000,
62 }
63 }
64}
65
66impl PropertyVerifier {
67 pub fn new() -> Self {
68 Self::default()
69 }
70
71 pub fn with_iterations(mut self, iterations: usize) -> Self {
72 self.test_iterations = iterations;
73 self
74 }
75
76 pub fn verify_function(&self, func: &HirFunction) -> Vec<VerificationResult> {
77 let mut results = vec![];
78
79 if let Some(result) = self.verify_type_preservation(func) {
81 results.push(result);
82 }
83
84 let mut memory_analyzer = memory_safety::MemorySafetyAnalyzer::new();
86 results.push(memory_analyzer.analyze_function(func));
87
88 let mut lifetime_analyzer = lifetime_analysis::LifetimeAnalyzer::new();
90 let lifetime_violations = lifetime_analyzer.analyze_function(func);
91 if lifetime_violations.is_empty() {
92 results.push(VerificationResult {
93 property: "lifetime_safety".into(),
94 status: PropertyStatus::Proven,
95 confidence: 1.0,
96 method: VerificationMethod::StaticAnalysis,
97 counterexamples: vec![],
98 });
99 } else {
100 results.push(VerificationResult {
101 property: "lifetime_safety".into(),
102 status: PropertyStatus::Violated(format!(
103 "{} lifetime violations found",
104 lifetime_violations.len()
105 )),
106 confidence: 1.0,
107 method: VerificationMethod::StaticAnalysis,
108 counterexamples: vec![],
109 });
110 }
111
112 let null_violations = memory_safety::check_null_safety(func);
114 if null_violations.is_empty() {
115 results.push(VerificationResult {
116 property: "null_safety".into(),
117 status: PropertyStatus::Proven,
118 confidence: 1.0,
119 method: VerificationMethod::StaticAnalysis,
120 counterexamples: vec![],
121 });
122 } else {
123 results.push(VerificationResult {
124 property: "null_safety".into(),
125 status: PropertyStatus::Violated(format!(
126 "{} violations found",
127 null_violations.len()
128 )),
129 confidence: 1.0,
130 method: VerificationMethod::StaticAnalysis,
131 counterexamples: vec![],
132 });
133 }
134
135 if func.properties.panic_free {
137 results.push(VerificationResult {
138 property: "panic_free".into(),
139 status: PropertyStatus::HighConfidence,
140 confidence: 0.95,
141 method: VerificationMethod::StaticAnalysis,
142 counterexamples: vec![],
143 });
144 }
145
146 if func.properties.always_terminates {
148 results.push(VerificationResult {
149 property: "termination".into(),
150 status: PropertyStatus::Proven,
151 confidence: 1.0,
152 method: VerificationMethod::StructuralInduction,
153 counterexamples: vec![],
154 });
155 }
156
157 if func.properties.is_pure {
159 results.push(VerificationResult {
160 property: "pure".into(),
161 status: PropertyStatus::HighConfidence,
162 confidence: 0.9,
163 method: VerificationMethod::StaticAnalysis,
164 counterexamples: vec![],
165 });
166 }
167
168 if func.annotations.thread_safety == depyler_annotations::ThreadSafety::Required {
170 results.push(self.verify_thread_safety(func));
171 }
172
173 results
174 }
175
176 fn verify_type_preservation(&self, func: &HirFunction) -> Option<VerificationResult> {
177 let all_typed = func
179 .params
180 .iter()
181 .all(|param| !matches!(param.ty, depyler_core::hir::Type::Unknown));
182
183 if all_typed && !matches!(func.ret_type, depyler_core::hir::Type::Unknown) {
184 Some(VerificationResult {
185 property: "type_preservation".into(),
186 status: PropertyStatus::Proven,
187 confidence: 1.0,
188 method: VerificationMethod::StaticAnalysis,
189 counterexamples: vec![],
190 })
191 } else {
192 Some(VerificationResult {
193 property: "type_preservation".into(),
194 status: PropertyStatus::Unknown,
195 confidence: 0.0,
196 method: VerificationMethod::StaticAnalysis,
197 counterexamples: vec![],
198 })
199 }
200 }
201
202 fn verify_thread_safety(&self, func: &HirFunction) -> VerificationResult {
203 let has_shared_state = self.detect_shared_state(func);
205 let has_proper_sync = func.annotations.interior_mutability
206 == depyler_annotations::InteriorMutability::ArcMutex;
207
208 if !has_shared_state || has_proper_sync {
209 VerificationResult {
210 property: "thread_safety".into(),
211 status: PropertyStatus::Proven,
212 confidence: 0.95,
213 method: VerificationMethod::StaticAnalysis,
214 counterexamples: vec![],
215 }
216 } else {
217 VerificationResult {
218 property: "thread_safety".into(),
219 status: PropertyStatus::Violated(
220 "Shared state without proper synchronization".to_string(),
221 ),
222 confidence: 1.0,
223 method: VerificationMethod::StaticAnalysis,
224 counterexamples: vec![],
225 }
226 }
227 }
228
229 fn detect_shared_state(&self, func: &HirFunction) -> bool {
230 func.annotations.ownership_model == depyler_annotations::OwnershipModel::Shared
232 }
233
234 pub fn generate_property_tests(&self, func: &HirFunction) -> Result<String> {
235 let test_code = properties::generate_quickcheck_tests(func, self.test_iterations)?;
236 Ok(test_code)
237 }
238}
239
240#[cfg(test)]
241mod tests {
242 use super::*;
243 use depyler_annotations::{
244 InteriorMutability, OwnershipModel, ThreadSafety, TranspilationAnnotations,
245 };
246 use depyler_core::hir::{HirExpr, HirStmt, Type};
247
248 fn create_test_function(name: &str, is_pure: bool, thread_safe: bool) -> HirFunction {
250 use smallvec::smallvec;
251
252 HirFunction {
253 name: name.to_string(),
254 params: smallvec![depyler_core::hir::HirParam::new("x".to_string(), Type::Int)],
255 ret_type: Type::Int,
256 body: vec![HirStmt::Return(Some(HirExpr::Var("x".to_string())))],
257 properties: depyler_core::hir::FunctionProperties {
258 is_pure,
259 always_terminates: true,
260 panic_free: true,
261 max_stack_depth: None,
262 can_fail: false,
263 error_types: vec![],
264 is_async: false,
265 is_generator: false,
266 },
267 annotations: TranspilationAnnotations {
268 thread_safety: if thread_safe {
269 ThreadSafety::Required
270 } else {
271 ThreadSafety::NotRequired
272 },
273 ownership_model: OwnershipModel::Owned,
274 interior_mutability: InteriorMutability::None,
275 ..Default::default()
276 },
277 docstring: None,
278 }
279 }
280
281 #[test]
282 fn test_property_verifier_creation() {
283 let verifier = PropertyVerifier::new();
284 assert!(verifier.enable_quickcheck);
285 assert!(verifier.enable_contracts);
286 assert_eq!(verifier.test_iterations, 1000);
287 }
288
289 #[test]
290 fn test_with_iterations() {
291 let verifier = PropertyVerifier::new().with_iterations(5000);
292 assert_eq!(verifier.test_iterations, 5000);
293 }
294
295 #[test]
296 fn test_verify_pure_function() {
297 let verifier = PropertyVerifier::new();
298 let func = create_test_function("pure_func", true, false);
299
300 let results = verifier.verify_function(&func);
301
302 assert!(!results.is_empty());
304
305 let purity_result = results.iter().find(|r| r.property == "pure");
307 assert!(purity_result.is_some());
308
309 let result = purity_result.unwrap();
310 assert!(matches!(result.status, PropertyStatus::HighConfidence));
311 assert!(result.confidence >= 0.9);
312 }
313
314 #[test]
315 fn test_verify_thread_safe_function() {
316 let verifier = PropertyVerifier::new();
317 let func = create_test_function("thread_safe_func", false, true);
318
319 let results = verifier.verify_function(&func);
320
321 let thread_safety_result = results.iter().find(|r| r.property == "thread_safety");
323 assert!(thread_safety_result.is_some());
324 }
325
326 #[test]
327 fn test_type_preservation_verification() {
328 let verifier = PropertyVerifier::new();
329 let mut func = create_test_function("typed_func", false, false);
330
331 let results = verifier.verify_function(&func);
333 let type_result = results
334 .iter()
335 .find(|r| r.property == "type_preservation")
336 .unwrap();
337 assert!(matches!(type_result.status, PropertyStatus::Proven));
338
339 func.ret_type = Type::Unknown;
341 let results = verifier.verify_function(&func);
342 let type_result = results
343 .iter()
344 .find(|r| r.property == "type_preservation")
345 .unwrap();
346 assert!(matches!(type_result.status, PropertyStatus::Unknown));
347 }
348
349 #[test]
350 fn test_memory_safety_verification() {
351 let verifier = PropertyVerifier::new();
352 let func = create_test_function("memory_safe_func", false, false);
353
354 let results = verifier.verify_function(&func);
355
356 let memory_result = results.iter().find(|r| r.property == "memory_safety");
358 assert!(memory_result.is_some());
359
360 let null_result = results.iter().find(|r| r.property == "null_safety");
361 assert!(null_result.is_some());
362 assert!(matches!(
363 null_result.unwrap().status,
364 PropertyStatus::Proven
365 ));
366 }
367
368 #[test]
369 fn test_property_status_serialization() {
370 use serde_json;
371
372 let statuses = vec![
373 PropertyStatus::Proven,
374 PropertyStatus::HighConfidence,
375 PropertyStatus::Likely,
376 PropertyStatus::Unknown,
377 PropertyStatus::Violated("test violation".to_string()),
378 ];
379
380 for status in statuses {
381 let serialized = serde_json::to_string(&status).unwrap();
382 let deserialized: PropertyStatus = serde_json::from_str(&serialized).unwrap();
383
384 match (&status, &deserialized) {
385 (PropertyStatus::Violated(s1), PropertyStatus::Violated(s2)) => assert_eq!(s1, s2),
386 _ => assert_eq!(
387 std::mem::discriminant(&status),
388 std::mem::discriminant(&deserialized)
389 ),
390 }
391 }
392 }
393
394 #[test]
395 fn test_verification_result_creation() {
396 let result = VerificationResult {
397 property: "test_property".to_string(),
398 status: PropertyStatus::Proven,
399 confidence: 1.0,
400 method: VerificationMethod::Exhaustive,
401 counterexamples: vec![],
402 };
403
404 assert_eq!(result.property, "test_property");
405 assert!(matches!(result.status, PropertyStatus::Proven));
406 assert_eq!(result.confidence, 1.0);
407 assert!(result.counterexamples.is_empty());
408 }
409
410 #[test]
415 fn test_verify_thread_safety_violated_shared_no_sync() {
416 let verifier = PropertyVerifier::new();
417 use smallvec::smallvec;
418 let func = HirFunction {
419 name: "unsafe_shared".to_string(),
420 params: smallvec![depyler_core::hir::HirParam::new("x".to_string(), Type::Int)],
421 ret_type: Type::Int,
422 body: vec![HirStmt::Return(Some(HirExpr::Var("x".to_string())))],
423 properties: Default::default(),
424 annotations: TranspilationAnnotations {
425 thread_safety: ThreadSafety::Required,
426 ownership_model: OwnershipModel::Shared,
427 interior_mutability: InteriorMutability::None,
428 ..Default::default()
429 },
430 docstring: None,
431 };
432
433 let results = verifier.verify_function(&func);
434 let thread_result = results.iter().find(|r| r.property == "thread_safety");
435 assert!(thread_result.is_some());
436 assert!(matches!(
437 thread_result.unwrap().status,
438 PropertyStatus::Violated(_)
439 ));
440 }
441
442 #[test]
443 fn test_verify_thread_safety_proven_with_arc_mutex() {
444 let verifier = PropertyVerifier::new();
445 use smallvec::smallvec;
446 let func = HirFunction {
447 name: "safe_shared".to_string(),
448 params: smallvec![depyler_core::hir::HirParam::new("x".to_string(), Type::Int)],
449 ret_type: Type::Int,
450 body: vec![HirStmt::Return(Some(HirExpr::Var("x".to_string())))],
451 properties: Default::default(),
452 annotations: TranspilationAnnotations {
453 thread_safety: ThreadSafety::Required,
454 ownership_model: OwnershipModel::Shared,
455 interior_mutability: InteriorMutability::ArcMutex,
456 ..Default::default()
457 },
458 docstring: None,
459 };
460
461 let results = verifier.verify_function(&func);
462 let thread_result = results.iter().find(|r| r.property == "thread_safety");
463 assert!(thread_result.is_some());
464 assert!(matches!(
465 thread_result.unwrap().status,
466 PropertyStatus::Proven
467 ));
468 }
469
470 #[test]
471 fn test_verify_function_no_pure_no_terminates() {
472 let verifier = PropertyVerifier::new();
473 use smallvec::smallvec;
474 let func = HirFunction {
475 name: "impure".to_string(),
476 params: smallvec![depyler_core::hir::HirParam::new("x".to_string(), Type::Int)],
477 ret_type: Type::Int,
478 body: vec![HirStmt::Return(Some(HirExpr::Var("x".to_string())))],
479 properties: depyler_core::hir::FunctionProperties {
480 is_pure: false,
481 always_terminates: false,
482 panic_free: false,
483 ..Default::default()
484 },
485 annotations: Default::default(),
486 docstring: None,
487 };
488
489 let results = verifier.verify_function(&func);
490 assert!(!results.iter().any(|r| r.property == "pure"));
492 assert!(!results.iter().any(|r| r.property == "termination"));
493 assert!(!results.iter().any(|r| r.property == "panic_free"));
494 }
495
496 #[test]
497 fn test_verify_type_preservation_unknown_param() {
498 let verifier = PropertyVerifier::new();
499 use smallvec::smallvec;
500 let func = HirFunction {
501 name: "untyped_param".to_string(),
502 params: smallvec![depyler_core::hir::HirParam::new(
503 "x".to_string(),
504 Type::Unknown
505 )],
506 ret_type: Type::Int,
507 body: vec![HirStmt::Return(Some(HirExpr::Var("x".to_string())))],
508 properties: Default::default(),
509 annotations: Default::default(),
510 docstring: None,
511 };
512
513 let results = verifier.verify_function(&func);
514 let type_result = results
515 .iter()
516 .find(|r| r.property == "type_preservation")
517 .unwrap();
518 assert!(matches!(type_result.status, PropertyStatus::Unknown));
519 assert_eq!(type_result.confidence, 0.0);
520 }
521
522 #[test]
523 fn test_detect_shared_state_owned() {
524 let verifier = PropertyVerifier::new();
525 use smallvec::smallvec;
526 let func = HirFunction {
527 name: "owned".to_string(),
528 params: smallvec![],
529 ret_type: Type::None,
530 body: vec![],
531 properties: Default::default(),
532 annotations: TranspilationAnnotations {
533 ownership_model: OwnershipModel::Owned,
534 ..Default::default()
535 },
536 docstring: None,
537 };
538 assert!(!verifier.detect_shared_state(&func));
539 }
540
541 #[test]
542 fn test_detect_shared_state_shared() {
543 let verifier = PropertyVerifier::new();
544 use smallvec::smallvec;
545 let func = HirFunction {
546 name: "shared".to_string(),
547 params: smallvec![],
548 ret_type: Type::None,
549 body: vec![],
550 properties: Default::default(),
551 annotations: TranspilationAnnotations {
552 ownership_model: OwnershipModel::Shared,
553 ..Default::default()
554 },
555 docstring: None,
556 };
557 assert!(verifier.detect_shared_state(&func));
558 }
559
560 #[test]
561 fn test_generate_property_tests() {
562 let verifier = PropertyVerifier::new().with_iterations(100);
563 use smallvec::smallvec;
564 let func = HirFunction {
565 name: "add".to_string(),
566 params: smallvec![
567 depyler_core::hir::HirParam::new("a".to_string(), Type::Int),
568 depyler_core::hir::HirParam::new("b".to_string(), Type::Int),
569 ],
570 ret_type: Type::Int,
571 body: vec![HirStmt::Return(Some(HirExpr::Var("a".to_string())))],
572 properties: Default::default(),
573 annotations: Default::default(),
574 docstring: None,
575 };
576
577 let test_code = verifier.generate_property_tests(&func);
578 assert!(test_code.is_ok());
579 let code = test_code.unwrap();
580 assert!(!code.is_empty());
581 assert!(code.contains("add"));
582 }
583
584 #[test]
585 fn test_verification_method_serialization() {
586 use serde_json;
587 let methods = vec![
588 VerificationMethod::Exhaustive,
589 VerificationMethod::PropertyTesting,
590 VerificationMethod::StaticAnalysis,
591 VerificationMethod::StructuralInduction,
592 VerificationMethod::Heuristic,
593 ];
594
595 for method in methods {
596 let serialized = serde_json::to_string(&method).unwrap();
597 let deserialized: VerificationMethod = serde_json::from_str(&serialized).unwrap();
598 assert_eq!(
599 std::mem::discriminant(&method),
600 std::mem::discriminant(&deserialized)
601 );
602 }
603 }
604
605 #[test]
606 fn test_test_case_serialization() {
607 use serde_json;
608 let test_case = TestCase {
609 inputs: vec![serde_json::json!(42), serde_json::json!("hello")],
610 expected_output: Some(serde_json::json!(42)),
611 actual_output: Some(serde_json::json!(43)),
612 error: Some("mismatch".to_string()),
613 };
614
615 let serialized = serde_json::to_string(&test_case).unwrap();
616 let deserialized: TestCase = serde_json::from_str(&serialized).unwrap();
617 assert_eq!(deserialized.inputs.len(), 2);
618 assert!(deserialized.error.is_some());
619 }
620
621 #[test]
622 fn test_test_case_empty() {
623 use serde_json;
624 let test_case = TestCase {
625 inputs: vec![],
626 expected_output: None,
627 actual_output: None,
628 error: None,
629 };
630
631 let serialized = serde_json::to_string(&test_case).unwrap();
632 let deserialized: TestCase = serde_json::from_str(&serialized).unwrap();
633 assert!(deserialized.inputs.is_empty());
634 assert!(deserialized.expected_output.is_none());
635 }
636
637 #[test]
638 fn test_property_verifier_default_eq_new() {
639 let v1 = PropertyVerifier::new();
640 let v2 = PropertyVerifier::default();
641 assert_eq!(v1.enable_quickcheck, v2.enable_quickcheck);
642 assert_eq!(v1.enable_contracts, v2.enable_contracts);
643 assert_eq!(v1.test_iterations, v2.test_iterations);
644 }
645}
646
647pub mod examples {}