Skip to content

Commit 1574e85

Browse files
committed
Swift: consume and store compiler frontend diagnostics
1 parent 62c26f8 commit 1574e85

File tree

5 files changed

+163
-4
lines changed

5 files changed

+163
-4
lines changed

swift/extractor/BUILD.bazel

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,11 @@ swift_cc_binary(
1010
visibility = ["//swift:__pkg__"],
1111
deps = [
1212
"//swift/extractor/infra",
13+
"//swift/extractor/invocation",
1314
"//swift/extractor/remapping",
1415
"//swift/extractor/translators",
1516
"//swift/third_party/swift-llvm-support",
17+
"@picosha2",
1618
],
1719
)
1820

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
load("//swift:rules.bzl", "swift_cc_library")
2+
3+
swift_cc_library(
4+
name = "invocation",
5+
srcs = glob(["*.cpp"]),
6+
hdrs = glob(["*.h"]),
7+
visibility = ["//swift:__subpackages__"],
8+
deps = [
9+
"//swift/third_party/swift-llvm-support",
10+
],
11+
)
Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
#include "swift/extractor/invocation/CodeQLDiagnosticsConsumer.h"
2+
#include "swift/extractor/trap/TrapDomain.h"
3+
#include "swift/extractor/trap/generated/TrapEntries.h"
4+
5+
#include <swift/AST/DiagnosticEngine.h>
6+
#include <swift/Basic/SourceManager.h>
7+
#include <llvm/ADT/SmallString.h>
8+
#include <llvm/Support/raw_ostream.h>
9+
#include <string>
10+
11+
using namespace codeql;
12+
13+
static int diagnosticsKind(const swift::DiagnosticInfo& diagInfo) {
14+
switch (diagInfo.Kind) {
15+
case swift::DiagnosticKind::Error:
16+
return 1;
17+
case swift::DiagnosticKind::Warning:
18+
return 2;
19+
20+
case swift::DiagnosticKind::Note:
21+
return 3;
22+
23+
case swift::DiagnosticKind::Remark:
24+
return 4;
25+
}
26+
return 0;
27+
}
28+
29+
static std::filesystem::path getFilePath(std::string_view path) {
30+
// TODO: this needs more testing
31+
// TODO: check canonicalization of names on a case insensitive filesystems
32+
// TODO: make symlink resolution conditional on CODEQL_PRESERVE_SYMLINKS=true
33+
std::error_code ec;
34+
auto ret = std::filesystem::canonical(path, ec);
35+
if (ec) {
36+
std::cerr << "Cannot get real path: " << std::quoted(path) << ": " << ec.message() << "\n";
37+
return {};
38+
}
39+
return ret;
40+
}
41+
42+
static void attachLocation(TrapDomain& trap,
43+
swift::SourceManager& sourceManager,
44+
const swift::DiagnosticInfo& diagInfo,
45+
DiagnosticsTrap& locatable) {
46+
auto loc = diagInfo.Loc;
47+
if (!loc.isValid()) {
48+
return;
49+
}
50+
auto filepath = getFilePath(sourceManager.getDisplayNameForLoc(loc));
51+
FilesTrap file;
52+
file.id = trap.createLabel<FileTag>();
53+
file.name = filepath;
54+
trap.emit(file);
55+
56+
LocationsTrap location;
57+
location.id = trap.createLabel<LocationTag>();
58+
location.file = file.id;
59+
std::tie(location.start_line, location.start_column) =
60+
sourceManager.getLineAndColumnInBuffer(loc);
61+
std::tie(location.end_line, location.end_column) = sourceManager.getLineAndColumnInBuffer(loc);
62+
trap.emit(location);
63+
trap.emit(LocatableLocationsTrap{locatable.id, location.id});
64+
}
65+
66+
void CodeQLDiagnosticsConsumer::handleDiagnostic(swift::SourceManager& sourceManager,
67+
const swift::DiagnosticInfo& diagInfo) {
68+
auto message = getDiagMessage(sourceManager, diagInfo);
69+
DiagnosticsTrap diag{};
70+
diag.id = trap.createLabel<DiagnosticsTag>();
71+
diag.kind = diagnosticsKind(diagInfo);
72+
diag.text = message;
73+
trap.emit(diag);
74+
attachLocation(trap, sourceManager, diagInfo, diag);
75+
}
76+
77+
std::string CodeQLDiagnosticsConsumer::getDiagMessage(swift::SourceManager& sourceManager,
78+
const swift::DiagnosticInfo& diagInfo) {
79+
// Translate ranges.
80+
llvm::SmallVector<llvm::SMRange, 2> ranges;
81+
for (auto R : diagInfo.Ranges)
82+
ranges.push_back(getRawRange(sourceManager, R));
83+
84+
// Translate fix-its.
85+
llvm::SmallVector<llvm::SMFixIt, 2> fixIts;
86+
for (const swift::DiagnosticInfo::FixIt& F : diagInfo.FixIts)
87+
fixIts.push_back(getRawFixIt(sourceManager, F));
88+
89+
// Actually substitute the diagnostic arguments into the diagnostic text.
90+
llvm::SmallString<256> Text;
91+
{
92+
llvm::raw_svector_ostream Out(Text);
93+
swift::DiagnosticEngine::formatDiagnosticText(Out, diagInfo.FormatString, diagInfo.FormatArgs);
94+
}
95+
96+
return Text.str().str();
97+
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
#pragma once
2+
3+
#include <swift/AST/DiagnosticConsumer.h>
4+
5+
namespace codeql {
6+
7+
class TrapDomain;
8+
9+
class CodeQLDiagnosticsConsumer : public swift::DiagnosticConsumer {
10+
public:
11+
explicit CodeQLDiagnosticsConsumer(TrapDomain& targetFile) : trap(targetFile) {}
12+
void handleDiagnostic(swift::SourceManager& sourceManager,
13+
const swift::DiagnosticInfo& diagInfo) override;
14+
15+
private:
16+
static std::string getDiagMessage(swift::SourceManager& sourceManager,
17+
const swift::DiagnosticInfo& diagInfo);
18+
TrapDomain& trap;
19+
};
20+
21+
} // namespace codeql

swift/extractor/main.cpp

Lines changed: 32 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,11 @@
11
#include <fstream>
22
#include <iomanip>
33
#include <stdlib.h>
4-
#include <unordered_set>
54
#include <vector>
6-
#include <string>
75
#include <iostream>
86
#include <regex>
97
#include <unistd.h>
8+
#include <picosha2.h>
109

1110
#include <swift/Basic/LLVMInitialize.h>
1211
#include <swift/FrontendTool/FrontendTool.h>
@@ -15,6 +14,8 @@
1514
#include "swift/extractor/TargetTrapFile.h"
1615
#include "swift/extractor/remapping/SwiftOutputRewrite.h"
1716
#include "swift/extractor/remapping/SwiftOpenInterception.h"
17+
#include "swift/extractor/invocation/CodeQLDiagnosticsConsumer.h"
18+
#include "swift/extractor/trap/TrapDomain.h"
1819

1920
using namespace std::string_literals;
2021

@@ -23,14 +24,21 @@ using namespace std::string_literals;
2324
// semantic analysis
2425
class Observer : public swift::FrontendObserver {
2526
public:
26-
explicit Observer(const codeql::SwiftExtractorConfiguration& config) : config{config} {}
27+
explicit Observer(const codeql::SwiftExtractorConfiguration& config,
28+
codeql::CodeQLDiagnosticsConsumer& diagConsumer)
29+
: config{config}, diagConsumer{diagConsumer} {}
30+
31+
void configuredCompiler(swift::CompilerInstance& instance) override {
32+
instance.addDiagnosticConsumer(&diagConsumer);
33+
}
2734

2835
void performedSemanticAnalysis(swift::CompilerInstance& compiler) override {
2936
codeql::extractSwiftFiles(config, compiler);
3037
}
3138

3239
private:
3340
const codeql::SwiftExtractorConfiguration& config;
41+
codeql::CodeQLDiagnosticsConsumer& diagConsumer;
3442
};
3543

3644
static std::string getenv_or(const char* envvar, const std::string& def) {
@@ -96,6 +104,23 @@ static void checkWhetherToRunUnderTool(int argc, char* const* argv) {
96104
execvp(args[0], args.data());
97105
}
98106

107+
// Creates a target file that should store per-invocation info, e.g. compilation args,
108+
// compilations, diagnostics, etc.
109+
codeql::TargetFile invocationTargetFile(codeql::SwiftExtractorConfiguration& configuration) {
110+
auto hasher = picosha2::hash256_one_by_one();
111+
for (auto& option : configuration.frontendOptions) {
112+
hasher.process(std::begin(option), std::end(option));
113+
}
114+
hasher.finish();
115+
auto target = "invocation-"s + get_hash_hex_string(hasher);
116+
auto maybeFile = codeql::createTargetTrapFile(configuration, target);
117+
if (!maybeFile) {
118+
std::cerr << "Cannot create invocation trap file: " << target << "\n";
119+
abort();
120+
}
121+
return std::move(maybeFile.value());
122+
}
123+
99124
int main(int argc, char** argv) {
100125
checkWhetherToRunUnderTool(argc, argv);
101126

@@ -131,7 +156,10 @@ int main(int argc, char** argv) {
131156
args.push_back(arg.c_str());
132157
}
133158

134-
Observer observer(configuration);
159+
auto invocationTrapFile = invocationTargetFile(configuration);
160+
codeql::TrapDomain invocationDomain(invocationTrapFile);
161+
codeql::CodeQLDiagnosticsConsumer diagConsumer(invocationDomain);
162+
Observer observer(configuration, diagConsumer);
135163
int frontend_rc = swift::performFrontend(args, "swift-extractor", (void*)main, &observer);
136164

137165
codeql::finalizeRemapping(remapping);

0 commit comments

Comments
 (0)