抑制マーカー

ソース内抑制マーカーは、違反している関数を編集したり、ファイルを走査から除外したりすることなく、しきい値違反を沈黙させます。ソースファイル内の任意のコメントにマーカーを置くと、bca check は対象のメトリクスがそのスコープでは制限内にあるものとして扱います。メトリクスの計算自体には影響しません — 生の bca metrics 出力は引き続きすべての数値を報告します。抑制は測定結果の表示に関する仕組みです: bca check は対象の違反をゲートから除外し、bca report markdown|html はデフォルトで該当するホットスポットテーブルから対象の関数を省略します(生の監査ビューが必要な場合は bca report --no-suppress を渡してください — report を参照)。

マーカーは、コードの編集が選択肢にならないケースのために存在します: 書き直し待ちの生成コード的なレガシーモジュール、コメントに文書化された承認済みの例外、そして Lizard#lizard forgives 慣例からの移行です。

ネイティブマーカー(bca:

ネイティブ方言は bca: 名前空間と suppress 動詞を使用し、プロジェクト内部の「suppression(抑制)」語彙(SuppressionPolicyFuncSpace::suppressed--no-suppress)と一致しています。形式は 4 つあります:

マーカースコープ効果
bca: suppress囲んでいる関数すべてのメトリクスを抑制
bca: suppress(metric, ...)囲んでいる関数列挙したメトリクスのみを抑制
bca: suppress-fileファイルすべてのメトリクスを抑制
bca: suppress-file(metric, ...)ファイル列挙したメトリクスのみを抑制

The two metric-list forms may carry a rationale — free text on the same line, after the marker itself:

#![allow(unused)]
fn main() {
// bca: suppress(nargs) — threaded context, not a god-function
}

No separator is required and none is privileged: — why, - why, : why, // why, and bare prose all read the same. The two bare verbs take no trailing text at all — bca: suppress see #123 is not a marker, whatever punctuation opens the trailing words. See Rationale text for why.

関数スコープのマーカーは、そのコメントをソース範囲に含む最も内側の FuncSpaceFuncSpace の rustdoc を参照)に付与されます。どの関数本体にも含まれない関数スコープのマーカーは黙って無視されます。ファイル全体を沈黙させるには、明示的な suppress-file 動詞を使用してください。ファイルスコープのマーカーはソース内のどこに書いても構いません — 「先頭 N 行以内に書かなければならない」といった規則はありません。

bca: suppress — 関数スコープ、全メトリクス(Rust)

#![allow(unused)]
fn main() {
// bca: suppress
fn legacy_dispatch(opcode: u8) -> Action {
    // サポートされる全オペコードに対する高密度な match
    match opcode { /* ... */ }
}
}

bca: suppress(metric, ...) — 関数スコープ、列挙したメトリクス(Python)

def parse_token_stream(tokens):
    # bca: suppress(cognitive)
    # 認知的複雑度はこのステートマシンに本質的なもの。
    # 循環的複雑度は依然として制限内に収まっている。
    ...

その他のしきい値(cyclomatic、halstead、loc など)は引き続き適用されます。

bca: suppress-file — ファイルスコープ、全メトリクス(JavaScript)

// bca: suppress-file
// 手動チューニングされたホットパス。しきい値を満たすための書き換えはしないこと。
function transform(input) { /* ... */ }
function validate(input) { /* ... */ }

bca: suppress-file(metric, ...) — ファイルスコープ、列挙したメトリクス(C++)

/* bca: suppress-file(halstead) */
// Halstead の volume は下の生成テーブルによって膨らんでいる。
// それ以外のメトリクスはファイル全体で引き続き適用される。

まずはより狭いツールを検討してください。 しきい値スコープ(#969)以降、メトリクスのファイル全体または impl 全体の 集計値 が関数ごとの制限として発火することはなくなったため、suppress-file に手が伸びる最も一般的な理由 — ファイルレベルの halstead / nargs / nexits / nom の合計を黙らせること — はなくなりました。suppress-file は、ファイル内の すべての 関数についてそのメトリクスを本当に沈黙させたい場合にのみ使ってください。どうしても複雑にならざるを得ない関数を 1 つだけ除外するには、その内部に関数スコープの bca: suppress(...) を書きます。将来の悪化に対してゲートを盲目にせずに既存の違反を容認するには、ベースラインエントリを推奨します。ベースラインは、関数が記録された値より 悪化 した時点で再び発火します。

Lizard 互換マーカー

既存の Lizard 対応コードベースを書き換えずに済むよう、2 つの Lizard 形式マーカーがそのまま認識されます:

Lizard マーカースコープ対応するネイティブマーカー
#lizard forgives囲んでいる関数bca: suppress
#lizard forgive globalファイルbca: suppress-file

互換レイヤーは意図的に狭くしてあります: 受け付けるのはこの 2 つの形だけです。その他の Lizard ディレクティブは通常のコメントとして解釈されます。Lizard にはメトリクス単位のスコープ指定がないため、ネイティブ形式の bca: suppress(metric, ...) リストに相当するものは Lizard にはありません — Lizard 形式のマーカーはすべてのメトリクスを沈黙させます。

Lizard の GENERATED CODE マーカーはここでは扱いません。これは生成コードの自動スキップ機構の一部です(生成コードのスキップ--no-skip-generated フラグを参照)。

ネイティブと Lizard の対照表

効果ネイティブ形式Lizard 形式
1 つの関数のすべてのメトリクスを沈黙させる// bca: suppress// #lizard forgives
1 つの関数の 1 つのメトリクスを沈黙させる// bca: suppress(cyclomatic)(該当なし)
ファイル全体のすべてのメトリクスを沈黙させる// bca: suppress-file// #lizard forgive global
ファイル全体の 1 つのメトリクスを沈黙させる// bca: suppress-file(halstead)(該当なし)

メトリクス識別子

bca: suppress(...)bca: suppress-file(...) の内側で受け付けられる識別子は次のとおりです:

abc, cognitive, cyclomatic, halstead, loc, mi, nargs, nexits, nom, npa, npm, wmc.

これらは、しきい値名および CodeMetrics に出力される JSON フィールド名と一致しますが、意図的な除外が 1 つあります:

  • nexits is the canonical spelling — bca: suppress(nexits) silences a nexits threshold violation. The legacy exit alias was retired in #555 and is no longer accepted; spelling it exit is an unknown identifier, which warns and is skipped — the recognized names beside it still suppress (see below).
  • tokens はしきい値チェック可能なメトリクス(かつ CodeMetrics の JSON フィールド)ですが、意図的に抑制リストから外されています: マーカーで無効化することはできません。tokens は保守性のヒューリスティックではなく、ハードなリソース上限として扱ってください。

ファミリー(例えば halstead)を沈黙させると、その配下のすべてのサブメトリクスしきい値(halstead.volumehalstead.effort など)が対象になります。抑制の語彙にドット付きの形式はありません。

bca: suppress(...) リスト内の未知の識別子は、次の形式の stderr 警告を出力します

warning: path/to/file.rs:42: unknown metric 'no_such_metric' in bca suppression marker; known metrics: abc, cognitive, ...

Only the offending identifier is dropped: bca: suppress(cognitive, exit) still silences cognitive. Skipping can only ever shrink what a marker covers, so a typo never widens scope to a metric the author did not name — while voiding the whole marker, which is what earlier releases did, produced the opposite hazard: a suppression its author believed was active and the gate did not.

Unknown verbs (anything other than suppress / suppress-file) and bodies that parse to no directive at all (an unbalanced parenthesis, a bare verb followed by any trailing text) produce the same shape of warning, and those do void the marker — there is nothing left of it to honour. None of these are fatal: a typo in one file does not derail a workspace walk, and a doc comment that merely mentions the syntax does not fail your gate.

Rationale text

Text after a metric list is yours; bca parses past it and does not interpret it. The asymmetry between the two forms is deliberate:

  • After a metric listbca: suppress(nargs) threaded context — anything goes. The parentheses are a positive signal that this comment is a marker, and the list has already stated the intent unambiguously, so whatever follows is prose.

  • After a bare verbbca: suppress — irreducible dispatch — there is no trailing text. Nothing in this shape separates a rationale from a sentence about the feature, and no separator can: -, :, //, # and the dashes are exactly the punctuation someone writing // bca: suppress - we removed this marker, see #123 reaches for. Accepting them silences every metric in the enclosing function on the strength of a comment, so the whole shape warns instead.

    If you want to record a reason, name the metrics — bca: suppress(cognitive, cyclomatic) — reason — which is more precise than the All hammer anyway. If All really is what you mean, put the reason on the line above the marker.

マーカーを書ける場所

マーカーは、コメントスタイルを問わず、任意のソースコメント内で認識されます。スキャナーはマッチングの前に、先頭の区切り文字 /*!#;- と ASCII 空白を取り除きます。これは bca が現在パースするすべてのコメント形式をカバーします:

  • C 系の行コメント: // bca: suppress
  • C 系のブロックコメント: /* bca: suppress */
  • Rust の内部ドキュメントコメント: //! bca: suppress/*! bca: suppress */
  • Python / シェル / Ruby / Perl の # コメント: # bca: suppress
  • Lisp / Lua / SQL の行コメント: ;; bca: suppress-- bca: suppress

関数スコープのマーカーは、コメントの行を (start_line..=end_line) 範囲に含む最も内側の Function 種別の FuncSpace に付与されます。クラスや構造体の本体内にあってもどのメソッドにも含まれないマーカーは黙って無視されます — クラス全体を沈黙させるには bca: suppress-file を使うか、各メソッドでマーカーを繰り返してください。

ファイルスコープのマーカーはトップレベルの Unit スペースにマージされ、ネストに関係なくファイル内のすべての関数に適用されます。

マーカーはコメントの先頭近くに配置してください。スキャナーは両端の区切り文字を取り除いた後、先頭に bca:(または #lizard)が来ることを期待します。複数行ブロックコメントの奥深くに埋もれたマーカーは認識されません。

--no-suppress(CI での監査)

bca check --no-suppress は、ネイティブと Lizard の両方を含むすべての抑制マーカーを無視し、走査内のすべてのしきい値違反を報告します。生の、沈黙させていない違反リストが必要な監査パイプラインで使用してください:

bca check --paths src/ --no-suppress

このフラグはメトリクスの値自体には影響しません: 生の bca metrics 出力は常にすべての数値を報告します。bca report markdown|html はデフォルトでホットスポットテーブルにおいてマーカーを尊重し、同じ生の監査ビューのために独自の --no-suppress フラグを受け付けます。

抑制された負債の可視化(--report-suppressed

抑制は違反をゲートから外しますが、同時に --format ドキュメントからも外れます — そのため抑制されたモジュールはコードスキャンレポートから完全に消えます。bca check --report-suppressed は、それをアクティブではなく 抑制済み として書き戻します:

bca check --report-format sarif --no-fail --report-suppressed \
    --tier=soft=0.95 --output bca.sarif

Offenders silenced by an in-source marker or covered by the baseline are emitted into the SARIF document with a SARIF suppressions entry — kind: "inSource" for markers, kind: "external" for the baseline. The suppression never fails the gate (exit code and the human offender rows are unaffected); the suppressions entry lets downstream tooling tell suppressed debt apart from active offenders.

GitHub Code Scanning に関する注意。 GitHub は SARIF の suppressions プロパティをネイティブには尊重しません — 抑制された結果はクローズ済みではなく オープン なアラートとして取り込まれます。Security タブでそれらを却下するには、advanced-security/dismiss-alerts アクションのような後続ステップが必要です。このアクションは suppressions[] を読み取り、一致するアラートを却下します。アクティブな違反だけを表示したい場合は、アップロードから --report-suppressed を外してください(本リポジトリ自身の Pages ワークフローがまさにそうしています)。

注記:

  • 抑制を表現できるのは SARIF フォーマットだけです。他の --format 値はこのフラグを無視し、アクティブな違反のみを出力します。
  • ベースラインでカバーされた違反のうちハード制限を下回っているものも表示されるように、(ベースラインの由来に合わせて)--tier=soft=0.95 と併用してください。
  • (マーカーの沈黙を解除して生の違反リストを表示する)--no-suppress および --write-baseline とは相互排他です。

適用除外の監査(bca exemptions

--no-suppress はマーカーが 沈黙させている 違反を表示しますが、マーカーそのものは表示しません。従来は、すべての抑制箇所を見つけるには --no-suppress 付きの実行結果と通常の実行結果を diff する必要がありました。bca exemptions はこの回避策に代わり、bca check ゲートがスキップするすべての項目を、3 つの適用除外ティアすべてにわたって 1 つのレポートで直接一覧表示します:

ティア粒度ソース
ソース内マーカー関数単位 / ファイル単位bca: suppress#lizard forgives、…
[check.exclude] グロブグロブ単位(ファイルのカテゴリ)bca.toml [check] exclude / --check-exclude
ベースラインエントリ(path, symbol, metric) 単位.bca-baseline.toml
# ツリー内のすべての適用除外を一覧表示します(ソース内マーカーは
# 他のすべての走査コマンドと同様に [walker.exclude] を尊重します)。
bca exemptions --paths src/
# In-source markers (2)
  src/parser.rs:120  bca: suppress       metrics=all  parse_long
  src/lib.rs:1       bca: suppress-file  metrics=halstead  (whole file)

# [check.exclude] globs (1)
  tests/**

# Baseline (.bca-baseline.toml, 1 entry)
  src/markdown_report.rs write_language_section cognitive 29

(関数スコープのマーカーの場合)囲んでいる関数がスコープの文脈を示します。ファイルスコープのマーカーは (whole file) と表示されます。また、関数の外に書かれた関数スコープのマーカー(何も抑制しません)は (no enclosing fn) と表示されるため、無効なマーカーも可視化されます。

フォーマットとセクションフィルタ

--format markdown は PR コメント向けのテーブルを出力します。--format json は、ダッシュボードや jq によるフィルタリングのために、3 つのティアすべてを単一の suppressions エンベロープの下にネストします:

bca exemptions --paths src/ --format json | jq '.suppressions.markers[] | select(.dialect == "lizard")'

JSON 形式では、省略されたセクション(--*-only フラグで要求されなかったもの)は null になり、要求されたが空だったセクションは [] になるため、フィルタは両者を区別できます。

相互排他的な --markers-only / --excludes-only / --baseline-only フラグは、PR ボットの特化(例:新規追加されたソース内マーカーにのみコメントするボット)のために、レポートを単一のティアに絞り込みます。ベースライン(bca.toml トップレベルの baseline)と [check.exclude][check] exclude)の入力は、bca check が読み取るのと同じソースをデフォルトとするため、監査はゲートがスキップする内容を正確に反映します。ベースラインは --baseline <path> で上書きできます。

以前の --only-markers / --only-excludes / --only-baseline の綴りは、既存の PR ボットの呼び出しを動かし続けるため、1 リリースサイクルの間は非表示のエイリアスとして残ります。diff-baseline のセクションフィルタと一致する --<section>-only 形式を優先してください。

bca check と異なり、bca exemptions は情報提供用であり、成功時は常に終了コード 0 で終了します。これはレビューのための表示面であって、ゲートではありません。

PR レビュー中に bca exemptionsbca diff-baseline と併用する方法については、ベースラインのレシピ も参照してください。

JSON 出力

FuncSpace は、マージされた抑制スコープを JSON 出力のオプションフィールド suppressed として公開します。スペースに適用されるマーカーがない場合、このフィールドは省略されるため、既存のスナップショット利用者には変化がありません。マーカーが発動した場合、このフィールドは次の 2 つの形のいずれかになります:

{ "suppressed": { "kind": "all" } }
{ "suppressed": { "kind": "some", "metrics": ["cognitive", "loc"] } }

kind: all は素のマーカー(bca: suppressbca: suppress-file、または任意の Lizard 形式のマーカー)に対応します。kind: somebca: suppress(...) / bca: suppress-file(...) で指定された明示的なメトリクスのリストを保持します。どちらの形も、ダッシュボードや監査ログに適した安定したシリアライズ出力です。

Lizard からの移行

互換レイヤーがあるため、移行は段階的に行えます:

  1. 既存の #lizard forgives#lizard forgive global のマーカーは変更なしでそのまま機能します。bca check は追加設定なしにそれらを尊重します。
  2. 機会を見てネイティブ形式に書き換えてください。bca: suppress(...) はメトリクス単位のスコープ指定が可能で(Lizard 形式はすべてを抑制します)、将来の監査証跡機能が拡張していく形式です。

本プロジェクトは Lizard 互換レイヤーを無期限に維持します。削除予定日はありません。

予約済み構文

次の形は将来の使用のために予約されており、現時点ではパースされません

  • bca: suppress-next — silence the immediately following declaration rather than the enclosing function. Rejected today as an unknown verb; it will be promoted to first-class behavior in a future release without breaking existing markers.

The reason = "..." argument this section used to reserve is no longer planned. Write the rationale after the metric list instead — see Rationale text — which is the spelling authors already reach for. Inside the parentheses, reason = "..." is still an unknown metric identifier and is skipped with a stderr warning.