1+ from enum import Enum
2+ from typing import Dict , List , Optional , Set
3+ from pydantic import BaseModel , Field
4+
5+
6+ class RustVisibility (Enum ):
7+ """Represents Rust visibility modifiers."""
8+ PUBLIC = "pub"
9+ PRIVATE = ""
10+ CRATE = "pub(crate)"
11+ SUPER = "pub(super)"
12+ IN_PATH = "pub(in path)"
13+
14+
15+ class SafetyClassification (Enum ):
16+ """Classifies the safety level of Rust code."""
17+ SAFE = "safe"
18+ UNSAFE = "unsafe"
19+ UNSAFE_CONTAINER = "unsafe_container"
20+ FFI = "ffi"
21+
22+
23+ class UnsafeReason (Enum ):
24+ """Reasons why code might be unsafe."""
25+ RAW_POINTER_DEREF = "raw_pointer_deref"
26+ MUTABLE_STATIC = "mutable_static"
27+ FFI_CALL = "ffi_call"
28+ UNION_FIELD_ACCESS = "union_field_access"
29+ INLINE_ASSEMBLY = "inline_assembly"
30+ UNSAFE_TRAIT_IMPL = "unsafe_trait_impl"
31+ CUSTOM = "custom"
32+
33+
34+ class UnsafeBlock (BaseModel ):
35+ """Represents an unsafe block within Rust code."""
36+ start_line : int
37+ end_line : int
38+ reasons : List [UnsafeReason ] = Field (default_factory = list )
39+ explanation : Optional [str ] = None # Documentation explaining why unsafe is needed
40+ containing_function : Optional [str ] = None
41+
42+
43+ class RustType (BaseModel ):
44+ """Represents a Rust type."""
45+ name : str
46+ is_reference : bool = False
47+ is_mutable : bool = False
48+ lifetime : Optional [str ] = None
49+ generic_params : List [str ] = Field (default_factory = list )
50+ is_sized : bool = True
51+ is_static : bool = False
52+ contains_raw_pointers : bool = False
53+ is_union : bool = False
54+
55+
56+ class RustAttribute (BaseModel ):
57+ """Represents a Rust attribute."""
58+ name : str
59+ arguments : List [str ] = Field (default_factory = list )
60+ is_inner : bool = False # #![foo] vs #[foo]
61+
62+
63+ class SafetyAnalysis (BaseModel ):
64+ """Analyzes and tracks safety-related information."""
65+ classification : SafetyClassification
66+ unsafe_blocks : List [UnsafeBlock ] = Field (default_factory = list )
67+ unsafe_fn_calls : List [str ] = Field (default_factory = list )
68+ raw_pointer_usage : bool = False
69+ ffi_interactions : bool = False
70+ unsafe_traits_used : List [str ] = Field (default_factory = list )
71+ mutable_statics : List [str ] = Field (default_factory = list )
72+ safety_comments : Optional [str ] = None
73+
74+
75+ class RustCallable (BaseModel ):
76+ """Represents a Rust function or method."""
77+ name : str
78+ visibility : RustVisibility = RustVisibility .PRIVATE
79+ doc_comment : Optional [str ] = None
80+ attributes : List [RustAttribute ] = Field (default_factory = list )
81+ parameters : List ["RustParameter" ] = Field (default_factory = list )
82+ return_type : Optional [RustType ] = None
83+ is_async : bool = False
84+ is_const : bool = False
85+ is_unsafe : bool = False
86+ is_extern : bool = False
87+ extern_abi : Optional [str ] = None
88+ generic_params : List ["RustGenericParam" ] = Field (default_factory = list )
89+ lifetime_params : List ["RustLifetimeParam" ] = Field (default_factory = list )
90+ where_clauses : List [str ] = Field (default_factory = list )
91+ code : str
92+ start_line : int
93+ end_line : int
94+ referenced_types : List [str ] = Field (default_factory = list )
95+ accessed_variables : List [str ] = Field (default_factory = list )
96+ call_sites : List ["RustCallSite" ] = Field (default_factory = list )
97+ variable_declarations : List ["RustVariableDeclaration" ] = Field (default_factory = list )
98+ cyclomatic_complexity : Optional [int ] = None
99+ safety_analysis : SafetyAnalysis
100+
101+ def is_fully_safe (self ) -> bool :
102+ """Check if the function is completely safe (no unsafe blocks or calls)."""
103+ return (
104+ self .safety_analysis .classification == SafetyClassification .SAFE
105+ and not self .safety_analysis .unsafe_blocks
106+ and not self .safety_analysis .unsafe_fn_calls
107+ )
108+
109+ def contains_unsafe (self ) -> bool :
110+ """Check if the function contains any unsafe code."""
111+ return (
112+ self .safety_analysis .classification in [SafetyClassification .UNSAFE , SafetyClassification .UNSAFE_CONTAINER ]
113+ or bool (self .safety_analysis .unsafe_blocks )
114+ or bool (self .safety_analysis .unsafe_fn_calls )
115+ )
116+
117+ def get_unsafe_reasons (self ) -> Set [UnsafeReason ]:
118+ """Get all reasons for unsafe usage in this function."""
119+ reasons = set ()
120+ for block in self .safety_analysis .unsafe_blocks :
121+ reasons .update (block .reasons )
122+ return reasons
123+
124+
125+ class RustModule (BaseModel ):
126+ """Represents a Rust module."""
127+ name : str
128+ doc_comment : Optional [str ] = None
129+ attributes : List [RustAttribute ] = Field (default_factory = list )
130+ visibility : RustVisibility = RustVisibility .PRIVATE
131+ types : Dict [str , "RustType" ] = Field (default_factory = dict )
132+ functions : Dict [str , RustCallable ] = Field (default_factory = dict )
133+ safe_functions : Dict [str , RustCallable ] = Field (default_factory = dict )
134+ unsafe_functions : Dict [str , RustCallable ] = Field (default_factory = dict )
135+ submodules : Dict [str , 'RustModule' ] = Field (default_factory = dict )
136+ constants : List ["RustVariableDeclaration" ] = Field (default_factory = list )
137+ macros : List [str ] = Field (default_factory = list )
138+ use_declarations : List [str ] = Field (default_factory = list )
139+ extern_crates : List [str ] = Field (default_factory = list )
140+ is_unsafe : bool = False
141+ file_path : Optional [str ] = None
142+ is_mod_rs : bool = False
143+
144+ def categorize_functions (self ):
145+ """Categorize functions based on their safety analysis."""
146+ self .safe_functions .clear ()
147+ self .unsafe_functions .clear ()
148+
149+ for name , func in self .functions .items ():
150+ if func .is_fully_safe ():
151+ self .safe_functions [name ] = func
152+ else :
153+ self .unsafe_functions [name ] = func
154+
155+
156+ class RustCrate (BaseModel ):
157+ """Represents a complete Rust crate."""
158+ name : str
159+ version : str
160+ root_module : RustModule
161+ dependencies : List ["RustDependencyEdge" ] = Field (default_factory = list )
162+ edition : str = "2021"
163+ features : List [str ] = Field (default_factory = list )
164+
165+ def analyze_safety (self ) -> Dict [str , int ]:
166+ """Analyze safety statistics across the crate."""
167+ stats = {
168+ "total_functions" : 0 ,
169+ "safe_functions" : 0 ,
170+ "unsafe_functions" : 0 ,
171+ "unsafe_blocks" : 0 ,
172+ "ffi_functions" : 0 ,
173+ }
174+
175+ def analyze_module (module : RustModule ):
176+ for func in module .functions .values ():
177+ stats ["total_functions" ] += 1
178+ if func .is_fully_safe ():
179+ stats ["safe_functions" ] += 1
180+ else :
181+ stats ["unsafe_functions" ] += 1
182+ if func .safety_analysis .classification == SafetyClassification .FFI :
183+ stats ["ffi_functions" ] += 1
184+ stats ["unsafe_blocks" ] += len (func .safety_analysis .unsafe_blocks )
185+
186+ for submodule in module .submodules .values ():
187+ analyze_module (submodule )
188+
189+ analyze_module (self .root_module )
190+ return stats
191+
192+ def get_unsafe_functions (self ) -> List [tuple [str , RustCallable ]]:
193+ """Get all unsafe functions in the crate with their module paths."""
194+ unsafe_fns = []
195+
196+ def collect_unsafe (module : RustModule , path : str ):
197+ for name , func in module .functions .items ():
198+ if not func .is_fully_safe ():
199+ full_path = f"{ path } ::{ name } " if path else name
200+ unsafe_fns .append ((full_path , func ))
201+
202+ for submod_name , submod in module .submodules .items ():
203+ new_path = f"{ path } ::{ submod_name } " if path else submod_name
204+ collect_unsafe (submod , new_path )
205+
206+ collect_unsafe (self .root_module , "" )
207+ return unsafe_fns
0 commit comments