Diagnósticos de SARIF Estruturados
O compilador MSVC pode ser feito para gerar diagnósticos como SARIF (Static Analysis Results Interchange Format). SARIF é um formato baseado em JSON legível por máquina.
Há duas maneiras de fazer com que o compilador MSVC produza diagnósticos SARIF:
- Passe a
/experimental:log
opção na linha de comando. Consulte a documentação para/experimental:log
obter detalhes. - Inicie
cl.exe
programaticamente e defina aSARIF_OUTPUT_PIPE
variável de ambiente para recuperar blocos SARIF por meio de um pipe.
Recuperando SARIF através de um pipe
As ferramentas que consomem SARIF do compilador MSVC enquanto uma compilação está em andamento usam um pipe. Consulte a documentação para CreatePipe
obter detalhes sobre como criar pipes do Windows.
Para recuperar o SARIF por meio de um pipe, defina a SARIF_OUTPUT_PIPE
variável de ambiente como a representação inteira codificada em UTF-16 do HANDLE
para a extremidade de gravação do pipe e, em seguida, inicie cl.exe
. O SARIF é enviado ao longo do tubo da seguinte forma:
- Quando um novo diagnóstico está disponível, ele é gravado nesse pipe.
- Os diagnósticos são gravados no pipe um de cada vez, em vez de como um objeto SARIF inteiro.
- Cada diagnóstico é representado por uma mensagem JSON-RPC 2.0 do tipo Notificação.
- A mensagem JSON-RPC é prefixada com um
Content-Length
cabeçalho com o formulárioContent-Length: <N>
seguido por duas novas linhas, onde<N>
é o comprimento da seguinte mensagem JSON-RPC em bytes. - A mensagem e o cabeçalho JSON-RPC são codificados em UTF-8.
- Esse formato JSON-RPC-with-header é compatível com vs-streamjsonrpc.
- O nome do método para a chamada JSON-RPC é
OnSarifResult
. - A chamada tem um único parâmetro que é codificado por nome com o nome
result
do parâmetro . - O valor do argumento é um único
result
objeto, conforme especificado pelo padrão SARIF Versão 2.1.
Exemplo
Aqui está um exemplo de um resultado SARIF JSON-RPC produzido por cl.exe
:
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}}}]}}}
Dados do resultado SARIF
O compilador gera SARIF que pode incluir informações adicionais para representar a estrutura aninhada de alguns diagnósticos. Um diagnóstico (representado por um result
objeto SARIF) pode conter uma "árvore de diagnóstico" de informações adicionais em seu relatedLocations
campo. Essa árvore é codificada usando um recipiente de propriedades SARIF da seguinte maneira:
O campo de properties
um location
objeto pode conter uma nestingLevel
propriedade cujo valor é a profundidade desse local na árvore de diagnóstico. Se um local não tiver um nestingLevel
especificado, a profundidade será considerada 0
e esse local será filho do diagnóstico raiz representado pelo result
objeto que o contém. Caso contrário, se o valor for maior que a profundidade do local imediatamente anterior a esse local no relatedLocations
campo, esse local será filho desse local. Caso contrário, esse local é um irmão do precedente location
mais próximo no relatedLocations
campo com a mesma profundidade.
Exemplo
Considere o seguinte código:
struct dog {};
struct cat {};
void pet(dog);
void pet(cat);
struct lizard {};
int main() {
pet(lizard{});
}
Quando esse código é compilado, o compilador produz o seguinte result
objeto (physicalLocation
as propriedades foram removidas para fins de brevidade):
{
"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)'"
}
}
]
}
A árvore de diagnóstico lógico produzida a partir das mensagens neste result
objeto é:
- 'pet': nenhuma função sobrecarregada poderia converter todos os tipos de argumento
- poderia ser 'Void Pet (gato)'
- 'void pet(cat)': não é possível converter o argumento 1 de 'lagarto' para 'cat
- Nenhum operador de conversão definido pelo usuário disponível que possa executar essa conversão, ou o operador não pode ser chamado
- 'void pet(cat)': não é possível converter o argumento 1 de 'lagarto' para 'cat
- ou 'animal de estimação void (cachorro)'
- 'void pet(dog)': não é possível converter o argumento 1 de 'lizard' para 'dog'
- Nenhum operador de conversão definido pelo usuário disponível que possa executar essa conversão, ou o operador não pode ser chamado
- 'void pet(dog)': não é possível converter o argumento 1 de 'lizard' para 'dog'
- ao tentar corresponder à lista de argumentos '(lagarto)'
- poderia ser 'Void Pet (gato)'