Skip to content

Commit 3a97517

Browse files
committed
Swift: extract ImportDecl and ModuleDecl
As `ASTMangler` crashes when called on `ModuleDecl`, we simply use its name. This might probably not work reliably in a scenario where multiple modules are compiled with the same name (like `main`), but this is left for future work. At the moment this cannot create DB inconsistencies.
1 parent 7a7440a commit 3a97517

22 files changed

+180
-24
lines changed

swift/codegen/schema.yml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -273,6 +273,9 @@ IfConfigDecl:
273273

274274
ImportDecl:
275275
_extends: Decl
276+
is_exported: predicate
277+
module: ModuleDecl
278+
declarations: ValueDecl*
276279

277280
MissingMemberDecl:
278281
_extends: Decl
@@ -1067,6 +1070,8 @@ GenericTypeDecl:
10671070

10681071
ModuleDecl:
10691072
_extends: TypeDecl
1073+
is_builtin_module: predicate
1074+
is_system_module: predicate
10701075

10711076
ConstructorRefCallExpr:
10721077
_extends: SelfApplyExpr

swift/extractor/SwiftExtractor.cpp

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -64,8 +64,19 @@ static std::string getTrapFilename(swift::ModuleDecl& module, swift::SourceFile*
6464
return filename;
6565
}
6666

67+
static llvm::SmallVector<swift::Decl*> getTopLevelDecls(swift::ModuleDecl& module,
68+
swift::SourceFile* primaryFile = nullptr) {
69+
llvm::SmallVector<swift::Decl*> ret;
70+
ret.push_back(&module);
71+
if (primaryFile) {
72+
primaryFile->getTopLevelDecls(ret);
73+
} else {
74+
module.getTopLevelDecls(ret);
75+
}
76+
return ret;
77+
}
78+
6779
static void extractDeclarations(const SwiftExtractorConfiguration& config,
68-
llvm::ArrayRef<swift::Decl*> topLevelDecls,
6980
swift::CompilerInstance& compiler,
7081
swift::ModuleDecl& module,
7182
swift::SourceFile* primaryFile = nullptr) {
@@ -119,6 +130,7 @@ static void extractDeclarations(const SwiftExtractorConfiguration& config,
119130
trap.emit(LocationsTrap{unknownLocationLabel, unknownFileLabel});
120131

121132
SwiftVisitor visitor(compiler.getSourceMgr(), arena, trap, module, primaryFile);
133+
auto topLevelDecls = getTopLevelDecls(module, primaryFile);
122134
for (auto decl : topLevelDecls) {
123135
visitor.extract(decl);
124136
}
@@ -203,18 +215,15 @@ void codeql::extractSwiftFiles(const SwiftExtractorConfiguration& config,
203215
// user code twice: once during the module build in a form of a source file, and then as
204216
// a pre-built module during building of the dependent source files.
205217
if (module->isSystemModule() || module->isBuiltinModule()) {
206-
llvm::SmallVector<swift::Decl*> decls;
207-
module->getTopLevelDecls(decls);
208-
// TODO: pass ModuleDecl directly when we have module extraction in place?
209-
extractDeclarations(config, decls, compiler, *module);
218+
extractDeclarations(config, compiler, *module);
210219
} else {
211220
for (auto file : module->getFiles()) {
212221
auto sourceFile = llvm::dyn_cast<swift::SourceFile>(file);
213222
if (!sourceFile || inputFiles.count(sourceFile->getFilename().str()) == 0) {
214223
continue;
215224
}
216225
archiveFile(config, *sourceFile);
217-
extractDeclarations(config, sourceFile->getTopLevelDecls(), compiler, *module, sourceFile);
226+
extractDeclarations(config, compiler, *module, sourceFile);
218227
}
219228
}
220229
}

swift/extractor/infra/SwiftDispatcher.h

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -179,6 +179,11 @@ class SwiftDispatcher {
179179
return ret;
180180
}
181181

182+
template <typename... Args>
183+
void emitDebugInfo(const Args&... args) {
184+
trap.debug(std::forward<Args>(args)...);
185+
}
186+
182187
// In order to not emit duplicated entries for declarations, we restrict emission to only
183188
// Decls declared within the current "scope".
184189
// Depending on the whether we are extracting a primary source file or not the scope is defined as
@@ -192,7 +197,9 @@ class SwiftDispatcher {
192197
if (decl.getModuleContext() != &currentModule) {
193198
return false;
194199
}
195-
if (!currentPrimarySourceFile) {
200+
// ModuleDecl is a special case: if it passed the previous test, it is the current module
201+
// but it never has a source file, so we short circuit to emit it in any case
202+
if (!currentPrimarySourceFile || decl.getKind() == swift::DeclKind::Module) {
196203
return true;
197204
}
198205
if (auto context = decl.getDeclContext()) {

swift/extractor/trap/TrapOutput.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,8 +41,8 @@ class TrapOutput {
4141

4242
template <typename... Args>
4343
void debug(const Args&... args) {
44-
out_ << "// DEBUG: ";
45-
(out_ << ... << args) << '\n';
44+
out_ << "/* DEBUG:\n";
45+
(out_ << ... << args) << "\n*/\n";
4646
}
4747

4848
private:

swift/extractor/visitors/DeclVisitor.cpp

Lines changed: 30 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -241,16 +241,15 @@ std::variant<codeql::AccessorDecl, codeql::AccessorDeclsTrap> DeclVisitor::trans
241241

242242
std::optional<codeql::SubscriptDecl> DeclVisitor::translateSubscriptDecl(
243243
const swift::SubscriptDecl& decl) {
244-
auto id = dispatcher_.assignNewLabel(decl, mangledName(decl));
245-
if (!dispatcher_.shouldEmitDeclBody(decl)) {
244+
auto entry = createNamedEntry(decl);
245+
if (!entry) {
246246
return std::nullopt;
247247
}
248-
SubscriptDecl entry{id};
249-
entry.element_type = dispatcher_.fetchLabel(decl.getElementInterfaceType());
248+
entry->element_type = dispatcher_.fetchLabel(decl.getElementInterfaceType());
250249
if (auto indices = decl.getIndices()) {
251-
entry.params = dispatcher_.fetchRepeatedLabels(*indices);
250+
entry->params = dispatcher_.fetchRepeatedLabels(*indices);
252251
}
253-
fillAbstractStorageDecl(decl, entry);
252+
fillAbstractStorageDecl(decl, *entry);
254253
return entry;
255254
}
256255

@@ -262,7 +261,32 @@ codeql::ExtensionDecl DeclVisitor::translateExtensionDecl(const swift::Extension
262261
return entry;
263262
}
264263

264+
codeql::ImportDecl DeclVisitor::translateImportDecl(const swift::ImportDecl& decl) {
265+
auto entry = dispatcher_.createEntry(decl);
266+
entry.is_exported = decl.isExported();
267+
entry.module = dispatcher_.fetchLabel(decl.getModule());
268+
entry.declarations = dispatcher_.fetchRepeatedLabels(decl.getDecls());
269+
return entry;
270+
}
271+
272+
std::optional<codeql::ModuleDecl> DeclVisitor::translateModuleDecl(const swift::ModuleDecl& decl) {
273+
auto entry = createNamedEntry(decl);
274+
if (!entry) {
275+
return std::nullopt;
276+
}
277+
entry->is_builtin_module = decl.isBuiltinModule();
278+
entry->is_system_module = decl.isSystemModule();
279+
fillTypeDecl(decl, *entry);
280+
return entry;
281+
}
282+
265283
std::string DeclVisitor::mangledName(const swift::ValueDecl& decl) {
284+
// ASTMangler::mangleAnyDecl crashes when called on `ModuleDecl`
285+
// TODO find a more unique string working also when different modules are compiled with the same
286+
// name
287+
if (decl.getKind() == swift::DeclKind::Module) {
288+
return static_cast<const swift::ModuleDecl&>(decl).getRealName().str().str();
289+
}
266290
// prefix adds a couple of special symbols, we don't necessary need them
267291
return mangler.mangleAnyDecl(&decl, /* prefix = */ false);
268292
}

swift/extractor/visitors/DeclVisitor.h

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,8 @@ class DeclVisitor : public AstVisitorBase<DeclVisitor> {
5050
const swift::AccessorDecl& decl);
5151
std::optional<codeql::SubscriptDecl> translateSubscriptDecl(const swift::SubscriptDecl& decl);
5252
codeql::ExtensionDecl translateExtensionDecl(const swift::ExtensionDecl& decl);
53+
codeql::ImportDecl translateImportDecl(const swift::ImportDecl& decl);
54+
std::optional<codeql::ModuleDecl> translateModuleDecl(const swift::ModuleDecl& decl);
5355

5456
private:
5557
std::string mangledName(const swift::ValueDecl& decl);
@@ -66,6 +68,15 @@ class DeclVisitor : public AstVisitorBase<DeclVisitor> {
6668
void fillAbstractStorageDecl(const swift::AbstractStorageDecl& decl,
6769
codeql::AbstractStorageDecl& entry);
6870

71+
template <typename D>
72+
std::optional<TrapClassOf<D>> createNamedEntry(const D& decl) {
73+
auto id = dispatcher_.assignNewLabel(decl, mangledName(decl));
74+
if (dispatcher_.shouldEmitDeclBody(decl)) {
75+
return TrapClassOf<D>{id};
76+
}
77+
return std::nullopt;
78+
}
79+
6980
private:
7081
swift::Mangle::ASTMangler mangler;
7182
};
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,28 @@
11
// generated by codegen/codegen.py
22
import codeql.swift.elements.decl.Decl
3+
import codeql.swift.elements.decl.ModuleDecl
4+
import codeql.swift.elements.decl.ValueDecl
35

46
class ImportDeclBase extends @import_decl, Decl {
57
override string getAPrimaryQlClass() { result = "ImportDecl" }
8+
9+
predicate isExported() { import_decl_is_exported(this) }
10+
11+
ModuleDecl getModule() {
12+
exists(ModuleDecl x |
13+
import_decls(this, x) and
14+
result = x.resolve()
15+
)
16+
}
17+
18+
ValueDecl getDeclaration(int index) {
19+
exists(ValueDecl x |
20+
import_decl_declarations(this, index, x) and
21+
result = x.resolve()
22+
)
23+
}
24+
25+
ValueDecl getADeclaration() { result = getDeclaration(_) }
26+
27+
int getNumberOfDeclarations() { result = count(getADeclaration()) }
628
}

swift/ql/lib/codeql/swift/generated/decl/ModuleDecl.qll

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,4 +3,8 @@ import codeql.swift.elements.decl.TypeDecl
33

44
class ModuleDeclBase extends @module_decl, TypeDecl {
55
override string getAPrimaryQlClass() { result = "ModuleDecl" }
6+
7+
predicate isBuiltinModule() { module_decl_is_builtin_module(this) }
8+
9+
predicate isSystemModule() { module_decl_is_system_module(this) }
610
}

swift/ql/lib/swift.dbscheme

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -644,7 +644,20 @@ if_config_decls( //dir=decl
644644
);
645645

646646
import_decls( //dir=decl
647-
unique int id: @import_decl
647+
unique int id: @import_decl,
648+
int module: @module_decl ref
649+
);
650+
651+
#keyset[id]
652+
import_decl_is_exported( //dir=decl
653+
int id: @import_decl ref
654+
);
655+
656+
#keyset[id, index]
657+
import_decl_declarations( //dir=decl
658+
int id: @import_decl ref,
659+
int index: int ref,
660+
int declaration: @value_decl ref
648661
);
649662

650663
missing_member_decls( //dir=decl
@@ -2018,6 +2031,16 @@ module_decls( //dir=decl
20182031
unique int id: @module_decl
20192032
);
20202033

2034+
#keyset[id]
2035+
module_decl_is_builtin_module( //dir=decl
2036+
int id: @module_decl ref
2037+
);
2038+
2039+
#keyset[id]
2040+
module_decl_is_system_module( //dir=decl
2041+
int id: @module_decl ref
2042+
);
2043+
20212044
constructor_ref_call_exprs( //dir=expr
20222045
unique int id: @constructor_ref_call_expr
20232046
);

swift/ql/test/TestUtils.qll

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ cached
44
predicate toBeTested(Element e) {
55
e instanceof File
66
or
7+
exists(ModuleDecl m | m = e and not m.isBuiltinModule() and not m.isSystemModule())
8+
or
79
exists(Locatable loc |
810
loc.getLocation().getFile().getName().matches("%swift/ql/test%") and
911
(

0 commit comments

Comments
 (0)