Skip to main content

depyler_verify/
lib.rs

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        // Property 1: Type preservation
80        if let Some(result) = self.verify_type_preservation(func) {
81            results.push(result);
82        }
83
84        // Property 2: Memory safety
85        let mut memory_analyzer = memory_safety::MemorySafetyAnalyzer::new();
86        results.push(memory_analyzer.analyze_function(func));
87
88        // Property 2b: Lifetime safety
89        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        // Property 3: Null safety
113        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        // Property 4: Panic freedom
136        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        // Property 5: Termination
147        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        // Property 6: Purity
158        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        // Property 7: Thread safety (if required)
169        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        // Check if all types are properly annotated
178        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        // Check for proper synchronization when thread safety is required
204        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        // Simplified check - in reality would analyze data flow
231        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    /// Helper function to create a test function
249    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        // Should have multiple verification results
303        assert!(!results.is_empty());
304
305        // Find the purity result
306        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        // Should include thread safety verification
322        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        // Test with fully typed function
332        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        // Test with unknown types
340        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        // Should include memory safety checks
357        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    // ========================================================================
411    // Session 11 - Deep Coverage Tests for Untested Paths
412    // ========================================================================
413
414    #[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        // Should NOT have purity, termination, or panic_free results
491        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
647/// Doctests for public API
648///
649/// # Example
650/// ```
651/// use depyler_verify::{PropertyVerifier, PropertyStatus};
652/// use depyler_core::hir::{HirFunction, HirParam, Type};
653/// use smallvec::smallvec;
654///
655/// let verifier = PropertyVerifier::new()
656///     .with_iterations(100);
657///
658/// // Create a simple function to verify
659/// let func = HirFunction {
660///     name: "add".to_string(),
661///     params: smallvec![
662///         HirParam { name: "a".to_string(), ty: Type::Int, default: None, is_vararg: false },
663///         HirParam { name: "b".to_string(), ty: Type::Int, default: None, is_vararg: false }
664///     ],
665///     ret_type: Type::Int,
666///     body: vec![],
667///     properties: Default::default(),
668///     annotations: Default::default(),
669///     docstring: None,
670/// };
671///
672/// let results = verifier.verify_function(&func);
673/// assert!(!results.is_empty());
674/// ```
675pub mod examples {}