構造化 SARIF 診断

MSVC コンパイラは、診断を SARIF (静的分析結果交換形式) として出力できます。 SARIF は、マシンで読み取り可能な JSON ベースの形式です。

MSVC コンパイラで SARIF 診断を生成するには、次の 2 つの方法があります。

  • コマンド ラインで /experimental:log スイッチを渡します。 詳細については、/experimental:log文書を参照してください。
  • プログラム cl.exe 起動し、パイプを介して SARIF ブロックを取得するように SARIF_OUTPUT_PIPE 環境変数を設定します。

パイプを介した SARIF の取得

コンパイルの進行中に MSVC コンパイラから SARIF を使用するツールは、パイプを使用します。 Windows パイプの作成の詳細については、 CreatePipe のドキュメントを参照してください。

パイプを介して SARIF を取得するには、 SARIF_OUTPUT_PIPE 環境変数を、 HANDLE の UTF-16 でエンコードされた整数表現としてパイプの書き込み末尾に設定し、 cl.exe起動します。 SARIF は、次のようにパイプに沿って送信されます。

  • 新しい診断が使用可能になると、このパイプに書き込まれます。
  • 診断は、SARIF オブジェクト全体としてではなく、一度に 1 つずつパイプに書き込まれます。
  • 各診断は、Notification 型の JSON-RPC 2.0 メッセージによって表されます。
  • JSON-RPC メッセージの先頭には、 Content-Length ヘッダーの前に、 Content-Length: <N> 形式の後に 2 つの改行が付きます。ここで、 <N> は次の JSON-RPC メッセージの長さ (バイト単位) です。
  • JSON-RPC メッセージとヘッダーはどちらも UTF-8 でエンコードされます。
  • この JSON-RPC-with-header 形式は、 vs-streamjsonrpc と互換性があります。
  • JSON-RPC 呼び出しのメソッド名は OnSarifResult
  • 呼び出しには、パラメーター名がresultby-nameエンコードされた 1 つのパラメーターがあります。
  • 引数の値は、SARIF バージョン 2.1 標準で指定されている 1 つのresult オブジェクトです。

cl.exeによって生成される JSON-RPC SARIF 結果の例を次に示します。

Content-Length: 334

{"jsonrpc":"2.0","method":"OnSarifResult","params":{"result":{"ruleId":"C1034","level":"fatal","message":{"text":"iostream: no include path set"},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"file:///C:/Users/sybrand/source/repos/cppcon-diag/cppcon-diag/cppcon-diag.cpp"},"region":{"startLine":1,"startColumn":10}}}]}}}{"jsonrpc":"2.0","method":"OnSarifResult","params":{"result":{"ruleId":"C1034","level":"fatal","message":{"text":"iostream: no include path set"},"locations":[{"physicalLocation":{"artifactLocation":{"uri":"file:///C:/Users/sybrand/source/repos/cppcon-diag/cppcon-diag/cppcon-diag.cpp"},"region":{"startLine":1,"startColumn":10}}}]}}}

SARIF の結果データ

コンパイラは、一部の診断の入れ子構造を表す追加情報を含む可能性がある SARIF を出力します。 診断 ( result SARIF オブジェクトで表される) には、その relatedLocations フィールドに追加情報の "診断ツリー" が含まれている場合があります。 このツリーは、次のように SARIF プロパティ バッグ を使用してエンコードされます。

location オブジェクトのproperties フィールドには、診断ツリー内のこの場所の深さを値とするnestingLevel プロパティが含まれている場合があります。 場所に nestingLevel が指定されていない場合、深さは 0 と見なされ、この場所は、それを含む result オブジェクトによって表されるルート診断の子です。 それ以外の場合、値が relatedLocations フィールド内のこの場所のすぐ前にある場所の深さを超える場合、この場所はその場所の子になります。 それ以外の場合、この場所は、同じ深度を持つrelatedLocations フィールド内の最も近い前のlocationの兄弟です。

次のコードがあるとします。

struct dog {};
struct cat {};

void pet(dog);
void pet(cat);

struct lizard {};

int main() {
    pet(lizard{});
}

このコードがコンパイルされると、コンパイラは次の result オブジェクトを生成します (簡潔にするためにプロパティphysicalLocation 削除されています)。

{
    "ruleId": "C2665",
    "level": "error",
    "message": {
        "text": "'pet': no overloaded function could convert all the argument types"
    },
    "relatedLocations": [
        {
            "id": 0,
            "message": {
                "text": "could be 'void pet(cat)'"
            }
        },
        {
            "id": 1,
            "message": {
                "text": "'void pet(cat)': cannot convert argument 1 from 'lizard' to 'cat'"
            },
            "properties": {
                "nestingLevel": 1
            }
        },
        {
            "id": 2,
            "message": {
                "text": "No user-defined-conversion operator available that can perform this conversion, or the operator cannot be called"
            },
            "properties": {
                "nestingLevel": 2
            }
        },
        {
            "id": 3,
            "message": {
                "text": "or       'void pet(dog)'"
            }
        },
        {
            "id": 4,
            "message": {
                "text": "'void pet(dog)': cannot convert argument 1 from 'lizard' to 'dog'"
            },
            "properties": {
                "nestingLevel": 1
            }
        },
        {
            "id": 5,
            "message": {
                "text": "No user-defined-conversion operator available that can perform this conversion, or the operator cannot be called"
            },
            "properties": {
                "nestingLevel": 2
            }
        },
        {
            "id": 6,
            "message": {
                "text": "while trying to match the argument list '(lizard)'"
            }
        }
    ]
}

この result オブジェクト内のメッセージから生成される論理診断ツリーは次のとおりです。

  • 'pet': オーバーロードされた関数がすべての引数型を変換できませんでした
    • 'void pet(cat)' である可能性があります
      • 'void pet(cat)': 引数 1 を 'lizard' から 'cat' に変換できません
        • この変換を実行できるユーザー定義変換演算子がない、または演算子を呼び出すことができない
    • または 'void pet(dog)'
      • 'void pet(dog)': 引数 1 を 'lizard' から 'dog' に変換できません
        • この変換を実行できるユーザー定義変換演算子がない、または演算子を呼び出すことができない
    • 引数リスト '(lizard)' と一致しようとしています

関連項目

/experimental:log (構造化された SARIF 診断を有効にする)