mirror of https://gitlab.com/cppit/jucipp
Browse Source
Allows to parse SARIF [1] static analysis result files, which is later used for clang-analyze integration. [1] https://docs.oasis-open.org/sarif/sarif/v2.1.0/sarif-v2.1.0.htmlmerge-requests/429/head
7 changed files with 998 additions and 0 deletions
@ -0,0 +1,336 @@ |
|||||||
|
#include "sarif.hpp" |
||||||
|
#include "utility.hpp" |
||||||
|
|
||||||
|
#define JSON_DISABLE_ENUM_SERIALIZATION 1 |
||||||
|
#include "nlohmann/json.hpp" |
||||||
|
|
||||||
|
#include <algorithm> |
||||||
|
#include <fstream> |
||||||
|
#include <iomanip> |
||||||
|
#include <unordered_set> |
||||||
|
|
||||||
|
namespace SARIF { |
||||||
|
|
||||||
|
std::string Message::to_string() const { |
||||||
|
if(arguments.empty()) |
||||||
|
return text; |
||||||
|
|
||||||
|
auto final_text = text; |
||||||
|
for(std::size_t i = 0; i < arguments.size(); ++i) { |
||||||
|
auto pattern = "{" + std::to_string(i) + "}"; |
||||||
|
for(auto pos = final_text.find(pattern); pos < final_text.size(); pos = final_text.find(pattern)) { |
||||||
|
final_text.replace(pos, pattern.size(), arguments[i]); |
||||||
|
} |
||||||
|
} |
||||||
|
return final_text; |
||||||
|
} |
||||||
|
|
||||||
|
void from_json(const nlohmann::json &json, Message &message) { |
||||||
|
message.text = json.value("text", ""); |
||||||
|
message.arguments = json.value("arguments", std::vector<std::string>{}); |
||||||
|
} |
||||||
|
|
||||||
|
bool Region::operator<(const Region &other) const noexcept { |
||||||
|
#define COMPARE(one, other) \ |
||||||
|
if(one < other) \
|
||||||
|
return true; \
|
||||||
|
else if(one > other) \
|
||||||
|
return false; |
||||||
|
|
||||||
|
COMPARE(start_line, other.start_line) |
||||||
|
COMPARE(start_column, other.start_column) |
||||||
|
COMPARE(end_line, other.end_line) |
||||||
|
COMPARE(end_column, other.end_column) |
||||||
|
return false; |
||||||
|
|
||||||
|
#undef COMPARE |
||||||
|
} |
||||||
|
|
||||||
|
void from_json(const nlohmann::json &json, Region ®ion) { |
||||||
|
region.start_line = json.value("startLine", 0U); |
||||||
|
region.end_line = json.value("endLine", region.start_line); |
||||||
|
region.start_column = json.value("startColumn", 1U); |
||||||
|
region.end_column = json.value("endColumn", region.start_column); |
||||||
|
|
||||||
|
region.snippet = json.value("snippet", nlohmann::json::object()).value("text", ""); |
||||||
|
} |
||||||
|
|
||||||
|
void from_json(const nlohmann::json &json, Location &location) { |
||||||
|
location.message = json.value("message", Message{}); |
||||||
|
|
||||||
|
auto physicalLocation = json.value("physicalLocation", nlohmann::json::object()); |
||||||
|
location.region = physicalLocation.value("region", Region{}); |
||||||
|
auto contextRegion = physicalLocation.value("contextRegion", nlohmann::json{}); |
||||||
|
if(!contextRegion.empty()) |
||||||
|
location.context_region = contextRegion.get<Region>(); |
||||||
|
|
||||||
|
auto uri = physicalLocation.value("artifactLocation", nlohmann::json::object()).value("uri", ""); |
||||||
|
if(starts_with(uri, "file://")) |
||||||
|
location.artifact_location = boost::filesystem::path{uri.substr(7)}; |
||||||
|
} |
||||||
|
|
||||||
|
using Importance = ThreadFlowLocation::Importance; |
||||||
|
NLOHMANN_JSON_SERIALIZE_ENUM(Importance, {{Importance::important, "important"}, {Importance::essential, "essential"}, {Importance::unimportant, "unimportant"}}) |
||||||
|
|
||||||
|
void from_json(const nlohmann::json &json, ThreadFlowLocation &location) { |
||||||
|
location.importance = json.value("importance", ThreadFlowLocation::Importance::important); |
||||||
|
location.location = json.value("location", Location{}); |
||||||
|
} |
||||||
|
|
||||||
|
void from_json(const nlohmann::json &json, ThreadFlow &flow) { |
||||||
|
flow.locations = json.value("locations", std::vector<ThreadFlowLocation>{}); |
||||||
|
} |
||||||
|
|
||||||
|
void from_json(const nlohmann::json &json, CodeFlow &flow) { |
||||||
|
flow.thread_flows = json.value("threadFlows", std::vector<ThreadFlow>{}); |
||||||
|
} |
||||||
|
|
||||||
|
using Level = Result::Level; |
||||||
|
NLOHMANN_JSON_SERIALIZE_ENUM(Level, {{Level::none, "none"}, {Level::error, "error"}, {Level::note, "note"}, {Level::warning, "warning"}}) |
||||||
|
|
||||||
|
std::string Result::get_message(const Location &location, bool markdown) const { |
||||||
|
auto text = location.message.to_string(); |
||||||
|
if(text.empty()) |
||||||
|
text = message.to_string(); |
||||||
|
|
||||||
|
if(markdown && !rule_id.empty() && !help_uri.empty()) |
||||||
|
text.append(" [(clang-analyze: ") |
||||||
|
.append(rule_id) |
||||||
|
.append(")](") |
||||||
|
.append(help_uri) |
||||||
|
.append(")"); |
||||||
|
else if(!rule_id.empty()) |
||||||
|
text.append(" (clang-analyze: ") |
||||||
|
.append(rule_id) |
||||||
|
.append(")"); |
||||||
|
|
||||||
|
return text; |
||||||
|
} |
||||||
|
|
||||||
|
std::string to_string(Result::Level level) { |
||||||
|
switch(level) { |
||||||
|
case Result::Level::error: |
||||||
|
return "error"; |
||||||
|
case Result::Level::none: |
||||||
|
return "none"; |
||||||
|
case Result::Level::note: |
||||||
|
return "note"; |
||||||
|
case Result::Level::warning: |
||||||
|
return "warning"; |
||||||
|
} |
||||||
|
return "(unknown)"; |
||||||
|
} |
||||||
|
|
||||||
|
void from_json(const nlohmann::json &json, Result &result) { |
||||||
|
result.code_flows = json.value("codeFlows", std::vector<CodeFlow>{}); |
||||||
|
result.level = json.value("level", Result::Level::warning); |
||||||
|
result.locations = json.value("locations", std::vector<Location>{}); |
||||||
|
result.message = json.value("message", Message{}); |
||||||
|
result.rule_id = json.value("ruleId", ""); |
||||||
|
} |
||||||
|
|
||||||
|
void from_json(const nlohmann::json &json, ReportingDescriptor &descriptor) { |
||||||
|
descriptor.id = json.value("id", ""); |
||||||
|
descriptor.name = json.value("name", ""); |
||||||
|
descriptor.help_uri = json.value("helpUri", ""); |
||||||
|
} |
||||||
|
|
||||||
|
void from_json(const nlohmann::json &json, ToolComponent &component) { |
||||||
|
component.rules = json.value("rules", std::vector<ReportingDescriptor>{}); |
||||||
|
} |
||||||
|
|
||||||
|
void from_json(const nlohmann::json &json, Tool &tool) { |
||||||
|
tool.driver = json.value("driver", ToolComponent{}); |
||||||
|
} |
||||||
|
|
||||||
|
void from_json(const nlohmann::json &json, Run &run) { |
||||||
|
run.results = json.value("results", std::vector<Result>{}); |
||||||
|
run.tool = json.value("tool", Tool{}); |
||||||
|
} |
||||||
|
|
||||||
|
void from_json(const nlohmann::json &json, Log &log) { |
||||||
|
log.runs = json.value("runs", std::vector<Run>{}); |
||||||
|
} |
||||||
|
|
||||||
|
static void recursive_add_context(nlohmann::json &json) { |
||||||
|
if(json.is_object()) { |
||||||
|
auto file_uri = json.value("artifactLocation", nlohmann::json::object()).value("uri", ""); |
||||||
|
auto region = json.value("region", Region{}); |
||||||
|
if(starts_with(file_uri, "file://") && region.start_line && region.end_line) { |
||||||
|
const std::size_t CONTEXT_LINES = 3; |
||||||
|
auto context_start_line = std::max(region.start_line, CONTEXT_LINES) - CONTEXT_LINES; |
||||||
|
auto context_num_desired_lines = region.end_line + 1 /* end is inclusive */ - region.start_line + 2 * CONTEXT_LINES; |
||||||
|
|
||||||
|
std::ifstream fis{file_uri.substr(7 /* "file://" */)}; |
||||||
|
std::string line; |
||||||
|
std::string context; |
||||||
|
std::string text; |
||||||
|
uint32_t context_num_lines = 0; |
||||||
|
|
||||||
|
for(uint32_t i = 1; i < context_start_line; ++i) |
||||||
|
std::getline(fis, line); |
||||||
|
for(uint32_t i = 0; i < context_num_desired_lines; ++i) { |
||||||
|
if(std::getline(fis, line)) { |
||||||
|
if(!context.empty()) |
||||||
|
context.append("\n"); |
||||||
|
context.append(line); |
||||||
|
++context_num_lines; |
||||||
|
|
||||||
|
if(i >= CONTEXT_LINES && i + CONTEXT_LINES < context_num_desired_lines) { |
||||||
|
if(i + CONTEXT_LINES + 1 == context_num_desired_lines && region.end_column > 1) |
||||||
|
line = line.substr(0, region.end_column - 1U); |
||||||
|
if(i == CONTEXT_LINES && region.start_column) |
||||||
|
line = line.substr(region.start_column - 1); |
||||||
|
if(!text.empty()) |
||||||
|
text.append("\n"); |
||||||
|
text.append(line); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
if(!context.empty()) |
||||||
|
json["contextRegion"] = nlohmann::json::object({ |
||||||
|
{"startLine", context_start_line}, |
||||||
|
{"endLine", context_start_line + context_num_lines - 1}, |
||||||
|
{"snippet", nlohmann::json::object({{"text", context}})}, |
||||||
|
}); |
||||||
|
if(!text.empty() && !json["region"].contains("snippet")) |
||||||
|
json["region"]["snippet"] = nlohmann::json::object({{"text", text}}); |
||||||
|
} |
||||||
|
} |
||||||
|
if(json.is_structured()) { |
||||||
|
for(auto &entry : json) { |
||||||
|
recursive_add_context(entry); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
static void recursive_remove_index(nlohmann::json &json) { |
||||||
|
if(json.is_object()) { |
||||||
|
auto it = json.find("index"); |
||||||
|
if(it != json.end()) |
||||||
|
json.erase(it); |
||||||
|
} |
||||||
|
if(json.is_structured()) { |
||||||
|
for(auto &entry : json) { |
||||||
|
recursive_remove_index(entry); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
std::vector<Result> merge_files(const boost::filesystem::path &output_file, std::vector<boost::filesystem::path> &input_files) { |
||||||
|
input_files.erase(std::remove_if(input_files.begin(), input_files.end(), [](const boost::filesystem::path &file) { |
||||||
|
return !boost::filesystem::exists(file); |
||||||
|
}), |
||||||
|
input_files.end()); |
||||||
|
|
||||||
|
// Deduplicate results added by multiple analyzes, e.g. for finding in headers
|
||||||
|
std::unordered_set<nlohmann::json> added_results; |
||||||
|
|
||||||
|
if(input_files.empty()) { |
||||||
|
boost::filesystem::remove(output_file); |
||||||
|
return {}; |
||||||
|
} |
||||||
|
|
||||||
|
std::ifstream fis{input_files.front().string()}; |
||||||
|
auto output = nlohmann::json::parse(fis); |
||||||
|
recursive_remove_index(output); |
||||||
|
auto &runs = output["runs"]; |
||||||
|
if(runs.is_null() || (runs.size() == 1 && runs[0]["results"].empty())) |
||||||
|
runs = nlohmann::json::array(); |
||||||
|
|
||||||
|
auto it = input_files.begin(); |
||||||
|
for(++it; it < input_files.end(); ++it) { |
||||||
|
fis = std::ifstream{it->string()}; |
||||||
|
auto part = nlohmann::json::parse(fis); |
||||||
|
auto it = part.find("runs"); |
||||||
|
if(it != part.end() && it->is_array()) { |
||||||
|
for(auto &run : *it) { |
||||||
|
auto &results = run["results"]; |
||||||
|
for(auto result_it = results.begin(); result_it != results.end();) { |
||||||
|
// Remove some values which we don't care about and might differ for otherwise identical results
|
||||||
|
auto it = result_it->find("ruleIndex"); |
||||||
|
if(it != result_it->end()) |
||||||
|
result_it->erase(it); |
||||||
|
recursive_remove_index(*result_it); |
||||||
|
|
||||||
|
if(added_results.find(*result_it) != added_results.end()) |
||||||
|
result_it = results.erase(result_it); |
||||||
|
else { |
||||||
|
added_results.emplace(*result_it); |
||||||
|
++result_it; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
if(results.empty()) |
||||||
|
// Do not merge runs without findings
|
||||||
|
continue; |
||||||
|
|
||||||
|
runs.insert(runs.end(), std::move(run)); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
recursive_add_context(output); |
||||||
|
|
||||||
|
std::ofstream fos{output_file.string()}; |
||||||
|
fos << std::setw(2) << output << std::endl; |
||||||
|
|
||||||
|
std::vector<Result> results{}; |
||||||
|
for(const auto &run : runs) { |
||||||
|
auto new_results = run.value("results", std::vector<Result>{}); |
||||||
|
results.insert(results.end(), std::make_move_iterator(new_results.begin()), std::make_move_iterator(new_results.end())); |
||||||
|
} |
||||||
|
return results; |
||||||
|
} |
||||||
|
|
||||||
|
std::vector<Result> results_for_file(const boost::filesystem::path &results_file, const boost::filesystem::path &source_file) { |
||||||
|
if(!boost::filesystem::exists(results_file)) |
||||||
|
return {}; |
||||||
|
|
||||||
|
const auto find_help_uri = [](const Run &run, const std::string &id) { |
||||||
|
for(const auto &rule : run.tool.driver.rules) { |
||||||
|
if(rule.id == id) |
||||||
|
return rule.help_uri; |
||||||
|
} |
||||||
|
return std::string{}; |
||||||
|
}; |
||||||
|
|
||||||
|
std::ifstream fis{results_file.string()}; |
||||||
|
auto log = nlohmann::json::parse(fis, nullptr).get<Log>(); |
||||||
|
|
||||||
|
std::vector<Result> results; |
||||||
|
for(auto &run : log.runs) { |
||||||
|
for(auto result : run.results) { |
||||||
|
if(std::any_of(result.locations.begin(), result.locations.end(), [&](const auto &loc) { |
||||||
|
return loc.artifact_location == source_file; |
||||||
|
})) { |
||||||
|
result.help_uri = find_help_uri(run, result.rule_id); |
||||||
|
results.push_back(std::move(result)); |
||||||
|
continue; |
||||||
|
} |
||||||
|
|
||||||
|
bool result_added = false; |
||||||
|
for(auto &code_flow : result.code_flows) { |
||||||
|
if(result_added) |
||||||
|
break; |
||||||
|
|
||||||
|
for(auto &thread_flow : code_flow.thread_flows) { |
||||||
|
if(result_added) |
||||||
|
break; |
||||||
|
|
||||||
|
if(std::any_of(thread_flow.locations.begin(), thread_flow.locations.end(), [&](const auto &loc) { |
||||||
|
return loc.location.artifact_location == source_file; |
||||||
|
})) { |
||||||
|
result.help_uri = find_help_uri(run, result.rule_id); |
||||||
|
results.push_back(std::move(result)); |
||||||
|
result_added = true; |
||||||
|
break; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
return results; |
||||||
|
} |
||||||
|
} // namespace SARIF
|
||||||
@ -0,0 +1,121 @@ |
|||||||
|
#pragma once |
||||||
|
|
||||||
|
#include <string> |
||||||
|
#include <vector> |
||||||
|
|
||||||
|
#include <boost/filesystem.hpp> |
||||||
|
#include <boost/optional.hpp> |
||||||
|
|
||||||
|
#include "nlohmann/json_fwd.hpp" |
||||||
|
|
||||||
|
namespace SARIF { |
||||||
|
|
||||||
|
struct Message { |
||||||
|
std::string text; |
||||||
|
std::vector<std::string> arguments; |
||||||
|
|
||||||
|
std::string to_string() const; |
||||||
|
}; |
||||||
|
void from_json(const nlohmann::json &json, Message &message); |
||||||
|
|
||||||
|
struct Region { |
||||||
|
std::size_t start_line; |
||||||
|
std::size_t end_line; |
||||||
|
std::size_t start_column; |
||||||
|
std::size_t end_column; |
||||||
|
std::string snippet; |
||||||
|
|
||||||
|
bool operator<(const Region &other) const noexcept; |
||||||
|
}; |
||||||
|
void from_json(const nlohmann::json &json, Region ®ion); |
||||||
|
|
||||||
|
struct Location { |
||||||
|
Message message; |
||||||
|
boost::filesystem::path artifact_location; |
||||||
|
Region region; |
||||||
|
boost::optional<Region> context_region; |
||||||
|
}; |
||||||
|
void from_json(const nlohmann::json &json, Location &location); |
||||||
|
|
||||||
|
struct ThreadFlowLocation { |
||||||
|
enum class Importance { |
||||||
|
important, |
||||||
|
essential, |
||||||
|
unimportant, |
||||||
|
}; |
||||||
|
|
||||||
|
Importance importance; |
||||||
|
Location location; |
||||||
|
}; |
||||||
|
void from_json(const nlohmann::json &json, ThreadFlowLocation &location); |
||||||
|
|
||||||
|
struct ThreadFlow { |
||||||
|
std::vector<ThreadFlowLocation> locations; |
||||||
|
}; |
||||||
|
void from_json(const nlohmann::json &json, ThreadFlow &flow); |
||||||
|
|
||||||
|
struct CodeFlow { |
||||||
|
std::vector<ThreadFlow> thread_flows; |
||||||
|
}; |
||||||
|
void from_json(const nlohmann::json &json, CodeFlow &flow); |
||||||
|
|
||||||
|
struct Result { |
||||||
|
enum class Level { |
||||||
|
none, |
||||||
|
note, |
||||||
|
warning, |
||||||
|
error, |
||||||
|
}; |
||||||
|
|
||||||
|
Level level; |
||||||
|
Message message; |
||||||
|
std::vector<Location> locations; |
||||||
|
std::vector<CodeFlow> code_flows; |
||||||
|
std::string rule_id; |
||||||
|
// Custom field
|
||||||
|
std::string help_uri; |
||||||
|
|
||||||
|
std::string get_message(const Location &location, bool markdown = false) const; |
||||||
|
}; |
||||||
|
std::string to_string(Result::Level level); |
||||||
|
void from_json(const nlohmann::json &json, Result &result); |
||||||
|
|
||||||
|
struct ReportingDescriptor { |
||||||
|
std::string id; |
||||||
|
std::string name; |
||||||
|
std::string help_uri; |
||||||
|
}; |
||||||
|
void from_json(const nlohmann::json &json, ReportingDescriptor &descriptor); |
||||||
|
|
||||||
|
struct ToolComponent { |
||||||
|
std::vector<ReportingDescriptor> rules; |
||||||
|
}; |
||||||
|
void from_json(const nlohmann::json &json, ToolComponent &component); |
||||||
|
|
||||||
|
struct Tool { |
||||||
|
ToolComponent driver; |
||||||
|
}; |
||||||
|
void from_json(const nlohmann::json &json, Tool &tool); |
||||||
|
|
||||||
|
struct Run { |
||||||
|
std::vector<Result> results; |
||||||
|
Tool tool; |
||||||
|
}; |
||||||
|
void from_json(const nlohmann::json &json, Run &run); |
||||||
|
|
||||||
|
// According to Static Analysis Results Interchange Format (SARIF) specification version 2.1,
|
||||||
|
// see https://docs.oasis-open.org/sarif/sarif/v2.1.0/sarif-v2.1.0.html
|
||||||
|
struct Log { |
||||||
|
std::vector<Run> runs; |
||||||
|
}; |
||||||
|
void from_json(const nlohmann::json &json, Log &log); |
||||||
|
|
||||||
|
/// Merges the results of all input files.
|
||||||
|
/// Removes duplicate entries (e.g. findings in headers included in multiple files).
|
||||||
|
/// Adds a context region for each location where possible.
|
||||||
|
/// Returns the list of merged results.
|
||||||
|
std::vector<Result> merge_files(const boost::filesystem::path &output_file, std::vector<boost::filesystem::path> &input_files); |
||||||
|
|
||||||
|
/// Returns the list of results for the given file.
|
||||||
|
std::vector<Result> results_for_file(const boost::filesystem::path &results_file, const boost::filesystem::path &source_file); |
||||||
|
} // namespace SARIF
|
||||||
@ -0,0 +1,72 @@ |
|||||||
|
#include "sarif.hpp" |
||||||
|
|
||||||
|
#include "nlohmann/json.hpp" |
||||||
|
#include <glib.h> |
||||||
|
|
||||||
|
#include <vector> |
||||||
|
|
||||||
|
int main() { |
||||||
|
|
||||||
|
const auto tests_path = boost::filesystem::canonical(JUCI_TESTS_PATH) / "sarif_test_files"; |
||||||
|
std::vector<boost::filesystem::path> example_files = { |
||||||
|
tests_path / "example_run.sarif.json", |
||||||
|
tests_path / "example_run2.sarif.json", |
||||||
|
tests_path / "no_such_file.sarif.json", |
||||||
|
}; |
||||||
|
const auto output_file = tests_path / "merged.sarif.json"; |
||||||
|
|
||||||
|
boost::filesystem::remove(output_file); |
||||||
|
g_assert(!boost::filesystem::exists(output_file)); |
||||||
|
|
||||||
|
auto results = SARIF::merge_files(output_file, example_files); |
||||||
|
g_assert_cmpuint(results.size(), ==, 2U); |
||||||
|
|
||||||
|
results = SARIF::results_for_file(output_file, "/home/user/jucipp/src/source_base.cpp"); |
||||||
|
g_assert_cmpuint(results.size(), ==, 1U); |
||||||
|
const auto &result = results.front(); |
||||||
|
g_assert(result.rule_id == "alpha.cplusplus.IteratorRange"); |
||||||
|
g_assert(result.message.text == "Past-the-end iterator dereferenced"); |
||||||
|
g_assert(result.message.arguments.empty()); |
||||||
|
g_assert(result.level == SARIF::Result::Level::warning); |
||||||
|
g_assert(result.help_uri == "https://clang.llvm.org/docs/analyzer/checkers.html#alpha-cplusplus-iteratorrange"); |
||||||
|
|
||||||
|
g_assert_cmpuint(result.locations.size(), ==, 1); |
||||||
|
const auto &loc = result.locations.front(); |
||||||
|
g_assert(loc.artifact_location == "/home/user/jucipp/src/source_base.cpp"); |
||||||
|
g_assert_cmpuint(loc.region.start_line, ==, 1940); |
||||||
|
g_assert_cmpuint(loc.region.end_line, ==, 1940); |
||||||
|
g_assert_cmpuint(loc.region.start_column, ==, 35); |
||||||
|
g_assert_cmpuint(loc.region.end_column, ==, 57); |
||||||
|
g_assert(loc.message.text.empty()); |
||||||
|
g_assert(loc.message.arguments.empty()); |
||||||
|
g_assert(result.get_message(loc, false) == "Past-the-end iterator dereferenced (clang-analyze: alpha.cplusplus.IteratorRange)"); |
||||||
|
g_assert(result.get_message(loc, true) == "Past-the-end iterator dereferenced [(clang-analyze: alpha.cplusplus.IteratorRange)](https://clang.llvm.org/docs/analyzer/checkers.html#alpha-cplusplus-iteratorrange)"); |
||||||
|
|
||||||
|
g_assert_cmpuint(result.code_flows.size(), ==, 1); |
||||||
|
const auto &codeFlow = result.code_flows.front(); |
||||||
|
|
||||||
|
g_assert_cmpuint(codeFlow.thread_flows.size(), ==, 1); |
||||||
|
const auto &threadFlow = codeFlow.thread_flows.front(); |
||||||
|
|
||||||
|
g_assert_cmpuint(threadFlow.locations.size(), ==, 14); |
||||||
|
|
||||||
|
const auto &firstLoc = threadFlow.locations.front(); |
||||||
|
g_assert(firstLoc.importance == SARIF::ThreadFlowLocation::Importance::unimportant); |
||||||
|
g_assert(firstLoc.location.message.text == "Taking false branch"); |
||||||
|
g_assert(firstLoc.location.message.arguments.empty()); |
||||||
|
g_assert(firstLoc.location.artifact_location == "/home/user/jucipp/src/source_base.cpp"); |
||||||
|
g_assert(firstLoc.location.region.start_line == 1875); |
||||||
|
g_assert(firstLoc.location.region.end_line == 1875); |
||||||
|
g_assert(firstLoc.location.region.start_column == 3); |
||||||
|
g_assert(firstLoc.location.region.end_column == 3); |
||||||
|
|
||||||
|
const auto &lastLoc = threadFlow.locations.back(); |
||||||
|
g_assert(lastLoc.importance == SARIF::ThreadFlowLocation::Importance::essential); |
||||||
|
g_assert(lastLoc.location.message.text == "Past-the-end iterator dereferenced"); |
||||||
|
g_assert(lastLoc.location.message.arguments.empty()); |
||||||
|
g_assert(lastLoc.location.artifact_location == "/home/user/jucipp/src/source_base.cpp"); |
||||||
|
g_assert(lastLoc.location.region.start_line == 1940); |
||||||
|
g_assert(lastLoc.location.region.end_line == 1940); |
||||||
|
g_assert(lastLoc.location.region.start_column == 35); |
||||||
|
g_assert(lastLoc.location.region.end_column == 57); |
||||||
|
} |
||||||
@ -0,0 +1,353 @@ |
|||||||
|
{ |
||||||
|
"$schema": "https://docs.oasis-open.org/sarif/sarif/v2.1.0/cos02/schemas/sarif-schema-2.1.0.json", |
||||||
|
"runs": [ |
||||||
|
{ |
||||||
|
"artifacts": [ |
||||||
|
{ |
||||||
|
"length": 75606, |
||||||
|
"location": { |
||||||
|
"index": 0, |
||||||
|
"uri": "file:///home/user/jucipp/src/source_base.cpp" |
||||||
|
}, |
||||||
|
"mimeType": "text/plain", |
||||||
|
"roles": [ |
||||||
|
"resultFile" |
||||||
|
] |
||||||
|
} |
||||||
|
], |
||||||
|
"columnKind": "unicodeCodePoints", |
||||||
|
"results": [ |
||||||
|
{ |
||||||
|
"codeFlows": [ |
||||||
|
{ |
||||||
|
"threadFlows": [ |
||||||
|
{ |
||||||
|
"locations": [ |
||||||
|
{ |
||||||
|
"importance": "unimportant", |
||||||
|
"location": { |
||||||
|
"message": { |
||||||
|
"text": "Taking false branch" |
||||||
|
}, |
||||||
|
"physicalLocation": { |
||||||
|
"artifactLocation": { |
||||||
|
"index": 0, |
||||||
|
"uri": "file:///home/user/jucipp/src/source_base.cpp" |
||||||
|
}, |
||||||
|
"region": { |
||||||
|
"endColumn": 3, |
||||||
|
"startColumn": 3, |
||||||
|
"startLine": 1875 |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
}, |
||||||
|
{ |
||||||
|
"importance": "important", |
||||||
|
"location": { |
||||||
|
"message": { |
||||||
|
"text": "Assuming the condition is false" |
||||||
|
}, |
||||||
|
"physicalLocation": { |
||||||
|
"artifactLocation": { |
||||||
|
"index": 0, |
||||||
|
"uri": "file:///home/user/jucipp/src/source_base.cpp" |
||||||
|
}, |
||||||
|
"region": { |
||||||
|
"endColumn": 52, |
||||||
|
"endLine": 1884, |
||||||
|
"startColumn": 6, |
||||||
|
"startLine": 1884 |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
}, |
||||||
|
{ |
||||||
|
"importance": "unimportant", |
||||||
|
"location": { |
||||||
|
"message": { |
||||||
|
"text": "Taking false branch" |
||||||
|
}, |
||||||
|
"physicalLocation": { |
||||||
|
"artifactLocation": { |
||||||
|
"index": 0, |
||||||
|
"uri": "file:///home/user/jucipp/src/source_base.cpp" |
||||||
|
}, |
||||||
|
"region": { |
||||||
|
"endColumn": 3, |
||||||
|
"startColumn": 3, |
||||||
|
"startLine": 1884 |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
}, |
||||||
|
{ |
||||||
|
"importance": "unimportant", |
||||||
|
"location": { |
||||||
|
"message": { |
||||||
|
"text": "'erase_line' is false" |
||||||
|
}, |
||||||
|
"physicalLocation": { |
||||||
|
"artifactLocation": { |
||||||
|
"index": 0, |
||||||
|
"uri": "file:///home/user/jucipp/src/source_base.cpp" |
||||||
|
}, |
||||||
|
"region": { |
||||||
|
"endColumn": 11, |
||||||
|
"startColumn": 11, |
||||||
|
"startLine": 1888 |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
}, |
||||||
|
{ |
||||||
|
"importance": "unimportant", |
||||||
|
"location": { |
||||||
|
"message": { |
||||||
|
"text": "Taking false branch" |
||||||
|
}, |
||||||
|
"physicalLocation": { |
||||||
|
"artifactLocation": { |
||||||
|
"index": 0, |
||||||
|
"uri": "file:///home/user/jucipp/src/source_base.cpp" |
||||||
|
}, |
||||||
|
"region": { |
||||||
|
"endColumn": 8, |
||||||
|
"startColumn": 8, |
||||||
|
"startLine": 1888 |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
}, |
||||||
|
{ |
||||||
|
"importance": "unimportant", |
||||||
|
"location": { |
||||||
|
"message": { |
||||||
|
"text": "'erase_word' is false" |
||||||
|
}, |
||||||
|
"physicalLocation": { |
||||||
|
"artifactLocation": { |
||||||
|
"index": 0, |
||||||
|
"uri": "file:///home/user/jucipp/src/source_base.cpp" |
||||||
|
}, |
||||||
|
"region": { |
||||||
|
"endColumn": 11, |
||||||
|
"startColumn": 11, |
||||||
|
"startLine": 1894 |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
}, |
||||||
|
{ |
||||||
|
"importance": "unimportant", |
||||||
|
"location": { |
||||||
|
"message": { |
||||||
|
"text": "Left side of '&&' is false" |
||||||
|
}, |
||||||
|
"physicalLocation": { |
||||||
|
"artifactLocation": { |
||||||
|
"index": 0, |
||||||
|
"uri": "file:///home/user/jucipp/src/source_base.cpp" |
||||||
|
}, |
||||||
|
"region": { |
||||||
|
"endColumn": 22, |
||||||
|
"startColumn": 22, |
||||||
|
"startLine": 1894 |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
}, |
||||||
|
{ |
||||||
|
"importance": "important", |
||||||
|
"location": { |
||||||
|
"message": { |
||||||
|
"text": "Assuming the condition is true" |
||||||
|
}, |
||||||
|
"physicalLocation": { |
||||||
|
"artifactLocation": { |
||||||
|
"index": 0, |
||||||
|
"uri": "file:///home/user/jucipp/src/source_base.cpp" |
||||||
|
}, |
||||||
|
"region": { |
||||||
|
"endColumn": 46, |
||||||
|
"endLine": 1914, |
||||||
|
"startColumn": 6, |
||||||
|
"startLine": 1914 |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
}, |
||||||
|
{ |
||||||
|
"importance": "unimportant", |
||||||
|
"location": { |
||||||
|
"message": { |
||||||
|
"text": "Taking true branch" |
||||||
|
}, |
||||||
|
"physicalLocation": { |
||||||
|
"artifactLocation": { |
||||||
|
"index": 0, |
||||||
|
"uri": "file:///home/user/jucipp/src/source_base.cpp" |
||||||
|
}, |
||||||
|
"region": { |
||||||
|
"endColumn": 3, |
||||||
|
"startColumn": 3, |
||||||
|
"startLine": 1914 |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
}, |
||||||
|
{ |
||||||
|
"importance": "essential", |
||||||
|
"location": { |
||||||
|
"message": { |
||||||
|
"text": "Calling 'BaseView::select_snippet_parameter'" |
||||||
|
}, |
||||||
|
"physicalLocation": { |
||||||
|
"artifactLocation": { |
||||||
|
"index": 0, |
||||||
|
"uri": "file:///home/user/jucipp/src/source_base.cpp" |
||||||
|
}, |
||||||
|
"region": { |
||||||
|
"endColumn": 31, |
||||||
|
"endLine": 1915, |
||||||
|
"startColumn": 5, |
||||||
|
"startLine": 1915 |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
}, |
||||||
|
{ |
||||||
|
"importance": "unimportant", |
||||||
|
"location": { |
||||||
|
"message": { |
||||||
|
"text": "Loop condition is false. Execution continues on line 1926" |
||||||
|
}, |
||||||
|
"physicalLocation": { |
||||||
|
"artifactLocation": { |
||||||
|
"index": 0, |
||||||
|
"uri": "file:///home/user/jucipp/src/source_base.cpp" |
||||||
|
}, |
||||||
|
"region": { |
||||||
|
"endColumn": 3, |
||||||
|
"startColumn": 3, |
||||||
|
"startLine": 1919 |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
}, |
||||||
|
{ |
||||||
|
"importance": "important", |
||||||
|
"location": { |
||||||
|
"message": { |
||||||
|
"text": "Assuming the condition is true" |
||||||
|
}, |
||||||
|
"physicalLocation": { |
||||||
|
"artifactLocation": { |
||||||
|
"index": 0, |
||||||
|
"uri": "file:///home/user/jucipp/src/source_base.cpp" |
||||||
|
}, |
||||||
|
"region": { |
||||||
|
"endColumn": 38, |
||||||
|
"endLine": 1937, |
||||||
|
"startColumn": 6, |
||||||
|
"startLine": 1937 |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
}, |
||||||
|
{ |
||||||
|
"importance": "unimportant", |
||||||
|
"location": { |
||||||
|
"message": { |
||||||
|
"text": "Taking true branch" |
||||||
|
}, |
||||||
|
"physicalLocation": { |
||||||
|
"artifactLocation": { |
||||||
|
"index": 0, |
||||||
|
"uri": "file:///home/user/jucipp/src/source_base.cpp" |
||||||
|
}, |
||||||
|
"region": { |
||||||
|
"endColumn": 3, |
||||||
|
"startColumn": 3, |
||||||
|
"startLine": 1937 |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
}, |
||||||
|
{ |
||||||
|
"importance": "essential", |
||||||
|
"location": { |
||||||
|
"message": { |
||||||
|
"text": "Past-the-end iterator dereferenced" |
||||||
|
}, |
||||||
|
"physicalLocation": { |
||||||
|
"artifactLocation": { |
||||||
|
"index": 0, |
||||||
|
"uri": "file:///home/user/jucipp/src/source_base.cpp" |
||||||
|
}, |
||||||
|
"region": { |
||||||
|
"endColumn": 57, |
||||||
|
"endLine": 1940, |
||||||
|
"startColumn": 35, |
||||||
|
"startLine": 1940 |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
] |
||||||
|
} |
||||||
|
] |
||||||
|
} |
||||||
|
], |
||||||
|
"level": "warning", |
||||||
|
"locations": [ |
||||||
|
{ |
||||||
|
"physicalLocation": { |
||||||
|
"artifactLocation": { |
||||||
|
"index": 0, |
||||||
|
"uri": "file:///home/user/jucipp/src/source_base.cpp" |
||||||
|
}, |
||||||
|
"region": { |
||||||
|
"endColumn": 57, |
||||||
|
"endLine": 1940, |
||||||
|
"startColumn": 35, |
||||||
|
"startLine": 1940 |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
], |
||||||
|
"message": { |
||||||
|
"text": "Past-the-end iterator dereferenced" |
||||||
|
}, |
||||||
|
"ruleId": "alpha.cplusplus.IteratorRange", |
||||||
|
"ruleIndex": 0 |
||||||
|
} |
||||||
|
], |
||||||
|
"tool": { |
||||||
|
"driver": { |
||||||
|
"fullName": "clang static analyzer", |
||||||
|
"informationUri": "https://clang.llvm.org/docs/UsersManual.html", |
||||||
|
"language": "en-US", |
||||||
|
"name": "clang", |
||||||
|
"rules": [ |
||||||
|
{ |
||||||
|
"defaultConfiguration": { |
||||||
|
"enabled": true, |
||||||
|
"level": "warning", |
||||||
|
"rank": -1 |
||||||
|
}, |
||||||
|
"fullDescription": { |
||||||
|
"text": "Check for iterators used outside their valid ranges" |
||||||
|
}, |
||||||
|
"helpUri": "https://clang.llvm.org/docs/analyzer/checkers.html#alpha-cplusplus-iteratorrange", |
||||||
|
"id": "alpha.cplusplus.IteratorRange", |
||||||
|
"name": "alpha.cplusplus.IteratorRange" |
||||||
|
} |
||||||
|
], |
||||||
|
"version": "clang version 21.1.8 (Fedora 21.1.8-4.fc43)" |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
], |
||||||
|
"version": "2.1.0" |
||||||
|
} |
||||||
@ -0,0 +1,111 @@ |
|||||||
|
{ |
||||||
|
"$schema": "https://docs.oasis-open.org/sarif/sarif/v2.1.0/cos02/schemas/sarif-schema-2.1.0.json", |
||||||
|
"runs": [ |
||||||
|
{ |
||||||
|
"artifacts": [ |
||||||
|
{ |
||||||
|
"length": 3814, |
||||||
|
"location": { |
||||||
|
"index": 0, |
||||||
|
"uri": "file:///home/user/jucipp/src/config.hpp" |
||||||
|
}, |
||||||
|
"mimeType": "text/plain", |
||||||
|
"roles": [ |
||||||
|
"resultFile" |
||||||
|
] |
||||||
|
} |
||||||
|
], |
||||||
|
"columnKind": "unicodeCodePoints", |
||||||
|
"results": [ |
||||||
|
{ |
||||||
|
"codeFlows": [ |
||||||
|
{ |
||||||
|
"threadFlows": [ |
||||||
|
{ |
||||||
|
"locations": [ |
||||||
|
{ |
||||||
|
"importance": "essential", |
||||||
|
"location": { |
||||||
|
"message": { |
||||||
|
"text": "Excessive padding in 'class Config::Source' (35 padding bytes, where 3 is optimal). Optimal fields order: style, font, spellcheck_language, show_whitespace_characters, word_wrap, clang_format_style, clang_tidy_checks, documentation_searches, map_font_size, right_margin_position, pixels_between_lines, default_tab_size, tooltip_top_offset, clang_usages_threads, add_missing_newline_at_end_of_file_on_save, remove_trailing_whitespace_characters_on_save, format_style_on_save, format_style_on_save_if_style_file_found, smart_brackets, smart_inserts, show_map, show_git_diff, show_background_pattern, show_right_margin, auto_tab_char_and_size, default_tab_char, tab_indents_line, highlight_current_line, show_line_numbers, enable_multiple_cursors, auto_reload_changed_files, search_for_selection, clang_tidy_enable, clang_detailed_preprocessing_record, debug_place_cursor_at_stop, consider reordering the fields or adding explicit padding members" |
||||||
|
}, |
||||||
|
"physicalLocation": { |
||||||
|
"artifactLocation": { |
||||||
|
"uri": "file:///home/user/jucipp/src/config.hpp" |
||||||
|
}, |
||||||
|
"contextRegion": { |
||||||
|
"endLine": 65, |
||||||
|
"snippet": { |
||||||
|
"text": " std::vector<std::string> clang_analyze_checks;\n };\n\n class Source {\n public:\n class DocumentationSearch {\n public:" |
||||||
|
}, |
||||||
|
"startLine": 59 |
||||||
|
}, |
||||||
|
"region": { |
||||||
|
"endColumn": 9, |
||||||
|
"startColumn": 9, |
||||||
|
"startLine": 62 |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
] |
||||||
|
} |
||||||
|
] |
||||||
|
} |
||||||
|
], |
||||||
|
"level": "warning", |
||||||
|
"locations": [ |
||||||
|
{ |
||||||
|
"physicalLocation": { |
||||||
|
"artifactLocation": { |
||||||
|
"uri": "file:///home/user/jucipp/src/config.hpp" |
||||||
|
}, |
||||||
|
"contextRegion": { |
||||||
|
"endLine": 65, |
||||||
|
"snippet": { |
||||||
|
"text": " std::vector<std::string> clang_analyze_checks;\n };\n\n class Source {\n public:\n class DocumentationSearch {\n public:" |
||||||
|
}, |
||||||
|
"startLine": 59 |
||||||
|
}, |
||||||
|
"region": { |
||||||
|
"endColumn": 9, |
||||||
|
"startColumn": 9, |
||||||
|
"startLine": 62 |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
], |
||||||
|
"message": { |
||||||
|
"text": "Excessive padding in 'class Config::Source' (35 padding bytes, where 3 is optimal). Optimal fields order: style, font, spellcheck_language, show_whitespace_characters, word_wrap, clang_format_style, clang_tidy_checks, documentation_searches, map_font_size, right_margin_position, pixels_between_lines, default_tab_size, tooltip_top_offset, clang_usages_threads, add_missing_newline_at_end_of_file_on_save, remove_trailing_whitespace_characters_on_save, format_style_on_save, format_style_on_save_if_style_file_found, smart_brackets, smart_inserts, show_map, show_git_diff, show_background_pattern, show_right_margin, auto_tab_char_and_size, default_tab_char, tab_indents_line, highlight_current_line, show_line_numbers, enable_multiple_cursors, auto_reload_changed_files, search_for_selection, clang_tidy_enable, clang_detailed_preprocessing_record, debug_place_cursor_at_stop, consider reordering the fields or adding explicit padding members" |
||||||
|
}, |
||||||
|
"ruleId": "optin.performance.Padding" |
||||||
|
} |
||||||
|
], |
||||||
|
"tool": { |
||||||
|
"driver": { |
||||||
|
"fullName": "clang static analyzer", |
||||||
|
"informationUri": "https://clang.llvm.org/docs/UsersManual.html", |
||||||
|
"language": "en-US", |
||||||
|
"name": "clang", |
||||||
|
"rules": [ |
||||||
|
{ |
||||||
|
"defaultConfiguration": { |
||||||
|
"enabled": true, |
||||||
|
"level": "warning", |
||||||
|
"rank": -1 |
||||||
|
}, |
||||||
|
"fullDescription": { |
||||||
|
"text": "Check for excessively padded structs." |
||||||
|
}, |
||||||
|
"helpUri": "https://clang.llvm.org/docs/analyzer/checkers.html#optin-performance-padding", |
||||||
|
"id": "optin.performance.Padding", |
||||||
|
"name": "optin.performance.Padding" |
||||||
|
} |
||||||
|
], |
||||||
|
"version": "clang version 21.1.8 (Fedora 21.1.8-4.fc43)" |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
], |
||||||
|
"version": "2.1.0" |
||||||
|
} |
||||||
Loading…
Reference in new issue