1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
#![cfg_attr(coverage_nightly, coverage(off))]
//! Dead code prover using reachability analysis and formal verification.
//!
//! This module implements sophisticated dead code detection that goes beyond
//! simple unused symbol detection. It performs whole-program reachability
//! analysis to prove that code is truly unreachable, considering various
//! entry points including FFI exports, dynamic dispatch, and reflection.
//!
//! # Analysis Approach
//!
//! 1. **Entry Point Discovery**: Identifies all program entry points
//! - Main functions, tests, benchmarks
//! - FFI exports (`#[no_mangle]`, `extern "C"`)
//! - Dynamic dispatch targets (trait objects, function pointers)
//! - Reflection and macro-generated code
//!
//! 2. **Reachability Propagation**: Traces execution paths from entry points
//! - Direct function calls
//! - Method invocations
//! - Closure captures
//! - Const evaluation paths
//!
//! 3. **Proof Generation**: Provides evidence for dead code claims
//! - Call graph showing unreachability
//! - Entry point analysis results
//! - Confidence scores based on analysis completeness
//!
//! # Example
//!
//! ```ignore
//! use pmat::services::dead_code_prover::ReachabilityAnalyzer;
//! use pmat::models::unified_ast::UnifiedAstNode;
//!
//! # fn example(ast: &UnifiedAstNode) {
//! let mut analyzer = ReachabilityAnalyzer::new();
//!
//! // Analyze AST to find entry points
//! analyzer.find_entry_points(ast);
//!
//! // Perform reachability analysis
//! let dead_code = analyzer.analyze_reachability(ast);
//!
//! // Generate proof for dead code
//! for item in &dead_code {
//! println!("Dead code: {} (confidence: {}%)",
//! item.name, item.confidence);
//! println!("Reason: {}", item.reason);
//! }
//! # }
//! ```ignore
use crate::models::unified_ast::{AstKind, FunctionKind, UnifiedAstNode};
use crate::services::dead_code_analyzer::{DeadCodeItem, DeadCodeReport, DeadCodeType};
use serde::{Deserialize, Serialize};
use std::collections::{HashMap, HashSet};
use std::path::Path;
/// Symbol identifier for cross-reference tracking
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub struct SymbolId {
pub file_path: String,
pub function_name: String,
pub line_number: usize,
}
/// Entry point types for reachability analysis
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum EntryPointType {
Main,
Test,
Benchmark,
Binary,
FFIExport,
DynamicDispatch,
Reflection,
}
/// Reachability analyzer with FFI awareness
pub struct ReachabilityAnalyzer {
/// Known entry points
entry_points: HashSet<SymbolId>,
/// Reachable symbols discovered during analysis
#[allow(dead_code)]
reachable: HashSet<SymbolId>,
/// FFI exports that should never be marked as dead
#[allow(dead_code)]
ffi_exports: HashSet<SymbolId>,
/// Dynamic dispatch targets
#[allow(dead_code)]
dynamic_targets: HashSet<SymbolId>,
}
/// FFI reference tracker for detecting externally visible symbols
pub struct FFIReferenceTracker {
/// Symbols marked with #[`no_mangle`]
no_mangle_symbols: HashSet<SymbolId>,
/// Symbols with custom export names
export_name_symbols: HashMap<SymbolId, String>,
/// extern "C" functions
extern_c_functions: HashSet<SymbolId>,
/// WASM bindgen exports
wasm_exports: HashSet<SymbolId>,
/// `PyO3` exports
python_exports: HashSet<SymbolId>,
}
/// Dynamic dispatch analyzer for trait objects and function pointers
pub struct DynamicDispatchAnalyzer {
/// Trait implementations
trait_impls: HashMap<String, Vec<SymbolId>>,
/// Function pointer usage
function_pointers: HashSet<SymbolId>,
/// Trait object usage
trait_objects: HashMap<String, Vec<SymbolId>>,
}
#[derive(Debug, Clone)]
pub enum Usage {
TraitObject(String),
FunctionPointer,
VTable,
}
/// Dead code proof with confidence scoring
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct DeadCodeProof {
pub item: SymbolId,
pub proof_type: DeadCodeProofType,
pub confidence: f64,
pub evidence: Vec<Evidence>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum DeadCodeProofType {
ProvenDead, // Definitely unreachable
ProvenLive, // Definitely reachable
UnknownLiveness, // Cannot determine
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Evidence {
pub evidence_type: EvidenceType,
pub description: String,
pub confidence: f64,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum EvidenceType {
NoReferences,
FFIExport,
DynamicDispatch,
TestFunction,
MainFunction,
UnreachableCode,
}
/// Main dead code prover with FFI awareness
pub struct DeadCodeProver {
#[allow(dead_code)]
reachability: ReachabilityAnalyzer,
ffi_tracker: FFIReferenceTracker,
dynamic_analyzer: DynamicDispatchAnalyzer,
}
// Default impls
impl Default for ReachabilityAnalyzer {
fn default() -> Self {
Self::new()
}
}
impl Default for FFIReferenceTracker {
fn default() -> Self {
Self::new()
}
}
impl Default for DynamicDispatchAnalyzer {
fn default() -> Self {
Self::new()
}
}
impl Default for DeadCodeProver {
fn default() -> Self {
Self::new()
}
}
// Implementation split into include files
include!("dead_code_prover_reachability.rs");
include!("dead_code_prover_ffi.rs");
include!("dead_code_prover_analysis.rs");
include!("dead_code_prover_tests.rs");