Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

メモリ上のソースの分析

big-code-analysis never requires source to live on disk. The recommended entry point analyze takes a Source carrying the language, source bytes, and an optional caller-supplied display name. No filesystem path is involved unless the C/C++ preprocessor lookup needs one (Source::preproc_path).

これは次の用途に有用です:

  • 生成コードを書き出す前にスコアリングする。
  • Scoring pre-processed or bundled source, such as the output of a template expansion.
  • すでにバッファをメモリ上に保持している言語サーバーやエディタプラグインから分析ツールを駆動する。
  • Running stdin pipelines and unit tests that should not touch the filesystem.

バッファからの読み取り

#![allow(unused)]
fn main() {
use big_code_analysis::{analyze, MetricsOptions, Source, LANG};

fn analyze_buffer(source: &[u8]) -> Option<u64> {
    // `Source::name` はトップレベルの `FuncSpace` に埋め込まれる
    // 表示識別子です。下流の消費者(ログ、JSON 出力)にとって
    // 意味のあるものを選んでください。付ける価値のあるものが
    // なければ `None` を渡します。
    let space = analyze(
        Source::new(LANG::Python, source).with_name(Some("<stdin>".to_owned())),
        MetricsOptions::default(),
    )
    .ok()?;

    Some(space.metrics.cognitive.cognitive_sum())
}
}

Source::new borrows the source bytes; the caller keeps ownership. If your downstream pipeline needs to highlight findings on the same bytes, you can keep using the original buffer after analyze returns.

標準入力からの読み取り

use std::io::{self, Read};

use big_code_analysis::{analyze, MetricsOptions, Source, LANG};

fn main() -> io::Result<()> {
    let mut source = Vec::new();
    io::stdin().read_to_end(&mut source)?;

    let space = match analyze(
        Source::new(LANG::Javascript, &source)
            .with_name(Some("<stdin>".to_owned())),
        MetricsOptions::default(),
    ) {
        Ok(space) => space,
        Err(err) => {
            eprintln!("parse failed: {err}");
            std::process::exit(1);
        }
    };

    println!("{}", space.metrics.cyclomatic.cyclomatic_sum());
    Ok(())
}

内容から言語を選択する

If you do not know the language up front, combine guess_language with analyze. guess_language checks the path extension, an Emacs mode declaration, and the shebang, in that order:

#![allow(unused)]
fn main() {
use std::path::PathBuf;

use big_code_analysis::{analyze, guess_language, MetricsOptions, Source};

fn analyze_unknown(path: PathBuf, source: Vec<u8>) -> Option<()> {
    let (lang, _name) = guess_language(&source, &path);
    let lang = lang?;
    // `.ok()?` は `MetricsError` を `None` に畳み込み、このヘルパーの
    // `Option` の戻り値の形を保ちます。バリアントを保持するより
    // 詳細なマッピングは `error-handling.md` を参照してください。
    let _space = analyze(
        Source::new(lang, &source)
            .with_name(path.to_str().map(str::to_owned)),
        MetricsOptions::default(),
    )
    .ok()?;
    Some(())
}
}

guess_language returns (None, _) when the extension, the mode line, and the shebang all fail to name a language. Treat that as “skip” rather than as a hard error.

注意点

  • Name identity matters. Top-level FuncSpace::name is whatever string you put in Source::name. Two analyses sharing the same name look identical to a downstream consumer that keys on it. Use distinct labels for distinct buffers.
  • Source::name is Option<String>. Passing None leaves the top-level FuncSpace::name as None, which suits ad-hoc snippets with no meaningful identity. Downstream consumers that require a stable identifier should check for None explicitly.
  • ファイルシステムへのフォールバックはありません。 CLI と異なり、ライブラリは隣接ファイルを読んだり、#include を辿ったり、.gitignore を解釈したりしません。分析したいバイト列を正確に与えてください。

一度のパースを複数のパスで再利用する

analyze is the one-shot entry point: bytes in, FuncSpace out. When you need more than metrics from a single parse (operators and operands, an AST dump, a function-span list), parse once with Ast::parse and call the per-pass methods on the returned handle (metrics, ops, dump, functions, and so on). See Parse once, run metrics many times.