Browse Source

Show clang-analyze results in source code view

The analysis results are parsed from the SARIF file and are shown
as diagnostics similar to compiler wranings.

Also supports a simple algorithm for finding an original source code
location for a diagnostic, if the source code changed since the
analysis run.
merge-requests/429/head
doe300 3 weeks ago
parent
commit
2872608b02
  1. 91
      src/source_clang.cpp
  2. 5
      src/source_clang.hpp

91
src/source_clang.cpp

@ -12,6 +12,7 @@
#include "documentation.hpp"
#include "filesystem.hpp"
#include "info.hpp"
#include "sarif.hpp"
#include "selection_dialog.hpp"
#include "usages_clang.hpp"
#include "utility.hpp"
@ -171,6 +172,16 @@ void Source::ClangViewParse::parse(size_t count) {
for(auto &token : *clang_tokens)
clang_tokens_offsets.emplace_back(token.get_source_range().get_offsets());
clang_diagnostics = clang_tu->get_diagnostics();
try {
auto analyze_results_file = Project::Build::create(this->file_path)->get_default_path() / "clang-analyze.sarif.json";
analyze_diagnostics = SARIF::results_for_file(analyze_results_file, file_path);
}
catch(const std::exception &err) {
analyze_diagnostics.clear();
Terminal::get().async_print("\e[31mError\e[m: Parsing clang-analyze results failed: " + std::string{err.what()} + ".\n", true);
}
parse_mutex.unlock();
dispatcher.post([this, count] {
if(count != parse_count || parse_state != ParseState::processing)
@ -525,6 +536,86 @@ void Source::ClangViewParse::update_diagnostics() {
}
}
std::map<std::pair<Gtk::TextIter, Gtk::TextIter>, std::set<std::string>> added_messages;
const auto add_tooltip_for_location = [this, &added_messages](const SARIF::Location &location, const SARIF::Result &result) {
auto text = result.get_message(location, true /* markdown */);
int line = location.region.start_line - 1;
if(line < 0 || line >= get_buffer()->get_line_count())
line = get_buffer()->get_line_count() - 1;
auto start = get_iter_at_line_end(line);
int offset = location.region.start_column - 1;
if(offset >= 0 && offset < start.get_line_index())
start = get_buffer()->get_iter_at_line_offset(line, offset);
if(start.ends_line()) {
while(!start.is_start() && start.ends_line())
start.backward_char();
}
line = location.region.end_line - 1;
if(line < 0 || line >= get_buffer()->get_line_count())
line = get_buffer()->get_line_count() - 1;
auto end = get_iter_at_line_end(line);
offset = location.region.end_column - 1;
if(offset >= 0 && offset < end.get_line_index())
end = get_buffer()->get_iter_at_line_offset(line, offset);
// Check whether the source actually still matches the scan result
auto match = get_buffer()->get_text(start, end);
if(!location.region.snippet.empty() && match != location.region.snippet) {
bool context_found = false;
if(location.context_region && !location.context_region->snippet.empty()) {
// Try to find the context in case the matching source was only moved
Gtk::TextIter match_start, match_end;
if(start.forward_search(location.context_region->snippet, static_cast<Gtk::TextSearchFlags>(0), match_start, match_end))
context_found = true;
else if(end.backward_search(location.context_region->snippet, static_cast<Gtk::TextSearchFlags>(0), match_start, match_end))
context_found = true;
else if(get_buffer()->begin().forward_search(location.context_region->snippet, static_cast<Gtk::TextSearchFlags>(0), match_start, match_end))
context_found = true;
if(context_found) {
match_start.forward_search(location.region.snippet, static_cast<Gtk::TextSearchFlags>(0), start, end, match_end);
}
}
text.append(" (Source modified since scan, match ").append(context_found ? "may be" : "is").append(" inaccurate)");
}
auto added_it = added_messages.find(std::make_pair(start, end));
if(added_it != added_messages.end() && added_it->second.find(text) != added_it->second.end())
return false;
diagnostic_offsets.emplace(start.get_offset());
added_messages[std::make_pair(start, end)].emplace(text);
add_diagnostic_tooltip(start, end, result.level == SARIF::Result::Level::error, [text](Tooltip &tooltip) {
tooltip.insert_markdown(text);
});
return true;
};
for(const auto &diagnostic : analyze_diagnostics) {
for(const auto &location : diagnostic.locations) {
if(location.artifact_location == file_path) {
if(add_tooltip_for_location(location, diagnostic)) {
num_warnings += diagnostic.level == SARIF::Result::Level::warning;
num_errors += diagnostic.level == SARIF::Result::Level::error;
}
}
}
for(const auto &code_flow : diagnostic.code_flows) {
for(const auto &thread_flow : code_flow.thread_flows) {
for(const auto &entry : thread_flow.locations) {
if(entry.location.artifact_location == file_path && entry.importance != SARIF::ThreadFlowLocation::Importance::unimportant) {
if(add_tooltip_for_location(entry.location, diagnostic)) {
num_warnings += diagnostic.level == SARIF::Result::Level::warning;
num_errors += diagnostic.level == SARIF::Result::Level::error;
}
}
}
}
}
}
status_diagnostics = std::make_tuple(num_warnings, num_errors, num_fix_its);
if(update_status_diagnostics)
update_status_diagnostics(this);

5
src/source_clang.hpp

@ -11,6 +11,10 @@
#include <set>
#include <thread>
namespace SARIF {
struct Result;
} // namespace SARIF
namespace Source {
class ClangViewParse : public View {
protected:
@ -57,6 +61,7 @@ namespace Source {
void update_diagnostics() REQUIRES(parse_mutex);
std::vector<clangmm::Diagnostic> clang_diagnostics GUARDED_BY(parse_mutex);
std::vector<SARIF::Result> analyze_diagnostics GUARDED_BY(parse_mutex);
/// Removes for instance ::__1:: and ::__cxx11:: from type
void remove_internal_namespaces(std::string &type);

Loading…
Cancel
Save