结构化 SARIF 诊断
MSVC 编译器可以将诊断输出为 SARIF(静态分析结果交换格式)。 SARIF 是一种计算机可读的基于 JSON 的格式。
可通过两种方法使 MSVC 编译器生成 SARIF 诊断:
- 在命令行中传递
/experimental:log
开关。 有关详细信息,请参阅/experimental:log
的文档。 - 以编程方式启动
cl.exe
并设置SARIF_OUTPUT_PIPE
环境变量,以通过管道检索 SARIF 块。
通过管道检索 SARIF
编译过程中使用来自 MSVC 编译器的 SARIF 的工具使用管道。 有关创建 Windows 管道的详细信息,请参阅有关 CreatePipe
的文档。
若要通过管道检索 SARIF,请将 SARIF_OUTPUT_PIPE
环境变量设置为管道写入端的 HANDLE
的 UTF-16 编码整数表示形式,然后启动 cl.exe
。 SARIF 沿管道发送,如下所示:
- 当新的诊断可用时,它会写入此管道。
- 诊断将一次性写入管道,而不是作为整个 SARIF 对象写入管道。
- 每个诊断都由一条类型为通知的 JSON-RPC 2.0 消息表示。
- JSON-RPC 消息以
Content-Length
标头为前缀,格式为Content-Length: <N>
,后跟两个换行符,其中<N>
是以下 JSON-RPC 消息的长度(以字节为单位)。 - JSON-RPC 消息和标头都以 UTF-8 编码。
- 此 JSON-RPC-with-header 格式与 vs-streamjsonrpc 兼容。
- JSON-RPC 调用的方法名称为
OnSarifResult
。 - 调用具有一个参数,该参数按名称进行编码,参数名称为
result
。 - 参数的值是 SARIF 版本 2.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)”