構造化 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
。 - 呼び出しには、パラメーター名が
result
by-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(cat)': 引数 1 を 'lizard' から 'cat' に変換できません
- または 'void pet(dog)'
- 'void pet(dog)': 引数 1 を 'lizard' から 'dog' に変換できません
- この変換を実行できるユーザー定義変換演算子がない、または演算子を呼び出すことができない
- 'void pet(dog)': 引数 1 を 'lizard' から 'dog' に変換できません
- 引数リスト '(lizard)' と一致しようとしています
- 'void pet(cat)' である可能性があります