Skip to content

Commit 5e74df3

Browse files
committed
Swift: cache file paths
This required a bit of a generalization of `TrapLabelStore` to not work only with pointers.
1 parent 7fbe4f8 commit 5e74df3

File tree

7 files changed

+105
-68
lines changed

7 files changed

+105
-68
lines changed

swift/extractor/SwiftExtractor.cpp

Lines changed: 6 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -104,29 +104,19 @@ static void extractDeclarations(const SwiftExtractorConfiguration& config,
104104
dumpArgs(*trapTarget, config);
105105
TrapDomain trap{*trapTarget};
106106

107-
// TODO: move default location emission elsewhere, possibly in a separate global trap file
107+
// TODO: remove this and recreate it with IPA when we have that
108108
// the following cannot conflict with actual files as those have an absolute path starting with /
109-
auto unknownFileLabel = trap.createLabel<FileTag>("unknown");
110-
auto unknownLocationLabel = trap.createLabel<LocationTag>("unknown");
111-
trap.emit(FilesTrap{unknownFileLabel});
112-
trap.emit(LocationsTrap{unknownLocationLabel, unknownFileLabel});
109+
File unknownFileEntry{trap.createLabel<FileTag>("unknown")};
110+
Location unknownLocationEntry{trap.createLabel<LocationTag>("unknown")};
111+
unknownLocationEntry.file = unknownFileEntry.id;
112+
trap.emit(unknownFileEntry);
113+
trap.emit(unknownLocationEntry);
113114

114115
SwiftVisitor visitor(compiler.getSourceMgr(), trap, module, primaryFile);
115116
auto topLevelDecls = getTopLevelDecls(module, primaryFile);
116117
for (auto decl : topLevelDecls) {
117118
visitor.extract(decl);
118119
}
119-
// TODO the following will be moved to the dispatcher when we start caching swift file objects
120-
// for the moment, topLevelDecls always contains the current module, which does not have a file
121-
// associated with it, so we need a special case when there are no top level declarations
122-
if (topLevelDecls.size() == 1) {
123-
// In the case of empty files, the dispatcher is not called, but we still want to 'record' the
124-
// fact that the file was extracted
125-
llvm::SmallString<PATH_MAX> name(filename);
126-
llvm::sys::fs::make_absolute(name);
127-
auto fileLabel = trap.createLabel<FileTag>(name.str().str());
128-
trap.emit(FilesTrap{fileLabel, name.str().str()});
129-
}
130120
}
131121

132122
static std::unordered_set<std::string> collectInputFilenames(swift::CompilerInstance& compiler) {

swift/extractor/infra/FilePath.h

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
#pragma once
2+
3+
#include <string>
4+
namespace codeql {
5+
6+
// wrapper around `std::string` mainly intended to unambiguously go into an `std::variant`
7+
// TODO probably not needed once we can use `std::filesystem::path`
8+
struct FilePath {
9+
FilePath() = default;
10+
FilePath(const std::string& path) : path{path} {}
11+
FilePath(std::string&& path) : path{std::move(path)} {}
12+
13+
std::string path;
14+
15+
bool operator==(const FilePath& other) const { return path == other.path; }
16+
};
17+
} // namespace codeql
18+
19+
namespace std {
20+
template <>
21+
struct hash<codeql::FilePath> {
22+
size_t operator()(const codeql::FilePath& value) { return hash<string>{}(value.path); }
23+
};
24+
} // namespace std

swift/extractor/infra/SwiftDispatcher.h

Lines changed: 59 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
#include "swift/extractor/trap/TrapDomain.h"
99
#include "swift/extractor/infra/SwiftTagTraits.h"
1010
#include "swift/extractor/trap/generated/TrapClasses.h"
11+
#include "swift/extractor/infra/FilePath.h"
1112

1213
namespace codeql {
1314

@@ -17,6 +18,24 @@ namespace codeql {
1718
// Since SwiftDispatcher sees all the AST nodes, it also attaches a location to every 'locatable'
1819
// node (AST nodes that are not types: declarations, statements, expressions, etc.).
1920
class SwiftDispatcher {
21+
// types to be supported by assignNewLabel/fetchLabel need to be listed here
22+
using Store = TrapLabelStore<const swift::Decl*,
23+
const swift::Stmt*,
24+
const swift::StmtCondition*,
25+
const swift::StmtConditionElement*,
26+
const swift::CaseLabelItem*,
27+
const swift::Expr*,
28+
const swift::Pattern*,
29+
const swift::TypeRepr*,
30+
const swift::TypeBase*,
31+
FilePath>;
32+
33+
template <typename E>
34+
static constexpr bool IsStorable = std::is_constructible_v<Store::Handle, const E&>;
35+
36+
template <typename E>
37+
static constexpr bool IsLocatable = std::is_base_of_v<LocatableTag, TrapTagOf<E>>;
38+
2039
public:
2140
// all references and pointers passed as parameters to this constructor are supposed to outlive
2241
// the SwiftDispatcher
@@ -27,7 +46,12 @@ class SwiftDispatcher {
2746
: sourceManager{sourceManager},
2847
trap{trap},
2948
currentModule{currentModule},
30-
currentPrimarySourceFile{currentPrimarySourceFile} {}
49+
currentPrimarySourceFile{currentPrimarySourceFile} {
50+
if (currentPrimarySourceFile) {
51+
// we make sure the file is in the trap output even if the source is empty
52+
fetchLabel(getFilePath(currentPrimarySourceFile->getFilename()));
53+
}
54+
}
3155

3256
template <typename Entry>
3357
void emit(const Entry& entry) {
@@ -61,9 +85,11 @@ class SwiftDispatcher {
6185
// This method gives a TRAP label for already emitted AST node.
6286
// If the AST node was not emitted yet, then the emission is dispatched to a corresponding
6387
// visitor (see `visit(T *)` methods below).
64-
template <typename E>
65-
TrapLabelOf<E> fetchLabel(E* e) {
66-
assert(e && "trying to fetch a label on nullptr, maybe fetchOptionalLabel is to be used?");
88+
template <typename E, std::enable_if_t<IsStorable<E>>* = nullptr>
89+
TrapLabelOf<E> fetchLabel(const E& e) {
90+
if constexpr (std::is_constructible_v<bool, const E&>) {
91+
assert(e && "fetching a label on a null entity, maybe fetchOptionalLabel is to be used?");
92+
}
6793
// this is required so we avoid any recursive loop: a `fetchLabel` during the visit of `e` might
6894
// end up calling `fetchLabel` on `e` itself, so we want the visit of `e` to call `fetchLabel`
6995
// only after having called `assignNewLabel` on `e`.
@@ -76,7 +102,7 @@ class SwiftDispatcher {
76102
visit(e);
77103
// TODO when everything is moved to structured C++ classes, this should be moved to createEntry
78104
if (auto l = store.get(e)) {
79-
if constexpr (!std::is_base_of_v<swift::TypeBase, E>) {
105+
if constexpr (IsLocatable<E>) {
80106
attachLocation(e, *l);
81107
}
82108
return *l;
@@ -93,36 +119,37 @@ class SwiftDispatcher {
93119
return fetchLabelFromUnion<AstNodeTag>(node);
94120
}
95121

96-
TrapLabel<ConditionElementTag> fetchLabel(const swift::StmtConditionElement& element) {
97-
return fetchLabel(&element);
122+
template <typename E, std::enable_if_t<IsStorable<E*>>* = nullptr>
123+
TrapLabelOf<E> fetchLabel(const E& e) {
124+
return fetchLabel(&e);
98125
}
99126

100127
// Due to the lazy emission approach, we must assign a label to a corresponding AST node before
101128
// it actually gets emitted to handle recursive cases such as recursive calls, or recursive type
102129
// declarations
103-
template <typename E, typename... Args>
104-
TrapLabelOf<E> assignNewLabel(E* e, Args&&... args) {
130+
template <typename E, typename... Args, std::enable_if_t<IsStorable<E>>* = nullptr>
131+
TrapLabelOf<E> assignNewLabel(const E& e, Args&&... args) {
105132
assert(waitingForNewLabel == Store::Handle{e} && "assignNewLabel called on wrong entity");
106133
auto label = trap.createLabel<TrapTagOf<E>>(std::forward<Args>(args)...);
107134
store.insert(e, label);
108135
waitingForNewLabel = std::monostate{};
109136
return label;
110137
}
111138

112-
template <typename E, typename... Args, std::enable_if_t<!std::is_pointer_v<E>>* = nullptr>
139+
template <typename E, typename... Args, std::enable_if_t<IsStorable<E*>>* = nullptr>
113140
TrapLabelOf<E> assignNewLabel(const E& e, Args&&... args) {
114141
return assignNewLabel(&e, std::forward<Args>(args)...);
115142
}
116143

117144
// convenience methods for structured C++ creation
118-
template <typename E, typename... Args, std::enable_if_t<!std::is_pointer_v<E>>* = nullptr>
145+
template <typename E, typename... Args>
119146
auto createEntry(const E& e, Args&&... args) {
120-
return TrapClassOf<E>{assignNewLabel(&e, std::forward<Args>(args)...)};
147+
return TrapClassOf<E>{assignNewLabel(e, std::forward<Args>(args)...)};
121148
}
122149

123150
// used to create a new entry for entities that should not be cached
124151
// an example is swift::Argument, that are created on the fly and thus have no stable pointer
125-
template <typename E, typename... Args, std::enable_if_t<!std::is_pointer_v<E>>* = nullptr>
152+
template <typename E, typename... Args>
126153
auto createUncachedEntry(const E& e, Args&&... args) {
127154
auto label = trap.createLabel<TrapTagOf<E>>(std::forward<Args>(args)...);
128155
attachLocation(&e, label);
@@ -206,34 +233,23 @@ class SwiftDispatcher {
206233
}
207234

208235
private:
209-
// types to be supported by assignNewLabel/fetchLabel need to be listed here
210-
using Store = TrapLabelStore<swift::Decl,
211-
swift::Stmt,
212-
swift::StmtCondition,
213-
swift::StmtConditionElement,
214-
swift::CaseLabelItem,
215-
swift::Expr,
216-
swift::Pattern,
217-
swift::TypeRepr,
218-
swift::TypeBase>;
219-
220236
void attachLocation(swift::SourceLoc start,
221237
swift::SourceLoc end,
222238
TrapLabel<LocatableTag> locatableLabel) {
223239
if (!start.isValid() || !end.isValid()) {
224240
// invalid locations seem to come from entities synthesized by the compiler
225241
return;
226242
}
227-
std::string filepath = getFilepath(start);
228-
auto fileLabel = trap.createLabel<FileTag>(filepath);
229-
// TODO: do not emit duplicate trap entries for Files
230-
trap.emit(FilesTrap{fileLabel, filepath});
231-
auto [startLine, startColumn] = sourceManager.getLineAndColumnInBuffer(start);
232-
auto [endLine, endColumn] = sourceManager.getLineAndColumnInBuffer(end);
233-
auto locLabel = trap.createLabel<LocationTag>('{', fileLabel, "}:", startLine, ':', startColumn,
234-
':', endLine, ':', endColumn);
235-
trap.emit(LocationsTrap{locLabel, fileLabel, startLine, startColumn, endLine, endColumn});
236-
trap.emit(LocatableLocationsTrap{locatableLabel, locLabel});
243+
auto file = getFilePath(sourceManager.getDisplayNameForLoc(start));
244+
Location entry{{}};
245+
entry.file = fetchLabel(file);
246+
std::tie(entry.start_line, entry.start_column) = sourceManager.getLineAndColumnInBuffer(start);
247+
std::tie(entry.end_line, entry.end_column) = sourceManager.getLineAndColumnInBuffer(end);
248+
entry.id = trap.createLabel<LocationTag>('{', entry.file, "}:", entry.start_line, ':',
249+
entry.start_column, ':', entry.end_line, ':',
250+
entry.end_column);
251+
emit(entry);
252+
emit(LocatableLocationsTrap{locatableLabel, entry.id});
237253
}
238254

239255
template <typename Tag, typename... Ts>
@@ -256,21 +272,18 @@ class SwiftDispatcher {
256272
return false;
257273
}
258274

259-
std::string getFilepath(swift::SourceLoc loc) {
275+
static FilePath getFilePath(llvm::StringRef path) {
260276
// TODO: this needs more testing
261277
// TODO: check canonicaliztion of names on a case insensitive filesystems
262278
// TODO: make symlink resolution conditional on CODEQL_PRESERVE_SYMLINKS=true
263-
auto displayName = sourceManager.getDisplayNameForLoc(loc);
264279
llvm::SmallString<PATH_MAX> realPath;
265-
if (std::error_code ec = llvm::sys::fs::real_path(displayName, realPath)) {
266-
std::cerr << "Cannot get real path: '" << displayName.str() << "': " << ec.message() << "\n";
280+
if (std::error_code ec = llvm::sys::fs::real_path(path, realPath)) {
281+
std::cerr << "Cannot get real path: '" << path.str() << "': " << ec.message() << "\n";
267282
return {};
268283
}
269284
return realPath.str().str();
270285
}
271286

272-
// TODO: The following methods are supposed to redirect TRAP emission to correpsonding visitors,
273-
// which are to be introduced in follow-up PRs
274287
virtual void visit(swift::Decl* decl) = 0;
275288
virtual void visit(swift::Stmt* stmt) = 0;
276289
virtual void visit(const swift::StmtCondition* cond) = 0;
@@ -281,6 +294,12 @@ class SwiftDispatcher {
281294
virtual void visit(swift::TypeRepr* type) = 0;
282295
virtual void visit(swift::TypeBase* type) = 0;
283296

297+
void visit(const FilePath& file) {
298+
auto entry = createEntry(file);
299+
entry.name = file.path;
300+
emit(entry);
301+
}
302+
284303
const swift::SourceManager& sourceManager;
285304
TrapDomain& trap;
286305
Store store;

swift/extractor/infra/SwiftTagTraits.h

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
#include <swift/AST/ASTVisitor.h>
66
#include "swift/extractor/trap/TrapTagTraits.h"
77
#include "swift/extractor/trap/generated/TrapTags.h"
8+
#include "swift/extractor/infra/FilePath.h"
89

910
namespace codeql {
1011

@@ -16,12 +17,12 @@ using SILFunctionTypeTag = SilFunctionTypeTag;
1617
using SILTokenTypeTag = SilTokenTypeTag;
1718
using SILBoxTypeReprTag = SilBoxTypeReprTag;
1819

19-
#define MAP_TYPE_TO_TAG(TYPE, TAG) \
20-
template <> \
21-
struct detail::ToTagFunctor<swift::TYPE> { \
22-
using type = TAG; \
20+
#define MAP_TYPE_TO_TAG(TYPE, TAG) \
21+
template <> \
22+
struct detail::ToTagFunctor<TYPE> { \
23+
using type = TAG; \
2324
}
24-
#define MAP_TAG(TYPE) MAP_TYPE_TO_TAG(TYPE, TYPE##Tag)
25+
#define MAP_TAG(TYPE) MAP_TYPE_TO_TAG(swift::TYPE, TYPE##Tag)
2526
#define MAP_SUBTAG(TYPE, PARENT) \
2627
MAP_TAG(TYPE); \
2728
static_assert(std::is_base_of_v<PARENT##Tag, TYPE##Tag>, \
@@ -36,7 +37,7 @@ using SILBoxTypeReprTag = SilBoxTypeReprTag;
3637

3738
MAP_TAG(Stmt);
3839
MAP_TAG(StmtCondition);
39-
MAP_TYPE_TO_TAG(StmtConditionElement, ConditionElementTag);
40+
MAP_TYPE_TO_TAG(swift::StmtConditionElement, ConditionElementTag);
4041
MAP_TAG(CaseLabelItem);
4142
#define ABSTRACT_STMT(CLASS, PARENT) MAP_SUBTAG(CLASS##Stmt, PARENT)
4243
#define STMT(CLASS, PARENT) ABSTRACT_STMT(CLASS, PARENT)
@@ -63,14 +64,16 @@ MAP_TAG(TypeRepr);
6364
#define TYPEREPR(CLASS, PARENT) ABSTRACT_TYPEREPR(CLASS, PARENT)
6465
#include <swift/AST/TypeReprNodes.def>
6566

66-
MAP_TYPE_TO_TAG(TypeBase, TypeTag);
67+
MAP_TYPE_TO_TAG(swift::TypeBase, TypeTag);
6768
#define ABSTRACT_TYPE(CLASS, PARENT) MAP_SUBTAG(CLASS##Type, PARENT)
6869
#define TYPE(CLASS, PARENT) ABSTRACT_TYPE(CLASS, PARENT)
6970
#include <swift/AST/TypeNodes.def>
7071

7172
OVERRIDE_TAG(FuncDecl, ConcreteFuncDeclTag);
7273
OVERRIDE_TAG(VarDecl, ConcreteVarDeclTag);
7374

75+
MAP_TYPE_TO_TAG(FilePath, FileTag);
76+
7477
#undef MAP_TAG
7578
#undef MAP_SUBTAG
7679
#undef MAP_TYPE_TO_TAG

swift/extractor/trap/TrapLabelStore.h

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,18 +20,18 @@ namespace codeql {
2020
template <typename... Ts>
2121
class TrapLabelStore {
2222
public:
23-
using Handle = std::variant<std::monostate, const Ts*...>;
23+
using Handle = std::variant<std::monostate, Ts...>;
2424

2525
template <typename T>
26-
std::optional<TrapLabelOf<T>> get(const T* e) {
26+
std::optional<TrapLabelOf<T>> get(const T& e) {
2727
if (auto found = store_.find(e); found != store_.end()) {
2828
return TrapLabelOf<T>::unsafeCreateFromUntyped(found->second);
2929
}
3030
return std::nullopt;
3131
}
3232

3333
template <typename T>
34-
void insert(const T* e, TrapLabelOf<T> l) {
34+
void insert(const T& e, TrapLabelOf<T> l) {
3535
auto [_, inserted] = store_.emplace(e, l);
3636
assert(inserted && "already inserted");
3737
}

swift/extractor/trap/TrapTagTraits.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,8 @@ struct ToTrapClassFunctor;
2626
} // namespace detail
2727

2828
template <typename T>
29-
using TrapTagOf = typename detail::ToTagOverride<std::remove_const_t<T>>::type;
29+
using TrapTagOf =
30+
typename detail::ToTagOverride<std::remove_const_t<std::remove_pointer_t<T>>>::type;
3031

3132
template <typename T>
3233
using TrapLabelOf = TrapLabel<TrapTagOf<T>>;

swift/extractor/visitors/SwiftVisitor.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ class SwiftVisitor : private SwiftDispatcher {
1515
using SwiftDispatcher::SwiftDispatcher;
1616

1717
template <typename T>
18-
void extract(T* entity) {
18+
void extract(const T& entity) {
1919
fetchLabel(entity);
2020
}
2121

0 commit comments

Comments
 (0)