21#include <clang/Frontend/CompilerInstance.h>
22#include <clang/Frontend/CompilerInvocation.h>
23#include <clang/Tooling/CompilationDatabase.h>
30void inject_resource_dir(
31 CommandLineArguments &args,
const char *argv_0,
void *main_addr)
33 using namespace std::string_literals;
35 if (std::any_of(std::begin(args), std::end(args), [](
const auto &arg) {
40 args = clang::tooling::getInsertArgumentAdjuster((
"-resource-dir=" +
41 clang::CompilerInvocation::GetResourcesPath(argv_0, main_addr))
52 std::string filepath = d.
location->file_relative().empty()
64 j[
"description"] = logging::escape_json(a.
description);
66 j[
"location"][
"file"] = a.
location.value().file();
67 j[
"location"][
"line"] = a.
location.value().line();
68 j[
"location"][
"column"] = a.
location.value().column();
73 std::string dn, std::vector<diagnostic> d, std::string description)
74 : error::diagram_generation_error{dt, dn, description}
75 , diagnostics{
std::move(d)}
80 : relative_to_{
std::move(relative_to)}
85 DiagnosticsEngine::Level diag_level,
const Diagnostic &info)
87 SmallVector<char> buf{};
88 info.FormatDiagnostic(buf);
92 d.
description = std::string{buf.data(), buf.size()};
94 if (info.hasSourceManager() && info.getLocation().isValid()) {
100 if (diag_level == clang::DiagnosticsEngine::Level::Error ||
101 diag_level == clang::DiagnosticsEngine::Level::Fatal) {
109 std::string diagram_name,
111 const std::vector<std::string> &source_paths,
112 std::filesystem::path relative_to,
bool quiet)
113 : diagram_type_{diagram_type}
114 , diagram_name_{
std::move(diagram_name)}
115 , compilations_{compilation_database}
116 , source_paths_{source_paths}
118 , pch_container_ops_{
std::make_shared<PCHContainerOperations>()}
119 , overlay_fs_{new llvm::vfs::OverlayFileSystem(
120 llvm::vfs::getRealFileSystem())}
121 , inmemory_fs_{new llvm::vfs::InMemoryFileSystem}
122 , files_{new FileManager(FileSystemOptions(), overlay_fs_)}
124 , diag_opts_{new
clang::DiagnosticOptions}
143 static int static_symbol;
145 std::vector<std::string> absolute_tu_paths;
148 auto absolute_tu_path =
149 clang::tooling::getAbsolutePath(*
overlay_fs_, source_path);
150 if (!absolute_tu_path) {
152 LOG_WARN(
"Skipping file {} in diagram {}. Could not resolve "
153 "absolute path for translation unit: {}",
155 llvm::toString(absolute_tu_path.takeError()));
159 absolute_tu_paths.emplace_back(std::move(*absolute_tu_path));
163 std::string initial_workdir;
164 if (
auto current_workdir =
overlay_fs_->getCurrentWorkingDirectory()) {
165 initial_workdir = std::move(*current_workdir);
169 LOG_ERROR(
"Could not get current working directory when generating "
174 for (
const auto &file : absolute_tu_paths) {
176 LOG_INFO(
"Processing diagram '{}' translation unit: {}",
181 if (compile_commands_for_file.empty()) {
184 "Skipping file {} for diagram '{}'. Compilation command "
190 if (compile_commands_for_file.size() > 1 &&
192 LOG_WARN(
"Multiple compile commands detected for file '{}' in "
193 "diagram '{}' - using only the first one...",
197 for (
auto &compile_command : compile_commands_for_file) {
199 compile_command.Directory))
200 llvm::report_fatal_error(
"Cannot chdir into \"" +
201 Twine(compile_command.Directory) +
"\"!");
208 for (
const auto &[file_name, file_content] :
210 if (!llvm::sys::path::is_absolute(file_name))
212 llvm::MemoryBuffer::getMemBuffer(file_content));
215 auto command_line = compile_command.CommandLine;
220 assert(!command_line.empty());
222 inject_resource_dir(command_line,
"clang_tool", &static_symbol);
224 ToolInvocation invocation(std::move(command_line), Action,
227#if LLVM_VERSION_MAJOR > 13
228 invocation.setDiagnosticOptions(
diag_opts_.get());
232 if (!initial_workdir.empty()) {
233 if (
const auto ec =
overlay_fs_->setCurrentWorkingDirectory(
237 LOG_ERROR(
"Error when trying to restore working "
253 throw std::runtime_error(
254 fmt::format(
"Unknown error while processing {}", file));
262 if (!initial_workdir.empty()) {
264 overlay_fs_->setCurrentWorkingDirectory(initial_workdir))
266 LOG_ERROR(
"Error when trying to restore working dir: {}",
273std::string to_string(clang::DiagnosticsEngine::Level level)
275 std::string level_str;
277 case clang::DiagnosticsEngine::Ignored:
278 level_str =
"IGNORED";
280 case clang::DiagnosticsEngine::Note:
283 case clang::DiagnosticsEngine::Remark:
284 level_str =
"REMARK";
286 case clang::DiagnosticsEngine::Warning:
287 level_str =
"WARNING";
289 case clang::DiagnosticsEngine::Error:
292 case clang::DiagnosticsEngine::Fatal:
296 level_str =
"UNKNOWN";