1616use serde:: { Deserialize , Serialize } ;
1717use std:: collections:: { HashMap , HashSet } ;
1818use std:: fs;
19- use syn:: {
20- Expr , FnArg , ImplItem , ImplItemFn , Item , ItemFn , ItemImpl , ReturnType , Stmt , Visibility ,
21- } ;
19+ use syn:: { Expr , FnArg , ImplItem , ImplItemFn , Item , ItemFn , ItemImpl , ReturnType , Stmt , Visibility } ;
20+ use syn:: spanned:: Spanned ;
2221
2322#[ derive( Serialize , Deserialize , Debug , Clone ) ]
2423pub struct BranchSide {
@@ -36,24 +35,55 @@ pub struct BranchProfileEntry {
3635 pub branch_sides : Vec < BranchSide > ,
3736}
3837
38+ #[ derive( Serialize , Deserialize , Debug , Clone ) ]
39+ pub struct CallSite {
40+ #[ serde( rename = "Src" ) ]
41+ pub src : String ,
42+ #[ serde( rename = "Dst" ) ]
43+ pub dst : String ,
44+ }
45+
3946#[ derive( Serialize , Deserialize , Debug , Clone ) ]
4047pub struct FunctionInfo {
48+ #[ serde( rename = "linkageType" ) ]
49+ pub linkage_type : String ,
50+ #[ serde( rename = "constantsTouched" ) ]
51+ pub constants_touched : Vec < String > ,
52+ #[ serde( rename = "argNames" ) ]
53+ pub arg_names : Vec < String > ,
54+ #[ serde( rename = "functionName" ) ]
4155 pub name : String ,
56+ #[ serde( rename = "functionSourceFile" ) ]
4257 pub file : String ,
58+ #[ serde( rename = "returnType" ) ]
4359 pub return_type : String ,
60+ #[ serde( rename = "argCount" ) ]
4461 pub arg_count : usize ,
62+ #[ serde( rename = "argTypes" ) ]
4563 pub arg_types : Vec < String > ,
64+ #[ serde( rename = "CyclomaticComplexity" ) ]
4665 pub complexity : usize ,
66+ #[ serde( rename = "functionsReached" ) ]
4767 pub called_functions : Vec < String > ,
68+ #[ serde( rename = "functionDepth" ) ]
4869 pub depth : usize ,
4970 pub visibility : String ,
71+ #[ serde( rename = "ICount" ) ]
5072 pub icount : usize ,
73+ #[ serde( rename = "BBCount" ) ]
5174 pub bbcount : usize ,
75+ #[ serde( rename = "EdgeCount" ) ]
5276 pub edge_count : usize ,
77+ #[ serde( rename = "functionUses" ) ]
5378 pub function_uses : usize ,
79+ #[ serde( rename = "BranchProfiles" ) ]
5480 pub branch_profiles : Vec < BranchProfileEntry > ,
81+ #[ serde( rename = "functionLinenumber" ) ]
5582 pub start_line : usize ,
83+ #[ serde( rename = "functionLinenumberEnd" ) ]
5684 pub end_line : usize ,
85+ #[ serde( rename = "Callsites" ) ]
86+ pub callsites : Vec < CallSite > ,
5787}
5888
5989pub struct FunctionAnalyser {
@@ -144,15 +174,23 @@ impl FunctionAnalyser {
144174 let branch_profiles = self . profile_branches ( stmts, file) ;
145175
146176 let mut called_functions = Vec :: new ( ) ;
177+ let mut callsites = Vec :: new ( ) ;
178+
147179 for stmt in stmts {
148- self . extract_called_functions ( stmt, name, & mut called_functions) ;
180+ self . extract_called_functions ( stmt, name, & mut called_functions, & mut callsites , file ) ;
149181 }
150182
183+ called_functions. sort ( ) ;
184+ called_functions. dedup ( ) ;
185+
151186 for called in & called_functions {
152187 * self . reverse_call_map . entry ( called. clone ( ) ) . or_insert ( 0 ) += 1 ;
153188 }
154189
155190 self . functions . push ( FunctionInfo {
191+ linkage_type : String :: new ( ) ,
192+ constants_touched : Vec :: new ( ) ,
193+ arg_names : Vec :: new ( ) ,
156194 name : name. to_string ( ) ,
157195 file : file. to_string ( ) ,
158196 return_type,
@@ -169,6 +207,7 @@ impl FunctionAnalyser {
169207 branch_profiles,
170208 start_line,
171209 end_line,
210+ callsites,
172211 } ) ;
173212
174213 self . call_stack
@@ -182,9 +221,11 @@ impl FunctionAnalyser {
182221 stmt : & Stmt ,
183222 current_function : & str ,
184223 called_functions : & mut Vec < String > ,
224+ callsites : & mut Vec < CallSite > ,
225+ file : & str ,
185226 ) {
186227 if let Stmt :: Expr ( expr, _) = stmt {
187- self . extract_from_expr ( expr, current_function, called_functions) ;
228+ self . extract_from_expr ( expr, current_function, called_functions, callsites , file ) ;
188229 }
189230 }
190231
@@ -193,6 +234,8 @@ impl FunctionAnalyser {
193234 expr : & Expr ,
194235 current_function : & str ,
195236 called_functions : & mut Vec < String > ,
237+ callsites : & mut Vec < CallSite > ,
238+ file : & str ,
196239 ) {
197240 match expr {
198241 Expr :: Call ( call_expr) => {
@@ -204,51 +247,41 @@ impl FunctionAnalyser {
204247 . map ( |seg| seg. ident . to_string ( ) )
205248 . collect :: < Vec < _ > > ( )
206249 . join ( "::" ) ;
207- if full_path != current_function {
208- called_functions. push ( full_path) ;
250+ if self . is_function_known ( & full_path) {
251+ called_functions. push ( full_path. clone ( ) ) ;
252+ let span = call_expr. func . span ( ) . start ( ) ;
253+ callsites. push ( CallSite {
254+ src : format ! ( "{},{},{}" , file, span. line, span. column) ,
255+ dst : full_path,
256+ } ) ;
209257 }
210258 }
211259 }
212260 Expr :: MethodCall ( method_call) => {
213261 let method_name = method_call. method . to_string ( ) ;
214- if method_name != current_function {
215- called_functions. push ( method_name) ;
216- }
217- }
218- Expr :: Path ( path) => {
219- let full_path = path
220- . path
221- . segments
222- . iter ( )
223- . map ( |seg| seg. ident . to_string ( ) )
224- . collect :: < Vec < _ > > ( )
225- . join ( "::" ) ;
226- if full_path != current_function {
227- called_functions. push ( full_path) ;
262+ if let Some ( impl_name) = self . method_impls . get ( & method_name) {
263+ let full_path = format ! ( "{}::{}" , impl_name, method_name) ;
264+ called_functions. push ( full_path. clone ( ) ) ;
265+ let span = method_call. span ( ) . start ( ) ;
266+ callsites. push ( CallSite {
267+ src : format ! ( "{},{},{}" , file, span. line, span. column) ,
268+ dst : full_path,
269+ } ) ;
228270 }
229271 }
230272 Expr :: Block ( block) => {
231273 for stmt in & block. block . stmts {
232- self . extract_called_functions ( stmt, current_function, called_functions) ;
233- }
234- }
235- Expr :: If ( if_expr) => {
236- self . extract_from_expr ( & * if_expr. cond , current_function, called_functions) ;
237- for stmt in & if_expr. then_branch . stmts {
238- self . extract_called_functions ( stmt, current_function, called_functions) ;
239- }
240- if let Some ( ( _, else_branch) ) = & if_expr. else_branch {
241- if let Expr :: Block ( block) = & * * else_branch {
242- for stmt in & block. block . stmts {
243- self . extract_called_functions ( stmt, current_function, called_functions) ;
244- }
245- }
274+ self . extract_called_functions ( stmt, current_function, called_functions, callsites, file) ;
246275 }
247276 }
248277 _ => { }
249278 }
250279 }
251280
281+ fn is_function_known ( & self , name : & str ) -> bool {
282+ self . functions . iter ( ) . any ( |f| f. name == name)
283+ }
284+
252285 fn get_visibility ( & self , vis : & Visibility ) -> String {
253286 match vis {
254287 Visibility :: Public ( _) => "public" . to_string ( ) ,
@@ -303,7 +336,7 @@ impl FunctionAnalyser {
303336 fn extract_branch_side ( & self , block : & syn:: Block , file : & str ) -> BranchSide {
304337 let mut branch_side_funcs = vec ! [ ] ;
305338 for stmt in & block. stmts {
306- self . extract_called_functions ( stmt, "temp_branch" , & mut branch_side_funcs) ;
339+ self . extract_called_functions ( stmt, "temp_branch" , & mut branch_side_funcs, & mut vec ! [ ] , file ) ;
307340 }
308341
309342 let span = block. brace_token . span . open ( ) . start ( ) ;
@@ -384,9 +417,10 @@ impl FunctionAnalyser {
384417 }
385418}
386419
387- pub fn analyse_directory ( dir : & str , exclude_dirs : & [ & str ] ) -> std:: io:: Result < String > {
420+ pub fn analyse_directory ( dir : & str , exclude_dirs : & [ & str ] ) -> std:: io:: Result < Vec < FunctionInfo > > {
388421 let mut analyser = FunctionAnalyser :: new ( ) ;
389422
423+ // Search for rust source files and process
390424 for entry in fs:: read_dir ( dir) ? {
391425 let entry = entry?;
392426 let path = entry. path ( ) ;
@@ -395,13 +429,14 @@ pub fn analyse_directory(dir: &str, exclude_dirs: &[&str]) -> std::io::Result<St
395429 continue ;
396430 } else if path. is_dir ( ) {
397431 let sub_result = analyse_directory ( path. to_str ( ) . unwrap ( ) , exclude_dirs) ?;
398- let parsed_functions: Vec < FunctionInfo > = serde_json:: from_str ( & sub_result) . unwrap ( ) ;
399- analyser. functions . extend ( parsed_functions) ;
432+ analyser. functions . extend ( sub_result) ;
400433 } else if path. extension ( ) . and_then ( |s| s. to_str ( ) ) == Some ( "rs" ) {
434+ // Parse the rust source code and build an AST by the syn crate
401435 let file_content = fs:: read_to_string ( & path) ?;
402436 let syntax = syn:: parse_file ( & file_content)
403437 . map_err ( |e| std:: io:: Error :: new ( std:: io:: ErrorKind :: Other , e) ) ?;
404438
439+ // Analyse and retrieve a list of functions/methods with all properties
405440 for item in syntax. items {
406441 match item {
407442 Item :: Fn ( func) => analyser. visit_function ( & func, path. to_str ( ) . unwrap ( ) ) ,
@@ -424,7 +459,9 @@ pub fn analyse_directory(dir: &str, exclude_dirs: &[&str]) -> std::io::Result<St
424459 }
425460 }
426461
462+ // Post process the result and add in additional information for each functions/methods
427463 analyser. calculate_depths ( ) ;
428464 analyser. post_process_called_functions ( ) ;
429- Ok ( serde_json:: to_string ( & analyser. functions ) . unwrap ( ) )
465+
466+ Ok ( analyser. functions )
430467}
0 commit comments