Skip to content

Commit 8756989

Browse files
authored
Merge pull request github#11101 from github/alexdenisov/extractor-errors
Swift: extract diagnostics
2 parents c95a6ea + 3f2f328 commit 8756989

24 files changed

+305
-31
lines changed

swift/extractor/BUILD.bazel

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ 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",

swift/extractor/infra/SwiftDispatcher.h

Lines changed: 7 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
#include "swift/extractor/trap/TrapDomain.h"
1111
#include "swift/extractor/infra/SwiftTagTraits.h"
1212
#include "swift/extractor/trap/generated/TrapClasses.h"
13-
#include "swift/extractor/infra/file/PathHash.h"
13+
#include "swift/extractor/infra/SwiftLocationExtractor.h"
1414

1515
namespace codeql {
1616

@@ -29,8 +29,7 @@ class SwiftDispatcher {
2929
const swift::Expr*,
3030
const swift::Pattern*,
3131
const swift::TypeRepr*,
32-
const swift::TypeBase*,
33-
std::filesystem::path>;
32+
const swift::TypeBase*>;
3433

3534
template <typename E>
3635
static constexpr bool IsStorable = std::is_constructible_v<Store::Handle, const E&>;
@@ -48,10 +47,11 @@ class SwiftDispatcher {
4847
: sourceManager{sourceManager},
4948
trap{trap},
5049
currentModule{currentModule},
51-
currentPrimarySourceFile{currentPrimarySourceFile} {
50+
currentPrimarySourceFile{currentPrimarySourceFile},
51+
locationExtractor(trap) {
5252
if (currentPrimarySourceFile) {
5353
// we make sure the file is in the trap output even if the source is empty
54-
fetchLabel(getFilePath(currentPrimarySourceFile->getFilename()));
54+
locationExtractor.emitFile(currentPrimarySourceFile->getFilename());
5555
}
5656
}
5757

@@ -325,20 +325,7 @@ class SwiftDispatcher {
325325
void attachLocation(swift::SourceLoc start,
326326
swift::SourceLoc end,
327327
TrapLabel<LocatableTag> locatableLabel) {
328-
if (!start.isValid() || !end.isValid()) {
329-
// invalid locations seem to come from entities synthesized by the compiler
330-
return;
331-
}
332-
auto file = getFilePath(sourceManager.getDisplayNameForLoc(start));
333-
DbLocation entry{{}};
334-
entry.file = fetchLabel(file);
335-
std::tie(entry.start_line, entry.start_column) = sourceManager.getLineAndColumnInBuffer(start);
336-
std::tie(entry.end_line, entry.end_column) = sourceManager.getLineAndColumnInBuffer(end);
337-
entry.id = trap.createLabel<DbLocationTag>('{', entry.file, "}:", entry.start_line, ':',
338-
entry.start_column, ':', entry.end_line, ':',
339-
entry.end_column);
340-
emit(entry);
341-
emit(LocatableLocationsTrap{locatableLabel, entry.id});
328+
locationExtractor.attachLocation(sourceManager, start, end, locatableLabel);
342329
}
343330

344331
template <typename Tag, typename... Ts>
@@ -391,19 +378,14 @@ class SwiftDispatcher {
391378
virtual void visit(const swift::TypeRepr* typeRepr, swift::Type type) = 0;
392379
virtual void visit(const swift::TypeBase* type) = 0;
393380

394-
void visit(const std::filesystem::path& file) {
395-
auto entry = createEntry(file, file.string());
396-
entry.name = file.string();
397-
emit(entry);
398-
}
399-
400381
const swift::SourceManager& sourceManager;
401382
TrapDomain& trap;
402383
Store store;
403384
Store::Handle waitingForNewLabel{std::monostate{}};
404385
swift::ModuleDecl& currentModule;
405386
swift::SourceFile* currentPrimarySourceFile;
406387
std::unordered_set<swift::ModuleDecl*> encounteredModules;
388+
SwiftLocationExtractor locationExtractor;
407389
};
408390

409391
} // namespace codeql
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
#include <swift/AST/SourceFile.h>
2+
#include <swift/Basic/SourceManager.h>
3+
4+
#include "swift/extractor/trap/TrapDomain.h"
5+
#include "swift/extractor/trap/generated/TrapEntries.h"
6+
#include "swift/extractor/trap/generated/TrapClasses.h"
7+
#include "swift/extractor/infra/SwiftLocationExtractor.h"
8+
9+
using namespace codeql;
10+
11+
static std::filesystem::path getFilePath(std::string_view path) {
12+
// TODO: this needs more testing
13+
// TODO: check canonicalization of names on a case insensitive filesystems
14+
// TODO: make symlink resolution conditional on CODEQL_PRESERVE_SYMLINKS=true
15+
std::error_code ec;
16+
auto ret = std::filesystem::canonical(path, ec);
17+
if (ec) {
18+
std::cerr << "Cannot get real path: " << std::quoted(path) << ": " << ec.message() << "\n";
19+
return {};
20+
}
21+
return ret;
22+
}
23+
24+
void SwiftLocationExtractor::attachLocation(const swift::SourceManager& sourceManager,
25+
swift::SourceLoc start,
26+
swift::SourceLoc end,
27+
TrapLabel<LocatableTag> locatableLabel) {
28+
if (!start.isValid() || !end.isValid()) {
29+
// invalid locations seem to come from entities synthesized by the compiler
30+
return;
31+
}
32+
auto file = getFilePath(sourceManager.getDisplayNameForLoc(start));
33+
DbLocation entry{{}};
34+
entry.file = fetchFileLabel(file);
35+
std::tie(entry.start_line, entry.start_column) = sourceManager.getLineAndColumnInBuffer(start);
36+
std::tie(entry.end_line, entry.end_column) = sourceManager.getLineAndColumnInBuffer(end);
37+
entry.id = trap.createLabel<DbLocationTag>('{', entry.file, "}:", entry.start_line, ':',
38+
entry.start_column, ':', entry.end_line, ':',
39+
entry.end_column);
40+
trap.emit(entry);
41+
trap.emit(LocatableLocationsTrap{locatableLabel, entry.id});
42+
}
43+
44+
void SwiftLocationExtractor::emitFile(llvm::StringRef path) {
45+
fetchFileLabel(getFilePath(path));
46+
}
47+
48+
TrapLabel<FileTag> SwiftLocationExtractor::fetchFileLabel(const std::filesystem::path& file) {
49+
if (store.count(file)) {
50+
return store[file];
51+
}
52+
53+
DbFile entry({});
54+
entry.id = trap.createLabel<DbFileTag>(file.string());
55+
entry.name = file.string();
56+
trap.emit(entry);
57+
store[file] = entry.id;
58+
return entry.id;
59+
}
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
#pragma once
2+
3+
#include <swift/Basic/SourceManager.h>
4+
#include <unordered_map>
5+
#include <filesystem>
6+
7+
#include "swift/extractor/trap/generated/TrapEntries.h"
8+
#include "swift/extractor/infra/file/PathHash.h"
9+
10+
namespace codeql {
11+
12+
class TrapDomain;
13+
14+
class SwiftLocationExtractor {
15+
public:
16+
explicit SwiftLocationExtractor(TrapDomain& trap) : trap(trap) {}
17+
18+
void attachLocation(const swift::SourceManager& sourceManager,
19+
swift::SourceLoc start,
20+
swift::SourceLoc end,
21+
TrapLabel<LocatableTag> locatableLabel);
22+
23+
void emitFile(llvm::StringRef path);
24+
25+
private:
26+
TrapLabel<FileTag> fetchFileLabel(const std::filesystem::path& file);
27+
TrapDomain& trap;
28+
std::unordered_map<std::filesystem::path, TrapLabel<FileTag>> store;
29+
};
30+
31+
} // namespace codeql
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/extractor/infra",
10+
],
11+
)
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
#include "swift/extractor/invocation/SwiftDiagnosticsConsumer.h"
2+
#include "swift/extractor/trap/generated/TrapEntries.h"
3+
#include "swift/extractor/trap/TrapDomain.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+
case swift::DiagnosticKind::Note:
20+
return 3;
21+
case swift::DiagnosticKind::Remark:
22+
return 4;
23+
}
24+
return 0;
25+
}
26+
27+
void SwiftDiagnosticsConsumer::handleDiagnostic(swift::SourceManager& sourceManager,
28+
const swift::DiagnosticInfo& diagInfo) {
29+
auto message = getDiagMessage(sourceManager, diagInfo);
30+
DiagnosticsTrap diag{};
31+
diag.id = trap.createLabel<DiagnosticsTag>();
32+
diag.kind = diagnosticsKind(diagInfo);
33+
diag.text = message;
34+
trap.emit(diag);
35+
locationExtractor.attachLocation(sourceManager, diagInfo.Loc, diagInfo.Loc, diag.id);
36+
}
37+
38+
std::string SwiftDiagnosticsConsumer::getDiagMessage(swift::SourceManager& sourceManager,
39+
const swift::DiagnosticInfo& diagInfo) {
40+
llvm::SmallString<256> text;
41+
llvm::raw_svector_ostream out(text);
42+
swift::DiagnosticEngine::formatDiagnosticText(out, diagInfo.FormatString, diagInfo.FormatArgs);
43+
return text.str().str();
44+
}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
#pragma once
2+
3+
#include <swift/AST/DiagnosticConsumer.h>
4+
#include "swift/extractor/infra/SwiftLocationExtractor.h"
5+
6+
namespace codeql {
7+
8+
class TrapDomain;
9+
10+
class SwiftDiagnosticsConsumer : public swift::DiagnosticConsumer {
11+
public:
12+
explicit SwiftDiagnosticsConsumer(TrapDomain& targetFile)
13+
: trap(targetFile), locationExtractor(targetFile) {}
14+
void handleDiagnostic(swift::SourceManager& sourceManager,
15+
const swift::DiagnosticInfo& diagInfo) override;
16+
17+
private:
18+
static std::string getDiagMessage(swift::SourceManager& sourceManager,
19+
const swift::DiagnosticInfo& diagInfo);
20+
TrapDomain& trap;
21+
SwiftLocationExtractor locationExtractor;
22+
};
23+
24+
} // namespace codeql

swift/extractor/main.cpp

Lines changed: 29 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 <chrono>
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/SwiftDiagnosticsConsumer.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::SwiftDiagnosticsConsumer& 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::SwiftDiagnosticsConsumer& diagConsumer;
3442
};
3543

3644
static std::string getenv_or(const char* envvar, const std::string& def) {
@@ -96,6 +104,20 @@ 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 timestamp = std::chrono::system_clock::now().time_since_epoch().count();
111+
auto filename = std::to_string(timestamp) + '-' + std::to_string(getpid());
112+
auto target = std::filesystem::path("invocations") / std::filesystem::path(filename);
113+
auto maybeFile = codeql::createTargetTrapFile(configuration, target);
114+
if (!maybeFile) {
115+
std::cerr << "Cannot create invocation trap file: " << target << "\n";
116+
abort();
117+
}
118+
return std::move(maybeFile.value());
119+
}
120+
99121
int main(int argc, char** argv) {
100122
checkWhetherToRunUnderTool(argc, argv);
101123

@@ -131,7 +153,10 @@ int main(int argc, char** argv) {
131153
args.push_back(arg.c_str());
132154
}
133155

134-
Observer observer(configuration);
156+
auto invocationTrapFile = invocationTargetFile(configuration);
157+
codeql::TrapDomain invocationDomain(invocationTrapFile);
158+
codeql::SwiftDiagnosticsConsumer diagConsumer(invocationDomain);
159+
Observer observer(configuration, diagConsumer);
135160
int frontend_rc = swift::performFrontend(args, "swift-extractor", (void*)main, &observer);
136161

137162
codeql::finalizeRemapping(remapping);

swift/ql/lib/codeql/swift/elements.qll

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import codeql.swift.elements.Callable
44
import codeql.swift.elements.Comment
55
import codeql.swift.elements.DbFile
66
import codeql.swift.elements.DbLocation
7+
import codeql.swift.elements.Diagnostics
78
import codeql.swift.elements.Element
89
import codeql.swift.elements.File
910
import codeql.swift.elements.Locatable
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
private import codeql.swift.generated.Diagnostics
2+
3+
class Diagnostics extends Generated::Diagnostics {
4+
override string toString() { result = getText() }
5+
}

0 commit comments

Comments
 (0)