2121from pathlib import Path
2222from typing import Dict , List , Tuple
2323
24- def get_rs_files (project_dir : str ) -> List [str ]:
25- """Recursively find all Rust files in the project directory."""
26- rs_files = []
27- for root , _ , files in os .walk (project_dir ):
28- for file in files :
29- if file .endswith (".rs" ):
30- rs_files .append (os .path .join (root , file ))
31- return rs_files
32-
33- def extract_function_line_info_from_file (file_path : str ) -> Dict [Tuple [str , str ], Tuple [int , int ]]:
34- """Extract function names with their start and end line numbers from a Rust file."""
35- info = {}
36- pattern = re .compile (r"fn\s+(\w+)\s*\(" )
37-
38- with open (file_path , "r" ) as f :
39- lines = f .readlines ()
40-
41- curr_func = None
42- start = 0
43-
44- for i , line in enumerate (lines , 1 ):
45- match = pattern .search (line )
46- if match :
47- if curr_func :
48- info [(curr_func , file_path )] = (start , i - 1 )
49-
50- curr_func = match .group (1 )
51- start = i
52-
53- if curr_func :
54- info [(curr_func , file_path )] = (start , len (lines ))
55-
56- return info
57-
58- def analyze_project_functions (project_dir : str ) -> Dict [Tuple [str , str ], Tuple [int , int ]]:
59- """Analyze all functions in the Rust project and map their line numbers."""
60- all_functions = {}
61- rs_files = get_rs_files (project_dir )
62-
63- for rs_file in rs_files :
64- functions = extract_function_line_info_from_file (rs_file )
65- all_functions .update (functions )
66-
67- return all_functions
68-
6924def run_rust_analysis (target_directory : str ) -> List [Dict ]:
7025 """Run the Rust analysis tool and retrieve JSON results."""
7126 try :
7227 result = subprocess .run (
73- [" cargo" , " run" , "--" , target_directory ],
28+ [' cargo' , ' run' , '--' , target_directory ],
7429 stdout = subprocess .PIPE ,
7530 stderr = subprocess .PIPE ,
76- cwd = " rust_function_analyser" ,
31+ cwd = ' rust_function_analyser' ,
7732 text = True ,
7833 check = True
7934 )
@@ -83,72 +38,55 @@ def run_rust_analysis(target_directory: str) -> List[Dict]:
8338 except ValueError :
8439 return []
8540
86- def add_line_data (rust_results : List [Dict ]):
87- """Add line data to functions from rust analysis result."""
88- line_info = analyze_project_functions (target_dir )
89-
90- for func in rust_results :
91- func_key = (func ["name" ], func ["file" ])
92- if func_key in line_info :
93- func ["start_line" ], func ["end_line" ] = line_info [func_key ]
94- else :
95- func ["start_line" ], func ["end_line" ] = 0 , 0
96- func ["called_functions" ] = [f .replace (" " , "" ) for f in func ["called_functions" ]]
97-
98- def create_yaml_output (data : List [Dict ], output_file = "data.yaml" ):
41+ def create_yaml_output (data : List [Dict ], output_file = 'data.yaml' ):
9942 """Generate a YAML file with the analysis results."""
10043 yaml_data = {
101- " Fuzzer filename" : "" ,
102- " All functions" : {
103- " Function list name" : " All functions" ,
104- " Elements" : []
44+ ' Fuzzer filename' : '' ,
45+ ' All functions' : {
46+ ' Function list name' : ' All functions' ,
47+ ' Elements' : []
10548 }
10649 }
10750
10851 for func in data :
109- yaml_data [" All functions" ][ " Elements" ].append ({
110- " functionName" : func [" name" ],
111- " functionSourceFile" : func [" file" ],
112- " linkageType" : "" ,
113- " functionLinenumber" : func [ " start_line" ] ,
114- " functionLinenumberEnd" : func [ " end_line" ] ,
115- " functionDepth" : func [" depth" ],
116- " returnType" : func [" return_type" ],
117- " argCount" : func [" arg_count" ],
118- " argTypes" : func [" arg_types" ],
119- " constantsTouched" : [],
120- " argNames" : [],
121- " BBCount" : 0 ,
122- "ICount" : 0 ,
123- " EdgeCount" : 0 ,
124- " CyclomaticComplexity" : func [" complexity" ],
125- " functionsReached" : func [" called_functions" ],
126- " functionUses" : 0 ,
127- " BranchProfiles" : [ ],
128- " Callsites" : []
52+ yaml_data [' All functions' ][ ' Elements' ].append ({
53+ ' functionName' : func [' name' ],
54+ ' functionSourceFile' : func [' file' ],
55+ ' linkageType' : '' ,
56+ ' functionLinenumber' : func . get ( ' start_line' , 0 ) ,
57+ ' functionLinenumberEnd' : func . get ( ' end_line' , 0 ) ,
58+ ' functionDepth' : func [' depth' ],
59+ ' returnType' : func [' return_type' ],
60+ ' argCount' : func [' arg_count' ],
61+ ' argTypes' : func [' arg_types' ],
62+ ' constantsTouched' : [],
63+ ' argNames' : [],
64+ ' BBCount' : func [ 'bbcount' ] ,
65+ 'iCount' : func [ 'icount' ] ,
66+ ' EdgeCount' : func [ 'edge_count' ] ,
67+ ' CyclomaticComplexity' : func [' complexity' ],
68+ ' functionsReached' : func [' called_functions' ],
69+ ' functionUses' : func [ 'function_uses' ] ,
70+ ' BranchProfiles' : func [ 'branch_profiles' ],
71+ ' Callsites' : []
12972 })
13073
131- with open (output_file , "w" ) as file :
74+ with open (output_file , 'w' ) as file :
13275 yaml .dump (yaml_data , file , default_flow_style = False )
13376
134- print (f" YAML output saved to { output_file } " )
77+ print (f' YAML output saved to { output_file } ' )
13578
136- if __name__ == " __main__" :
79+ if __name__ == ' __main__' :
13780 if len (sys .argv ) != 2 :
138- print (" Usage: python3 script.py <target_directory>" )
81+ print (' Usage: python3 script.py <target_directory>' )
13982 sys .exit (1 )
14083
14184 target_dir = sys .argv [1 ]
14285 if not Path (target_dir ).is_dir ():
143- print (f" Error: { target_dir } is not a valid directory" )
86+ print (f' Error: { target_dir } is not a valid directory' )
14487 sys .exit (1 )
14588
14689 # Run the rust analysis frontend code
14790 rust_analysis_results = run_rust_analysis (target_dir )
14891
149- # Manually extract the line info for each function.
150- # This is needed because the rust analysis syn AST approach
151- # cannot retrieve line number info on stable rust and non-nightly build
152- add_line_data (rust_analysis_results )
153-
15492 create_yaml_output (rust_analysis_results )
0 commit comments