结构化 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(dog)”
      • “void pet(dog)”:无法将参数 1 从“lizard'”转换为“dog”
        • 没有可用于执行该转换的用户定义的转换运算符,或者无法调用该运算符
    • 尝试匹配参数列表“(lizard)”时

另请参阅

/experimental:log(启用结构化 SARIF 诊断)