Skip to content

Commit c5c2288

Browse files
committed
Fix logic
Signed-off-by: Arthur Chan <[email protected]>
1 parent bbf6210 commit c5c2288

File tree

7 files changed

+395
-138
lines changed

7 files changed

+395
-138
lines changed

frontends/rust/README.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
# Rust frontend
2+
3+
This is work in progress.
4+
5+
Depends on Syn crate for Rust AST processing.
6+
7+
Example of running: `cargo run -- $SRC`

frontends/rust/process.py

Lines changed: 0 additions & 92 deletions
This file was deleted.

frontends/rust/rust_function_analyser/Cargo.toml

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,10 @@ version = "0.1.0"
44
edition = "2021"
55

66
[dependencies]
7+
proc-macro2 = { version = "1.0", features = ["span-locations"] }
78
syn = { version = "2.0", features = ["full", "visit"] }
8-
quote = "1.0"
9-
walkdir = "2.4"
109
serde = { version = "1.0", features = ["derive"] }
1110
serde_json = "1.0"
12-
proc-macro2 = { version = "1.0", features = ["span-locations"] }
11+
serde_yaml = "0.9"
12+
quote = "1.0"
13+
walkdir = "2.4"

frontends/rust/rust_function_analyser/src/analyse.rs

Lines changed: 77 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,8 @@
1616
use serde::{Deserialize, Serialize};
1717
use std::collections::{HashMap, HashSet};
1818
use 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)]
2423
pub 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)]
4047
pub 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

5989
pub 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

Comments
 (0)