21#include <clang/Frontend/CompilerInstance.h>
22#include <clang/Frontend/CompilerInvocation.h>
23#include <clang/Tooling/CompilationDatabase.h>
24#if LLVM_VERSION_MAJOR >= 22
25#include <clang/Options/OptionUtils.h>
33void inject_resource_dir(
34 CommandLineArguments &args,
const char *argv_0,
void *main_addr)
36 using namespace std::string_literals;
38 if (std::any_of(std::begin(args), std::end(args), [](
const auto &arg) {
43 args = clang::tooling::getInsertArgumentAdjuster((
"-resource-dir=" +
44#
if LLVM_VERSION_MAJOR < 22
45 clang::CompilerInvocation::GetResourcesPath(argv_0, main_addr)
47 clang::GetResourcesPath(argv_0, main_addr)
60 std::string filepath = d.
location->file_relative().empty()
72 j[
"description"] = logging::escape_json(a.
description);
74 j[
"location"][
"file"] = a.
location.value().file();
75 j[
"location"][
"line"] = a.
location.value().line();
76 j[
"location"][
"column"] = a.
location.value().column();
81 std::string dn, std::vector<diagnostic> d, std::string description)
82 : error::diagram_generation_error{dt, dn, description}
83 , diagnostics{
std::move(d)}
88 : relative_to_{
std::move(relative_to)}
93 DiagnosticsEngine::Level diag_level,
const Diagnostic &info)
95 SmallVector<char> buf{};
96 info.FormatDiagnostic(buf);
100 d.
description = std::string{buf.data(), buf.size()};
102 if (info.hasSourceManager() && info.getLocation().isValid()) {
108 if (diag_level == clang::DiagnosticsEngine::Level::Error ||
109 diag_level == clang::DiagnosticsEngine::Level::Fatal) {
117 std::string diagram_name,
119 const std::vector<std::string> &source_paths,
120 std::filesystem::path relative_to,
bool quiet)
121 : diagram_type_{diagram_type}
122 , diagram_name_{
std::move(diagram_name)}
123 , compilations_{compilation_database}
124 , source_paths_{source_paths}
126 , pch_container_ops_{
std::make_shared<PCHContainerOperations>()}
127 , overlay_fs_{new llvm::vfs::OverlayFileSystem(
128 llvm::vfs::getRealFileSystem())}
129 , inmemory_fs_{new llvm::vfs::InMemoryFileSystem}
130 , files_{new FileManager(FileSystemOptions(), overlay_fs_)}
132 , diag_opts_{new
clang::DiagnosticOptions}
151 static int static_symbol;
153 std::vector<std::string> absolute_tu_paths;
156 auto absolute_tu_path =
157 clang::tooling::getAbsolutePath(*
overlay_fs_, source_path);
158 if (!absolute_tu_path) {
160 LOG_WARN(
"Skipping file {} in diagram {}. Could not resolve "
161 "absolute path for translation unit: {}",
163 llvm::toString(absolute_tu_path.takeError()));
167 absolute_tu_paths.emplace_back(std::move(*absolute_tu_path));
171 std::string initial_workdir;
172 if (
auto current_workdir =
overlay_fs_->getCurrentWorkingDirectory()) {
173 initial_workdir = std::move(*current_workdir);
177 LOG_ERROR(
"Could not get current working directory when generating "
182 for (
const auto &file : absolute_tu_paths) {
184 LOG_INFO(
"Processing diagram '{}' translation unit: {}",
189 if (compile_commands_for_file.empty()) {
192 "Skipping file {} for diagram '{}'. Compilation command "
198 if (compile_commands_for_file.size() > 1 &&
200 LOG_WARN(
"Multiple compile commands detected for file '{}' in "
201 "diagram '{}' - using only the first one...",
205 for (
auto &compile_command : compile_commands_for_file) {
207 compile_command.Directory))
208 llvm::report_fatal_error(
"Cannot chdir into \"" +
209 Twine(compile_command.Directory) +
"\"!");
216 for (
const auto &[file_name, file_content] :
218 if (!llvm::sys::path::is_absolute(file_name))
220 llvm::MemoryBuffer::getMemBuffer(file_content));
223 auto command_line = compile_command.CommandLine;
228 assert(!command_line.empty());
230 inject_resource_dir(command_line,
"clang_tool", &static_symbol);
232 ToolInvocation invocation(std::move(command_line), Action,
235#if LLVM_VERSION_MAJOR > 13
236 invocation.setDiagnosticOptions(
diag_opts_.get());
240 if (!initial_workdir.empty()) {
241 if (
const auto ec =
overlay_fs_->setCurrentWorkingDirectory(
245 LOG_ERROR(
"Error when trying to restore working "
261 throw std::runtime_error(
262 fmt::format(
"Unknown error while processing {}", file));
270 if (!initial_workdir.empty()) {
272 overlay_fs_->setCurrentWorkingDirectory(initial_workdir))
274 LOG_ERROR(
"Error when trying to restore working dir: {}",
281std::string to_string(clang::DiagnosticsEngine::Level level)
283 std::string level_str;
285 case clang::DiagnosticsEngine::Ignored:
286 level_str =
"IGNORED";
288 case clang::DiagnosticsEngine::Note:
291 case clang::DiagnosticsEngine::Remark:
292 level_str =
"REMARK";
294 case clang::DiagnosticsEngine::Warning:
295 level_str =
"WARNING";
297 case clang::DiagnosticsEngine::Error:
300 case clang::DiagnosticsEngine::Fatal:
304 level_str =
"UNKNOWN";