FuncSpace 結果の走査
FuncSpace は、ライブラリが analyze から返すツリーです。トップレベルのノードはファイル全体を表し、その spaces フィールドにはネストされた関数 / クラス / impl / トレイト / 名前空間のスペースが入ります。各ノードは同じ CodeMetrics ペイロードを持つため、どのメトリクスも任意の粒度で参照できます。
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 は列挙型です。これに対してマッチすることで、関心のあるもの(Function のみ、あるいは「メソッドを持つものすべて」など)に絞り込めます。
再帰的な走査
再帰はツリーの形をそのまま反映します。ここでは、認知的複雑度がしきい値を超えるすべての関数スペースを収集します。
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}"); } }
反復的な走査
深いツリーには明示的なスタックを使ってください。Rust は末尾呼び出し最適化を行わず、病的な生成コードは際限なくネストし得ます。
#![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 は各メトリクスをそれぞれ独立した Stats 構造体として公開します。各構造体は、整数値のサマリーアクセサに加えて、スペースごとの派生値のアクセサを提供します。いくつかのパターンを示します。
#![allow(unused)] fn main() { use big_code_analysis::FuncSpace; fn summary(space: &FuncSpace) { let m = &space.metrics; println!("cognitive (this space): {}", m.cognitive.cognitive_sum()); println!("cyclomatic (this space): {}", m.cyclomatic.cyclomatic_sum()); 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 にあります。
走査順序に依存しない
ライブラリは 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 } }