FuncSpace 結果の走査
FuncSpace is the tree the library hands back from analyze. The top-level node represents the whole file; its spaces field holds nested function, class, impl, trait and namespace spaces. Each node carries the same CodeMetrics payload, so any metric is available at any level.
FuncSpace の構造
最もよく使うフィールドは次のとおりです。
| フィールド | 型 | 説明 |
|---|---|---|
name | Option<String> | 呼び出し側が指定した識別子(トップレベル)またはシンボル名(ネスト時) |
kind | SpaceKind | Unit, Function, Class, Impl, … |
start_line | usize | 開始行(1 始まり) |
end_line | usize | 終了行(1 始まり) |
spaces | Vec<FuncSpace> | ネストされたスペース |
metrics | CodeMetrics | スペースごとのすべてのメトリクス値 |
suppressed | SuppressionScope | ソース内の抑制マーカー |
SpaceKind is an enum; match on it to filter what you care about (Function only, or “anything that owns methods”).
start_line..=end_line は両端を含み、ファイルの終端を越えることはありません。また、ネストしたスペースの範囲は常に親の範囲の内側にあるため、これでソースをスライスしても安全です。
再帰的な走査
再帰はツリーの形をそのまま反映します。ここでは、認知的複雑度がしきい値を超えるすべての関数スペースを収集します。
use big_code_analysis::{
analyze, FuncSpace, MetricsOptions, SpaceKind, Source, LANG,
};
fn hotspots(space: &FuncSpace, threshold: u64, out: &mut Vec<String>) {
if space.kind == SpaceKind::Function
&& space.metrics.cognitive.cognitive_sum() > threshold
&& let Some(name) = &space.name
{
out.push(format!(
"{name} (lines {}–{})",
space.start_line, space.end_line,
));
}
for child in &space.spaces {
hotspots(child, threshold, out);
}
}
fn main() {
let source = b"\
fn easy() { let _ = 1; }
fn hard(x: i32) -> i32 {
if x > 0 { if x > 10 { 1 } else { 2 } } else { 3 }
}
";
let space = analyze(
Source::new(LANG::Rust, source).with_name(Some("snippet.rs".to_owned())),
MetricsOptions::default(),
)
.expect("parses");
let mut hits = Vec::new();
hotspots(&space, 2, &mut hits);
for hit in hits {
println!("{hit}");
}
}
反復的な走査
For deep trees, prefer an explicit stack. Rust does not guarantee tail-call optimisation, and pathological generated code can be arbitrarily nested:
#![allow(unused)]
fn main() {
use big_code_analysis::FuncSpace;
fn total_functions(root: &FuncSpace) -> usize {
let mut stack = vec![root];
let mut count = 0;
while let Some(space) = stack.pop() {
if space.kind == big_code_analysis::SpaceKind::Function {
count += 1;
}
stack.extend(space.spaces.iter());
}
count
}
}
メトリクスごとの値の読み取り
CodeMetrics exposes each metric as its own Stats struct. Each struct offers integer-valued summary accessors plus per-space derived ones. A few patterns:
#![allow(unused)]
fn main() {
use big_code_analysis::FuncSpace;
fn summary(space: &FuncSpace) {
let m = &space.metrics;
println!("cognitive (this space): {}", m.cognitive.cognitive());
println!("cyclomatic (this space): {}", m.cyclomatic.cyclomatic());
println!("# functions in this space: {}", m.nom.functions_sum());
println!("source lines (sloc): {}", m.loc.sloc());
println!("physical lines (ploc): {}", m.loc.ploc());
println!("ABC branches: {}", m.abc.branches());
}
}
*_sum アクセサは子スペースをまたいで集計します。m.loc.sloc() のような素のアクセサは、この ノード自体に帰属する値です。フィールドとメソッドの全一覧はメトリクスごとの rustdoc にあります。
Do not rely on traversal order
ライブラリは AST(抽象構文木)をソース順に走査しますが、契約上保証されるのは、各スペースがツリーに一度だけ現れることだけです。バージョン間で安定した順序が必要な場合は、走査後に start_line でソートしてください。
#![allow(unused)]
fn main() {
use big_code_analysis::FuncSpace;
fn flatten(space: &FuncSpace, out: &mut Vec<(usize, String)>) {
if let Some(name) = &space.name {
out.push((space.start_line, name.clone()));
}
for child in &space.spaces {
flatten(child, out);
}
}
fn sorted(space: &FuncSpace) -> Vec<(usize, String)> {
let mut v = Vec::new();
flatten(space, &mut v);
v.sort_by_key(|&(line, _)| line);
v
}
}