SARIF output

bca.to_sarif(result, *, thresholds=None) renders an analysis result (or an iterable of them) into a SARIF 2.1.0 JSON document, ready for upload to GitHub Code Scanning or any other SARIF consumer. The output is produced by the same Rust writer that backs bca check --report-format sarif, so the schema URL, tool driver name / version, and rule descriptions match the CLI byte-for-byte.

Examples on this page import the package as bca (import big_code_analysis as bca). A bare bca in a shell command is the CLI binary.

def run(
    paths: Iterable[Path],
    sarif_path: Path,
    thresholds: Mapping[str, float],
) -> str:
    """Analyse ``paths`` and write a SARIF document to ``sarif_path``.

    Returns the rendered SARIF JSON so the caller (or the test) can
    inspect it without re-reading the file.
    """
    batch = bca.analyze_batch(paths)
    sarif = bca.to_sarif(batch, thresholds=dict(thresholds))

    sarif_path.parent.mkdir(parents=True, exist_ok=True)
    sarif_path.write_text(sarif, encoding="utf-8")
    print(f"wrote {sarif_path} ({len(sarif.encode('utf-8'))} bytes)")
    return sarif

to_sarif accepts:

  • A single dict returned by bca.analyze or bca.analyze_source.
  • Any iterable yielding such dicts, bca.AnalysisFailure instances, and/or None (the natural shape of bca.analyze_batch's return value). AnalysisFailure and None entries are skipped silently — they represent files for which no record was emitted, not findings.
  • A scalar None, the documented return of bca.analyze for a skipped file; it yields an empty SARIF run.

Thresholds

Accepted threshold names mirror the CLI's EXTRACTORS table in big-code-analysis-cli/src/thresholds.rs:

  • cognitive, cyclomatic, cyclomatic.modified
  • halstead.volume, halstead.difficulty, halstead.effort, halstead.time, halstead.bugs
  • loc.sloc, loc.ploc, loc.lloc, loc.cloc, loc.blank
  • nom, tokens, nexits, nargs
  • mi.original, mi.sei, mi.visual_studio
  • abc, wmc, npm, npa

An unknown name raises ValueError listing the accepted set, so a typo fails fast instead of silently producing an empty SARIF run.

thresholds=None (the default) and thresholds={} both produce a well-formed SARIF document with empty results and rules arrays. This matches bca check, which applies no implicit limits: every run supplies its own, from --threshold or a bca.toml (which bca init scaffolds with a starting table).

Upload to GitHub Code Scanning

# .github/workflows/code-scanning.yml (excerpt)
- name: Compute metric SARIF
  run: |
    python - <<'PY'
    import big_code_analysis as bca
    with open("paths.txt", encoding="utf-8") as paths_fh:
        results = bca.analyze_batch(paths_fh.read().splitlines())
    with open("metrics.sarif", "w", encoding="utf-8") as fh:
        fh.write(bca.to_sarif(results, thresholds={"cyclomatic": 15}))
    PY
- name: Upload to Code Scanning
  uses: github/codeql-action/upload-sarif@v3
  with:
    sarif_file: metrics.sarif

The upload action is documented under github/codeql-action/upload-sarif. The bindings produce one SARIF run per call; the action handles the upload to the repository's Code Scanning alerts.

Which spaces produce findings

to_sarif emits a finding at every space — the file unit, each container, and each leaf function or closure — whose own value breaches its limit, exactly matching bca check --report-format sarif. For most metrics the JSON headline at a space already is that space's own value. The four subtree-aggregate metrics — cyclomatic, cyclomatic.modified, cognitive, and abc — additionally expose a sum / magnitude rolled up across child spaces; the binding reads their per-space value field instead, so it reports an interior breach (for example a function whose own complexity breaches even though a nested closure's does not) without being fooled by the larger aggregate.

Unit findings carry logicalLocations: [{"fullyQualifiedName": "<file>"}]. Every other space carries its qualified symbol. Within that symbol, a closure/lambda (the <anonymous> name every grammar emits) and the None-name parse-failure case both collapse to <anon@L{start_line}>, matching the CLI's space_segment.

See also

  • Batch processing — the natural source of input iterables for to_sarif; AnalysisFailure entries are skipped silently.
  • Metric selection — threshold names are a closed set independent of metrics=; requesting a narrower metric suite while gating on a dropped threshold yields an empty SARIF run.
  • Error handling — the typed exceptions to_sarif raises for bad caller input (TypeError / ValueError).