Skip to content

Commit fda9d19

Browse files
committed
Swift: replace undefined labels with UnspecifiedElement
1 parent 8d3e6ff commit fda9d19

File tree

2 files changed

+94
-19
lines changed

2 files changed

+94
-19
lines changed

swift/extractor/infra/SwiftDispatcher.h

Lines changed: 58 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -61,16 +61,26 @@ class SwiftDispatcher {
6161

6262
template <typename Entry>
6363
void emit(Entry&& entry) {
64-
entry.forEachLabel([&entry](const char* field, int index, auto& label) {
64+
bool valid = true;
65+
entry.forEachLabel([&valid, &entry, this](const char* field, int index, auto& label) {
66+
using Label = std::remove_reference_t<decltype(label)>;
6567
if (!label.valid()) {
6668
std::cerr << entry.NAME << " has undefined " << field;
6769
if (index >= 0) {
6870
std::cerr << '[' << index << ']';
6971
}
70-
std::cerr << '\n';
72+
if constexpr (std::is_base_of_v<typename Label::Tag, UnspecifiedElementTag>) {
73+
std::cerr << ", replacing with unspecified element\n";
74+
label = emitUnspecified(idOf(entry), field, index);
75+
} else {
76+
std::cerr << ", skipping emission\n";
77+
valid = false;
78+
}
7179
}
7280
});
73-
trap.emit(entry);
81+
if (valid) {
82+
trap.emit(entry);
83+
}
7484
}
7585

7686
template <typename Entry>
@@ -97,13 +107,39 @@ class SwiftDispatcher {
97107
emit(ElementIsUnknownTrap{label});
98108
}
99109

110+
TrapLabel<UnspecifiedElementTag> emitUnspecified(std::optional<TrapLabel<ElementTag>>&& parent,
111+
const char* property,
112+
int index) {
113+
UnspecifiedElement entry{trap.createLabel<UnspecifiedElementTag>()};
114+
entry.error = "element was unspecified by the extractor";
115+
entry.parent = std::move(parent);
116+
entry.property = property;
117+
if (index >= 0) {
118+
entry.index = index;
119+
}
120+
trap.emit(entry);
121+
return entry.id;
122+
}
123+
124+
template <typename E>
125+
std::optional<TrapLabel<ElementTag>> idOf(const E& entry) {
126+
if constexpr (HasId<E>::value) {
127+
return entry.id;
128+
} else {
129+
return std::nullopt;
130+
}
131+
}
132+
100133
// This method gives a TRAP label for already emitted AST node.
101134
// If the AST node was not emitted yet, then the emission is dispatched to a corresponding
102135
// visitor (see `visit(T *)` methods below).
103136
template <typename E, typename... Args, std::enable_if_t<IsStorable<E>>* = nullptr>
104137
TrapLabelOf<E> fetchLabel(const E& e, Args&&... args) {
105138
if constexpr (std::is_constructible_v<bool, const E&>) {
106-
assert(e && "fetching a label on a null entity, maybe fetchOptionalLabel is to be used?");
139+
if (!e) {
140+
// this will be treated on emission
141+
return undefined_label;
142+
}
107143
}
108144
// this is required so we avoid any recursive loop: a `fetchLabel` during the visit of `e` might
109145
// end up calling `fetchLabel` on `e` itself, so we want the visit of `e` to call `fetchLabel`
@@ -214,17 +250,18 @@ class SwiftDispatcher {
214250
return std::nullopt;
215251
}
216252

217-
// map `fetchLabel` on the iterable `arg`, returning a vector of all labels
253+
// map `fetchLabel` on the iterable `arg`
218254
// universal reference `Arg&&` is used to catch both temporary and non-const references, not
219255
// for perfect forwarding
220256
template <typename Iterable>
221257
auto fetchRepeatedLabels(Iterable&& arg) {
222-
std::vector<decltype(fetchLabel(*arg.begin()))> ret;
258+
using Label = decltype(fetchLabel(*arg.begin()));
259+
TrapLabelVectorWrapper<typename Label::Tag> ret;
223260
if constexpr (HasSize<Iterable>::value) {
224-
ret.reserve(arg.size());
261+
ret.data.reserve(arg.size());
225262
}
226263
for (auto&& e : arg) {
227-
ret.push_back(fetchLabel(e));
264+
ret.data.push_back(fetchLabel(e));
228265
}
229266
return ret;
230267
}
@@ -279,6 +316,12 @@ class SwiftDispatcher {
279316
template <typename T>
280317
struct HasSize<T, decltype(std::declval<T>().size(), void())> : std::true_type {};
281318

319+
template <typename T, typename = void>
320+
struct HasId : std::false_type {};
321+
322+
template <typename T>
323+
struct HasId<T, decltype(std::declval<T>().id, void())> : std::true_type {};
324+
282325
void attachLocation(swift::SourceLoc start,
283326
swift::SourceLoc end,
284327
TrapLabel<LocatableTag> locatableLabel) {
@@ -302,19 +345,20 @@ class SwiftDispatcher {
302345
TrapLabel<Tag> fetchLabelFromUnion(const llvm::PointerUnion<Ts...> u) {
303346
TrapLabel<Tag> ret{};
304347
// with logical op short-circuiting, this will stop trying on the first successful fetch
305-
// don't feel tempted to replace the variable with the expression inside the `assert`, or
306-
// building with `NDEBUG` will not trigger the fetching
307348
bool unionCaseFound = (... || fetchLabelFromUnionCase<Tag, Ts>(u, ret));
308-
assert(unionCaseFound && "llvm::PointerUnion not set to a known case");
349+
if (!unionCaseFound) {
350+
// TODO emit error/warning here
351+
return undefined_label;
352+
}
309353
return ret;
310354
}
311355

312356
template <typename Tag, typename T, typename... Ts>
313357
bool fetchLabelFromUnionCase(const llvm::PointerUnion<Ts...> u, TrapLabel<Tag>& output) {
314358
// we rely on the fact that when we extract `ASTNode` instances (which only happens
315-
// on `BraceStmt` elements), we cannot encounter a standalone `TypeRepr` there, so we skip
316-
// this case; extracting `TypeRepr`s here would be problematic as we would not be able to
317-
// provide the corresponding type
359+
// on `BraceStmt`/`IfConfigDecl` elements), we cannot encounter a standalone `TypeRepr` there,
360+
// so we skip this case; extracting `TypeRepr`s here would be problematic as we would not be
361+
// able to provide the corresponding type
318362
if constexpr (!std::is_same_v<T, swift::TypeRepr*>) {
319363
if (auto e = u.template dyn_cast<T>()) {
320364
output = fetchLabel(e);

swift/extractor/trap/TrapLabel.h

Lines changed: 36 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,14 @@
44
#include <iomanip>
55
#include <iostream>
66
#include <string>
7+
#include <vector>
78

89
namespace codeql {
910

11+
struct UndefinedTrapLabel {};
12+
13+
constexpr UndefinedTrapLabel undefined_label{};
14+
1015
class UntypedTrapLabel {
1116
uint64_t id_;
1217

@@ -18,14 +23,17 @@ class UntypedTrapLabel {
1823

1924
protected:
2025
UntypedTrapLabel() : id_{undefined} {}
21-
UntypedTrapLabel(uint64_t id) : id_{id} {}
26+
UntypedTrapLabel(uint64_t id) : id_{id} { assert(id != undefined); }
2227

2328
public:
29+
bool valid() const { return id_ != undefined; }
30+
explicit operator bool() const { return valid(); }
31+
2432
friend std::ostream& operator<<(std::ostream& out, UntypedTrapLabel l) {
2533
// TODO: this is a temporary fix to catch us from outputting undefined labels to trap
2634
// this should be moved to a validity check, probably aided by code generation and carried out
2735
// by `SwiftDispatcher`
28-
assert(l.id_ != undefined && "outputting an undefined label!");
36+
assert(l && "outputting an undefined label!");
2937
out << '#' << std::hex << l.id_ << std::dec;
3038
return out;
3139
}
@@ -44,14 +52,37 @@ class TrapLabel : public UntypedTrapLabel {
4452
using Tag = TagParam;
4553

4654
TrapLabel() = default;
55+
TrapLabel(UndefinedTrapLabel) : TrapLabel() {}
56+
57+
TrapLabel& operator=(UndefinedTrapLabel) {
58+
*this = TrapLabel{};
59+
return *this;
60+
}
4761

4862
// The caller is responsible for ensuring ID uniqueness.
4963
static TrapLabel unsafeCreateFromExplicitId(uint64_t id) { return {id}; }
5064
static TrapLabel unsafeCreateFromUntyped(UntypedTrapLabel label) { return {label.id_}; }
5165

52-
template <typename OtherTag>
53-
TrapLabel(const TrapLabel<OtherTag>& other) : UntypedTrapLabel(other) {
54-
static_assert(std::is_base_of_v<Tag, OtherTag>, "wrong label assignment!");
66+
template <typename SourceTag>
67+
TrapLabel(const TrapLabel<SourceTag>& other) : UntypedTrapLabel(other) {
68+
static_assert(std::is_base_of_v<Tag, SourceTag>, "wrong label assignment!");
69+
}
70+
};
71+
72+
// wrapper class to allow directly assigning a vector of TrapLabel<A> to a vector of
73+
// TrapLabel<B> if B is a base of A, using move semantics rather than copying
74+
template <typename TagParam>
75+
struct TrapLabelVectorWrapper {
76+
using Tag = TagParam;
77+
78+
std::vector<TrapLabel<TagParam>> data;
79+
80+
template <typename DestinationTag>
81+
operator std::vector<TrapLabel<DestinationTag>>() && {
82+
static_assert(std::is_base_of_v<DestinationTag, Tag>, "wrong label assignment!");
83+
// reinterpret_cast is safe because TrapLabel instances differ only on the type, not the
84+
// underlying data
85+
return std::move(reinterpret_cast<std::vector<TrapLabel<DestinationTag>>&>(data));
5586
}
5687
};
5788

0 commit comments

Comments
 (0)