66#include < queue>
77
88#include < swift/AST/SourceFile.h>
9- #include < swift/Basic/FileTypes.h>
10- #include < llvm/ADT/SmallString.h>
11- #include < llvm/Support/FileSystem.h>
12- #include < llvm/Support/Path.h>
9+ #include < swift/AST/Builtins.h>
1310
14- #include " swift/extractor/trap/generated/TrapClasses.h"
1511#include " swift/extractor/trap/TrapDomain.h"
1612#include " swift/extractor/visitors/SwiftVisitor.h"
1713#include " swift/extractor/TargetTrapFile.h"
14+ #include " swift/extractor/SwiftBuiltinSymbols.h"
1815
1916using namespace codeql ;
2017using namespace std ::string_literals;
@@ -65,7 +62,40 @@ static std::string getFilename(swift::ModuleDecl& module, swift::SourceFile* pri
6562 filename += module .getName ().str ();
6663 return filename;
6764 }
68- return module .getModuleFilename ().str ();
65+ if (module .isBuiltinModule ()) {
66+ // The Builtin module has an empty filename, let's fix that
67+ return " /__Builtin__" ;
68+ }
69+ auto filename = module .getModuleFilename ().str ();
70+ // there is a special case of a module without an actual filename reporting `<imports>`: in this
71+ // case we want to avoid the `<>` characters, in case a dirty DB is imported on Windows
72+ if (filename == " <imports>" ) {
73+ return " /__imports__" ;
74+ }
75+ return filename;
76+ }
77+
78+ /* The builtin module is special, as it does not publish any top-level declaration
79+ * It creates (and caches) declarations on demand when a lookup is carried out
80+ * (see BuiltinUnit in swift/AST/FileUnit.h for the cache details, and getBuiltinValueDecl in
81+ * swift/AST/Builtins.h for the creation details)
82+ * As we want to create the Builtin trap file once and for all so that it works for other
83+ * extraction runs, rather than collecting what we need we pre-populate the builtin trap with
84+ * what we expect. This list might need thus to be expanded.
85+ * Notice, that while swift/AST/Builtins.def has a list of builtin symbols, it does not contain
86+ * all information required to instantiate builtin variants.
87+ * Other possible approaches:
88+ * * create one trap per builtin declaration when encountered
89+ * * expand the list to all possible builtins (of which there are a lot)
90+ */
91+ static void getBuiltinDecls (swift::ModuleDecl& builtinModule,
92+ llvm::SmallVector<swift::Decl*>& decls) {
93+ llvm::SmallVector<swift::ValueDecl*> values;
94+ for (auto symbol : swiftBuiltins) {
95+ builtinModule.lookupValue (builtinModule.getASTContext ().getIdentifier (symbol),
96+ swift::NLKind::QualifiedLookup, values);
97+ }
98+ decls.insert (decls.end (), values.begin (), values.end ());
6999}
70100
71101static llvm::SmallVector<swift::Decl*> getTopLevelDecls (swift::ModuleDecl& module ,
@@ -74,16 +104,19 @@ static llvm::SmallVector<swift::Decl*> getTopLevelDecls(swift::ModuleDecl& modul
74104 ret.push_back (&module );
75105 if (primaryFile) {
76106 primaryFile->getTopLevelDecls (ret);
107+ } else if (module .isBuiltinModule ()) {
108+ getBuiltinDecls (module , ret);
77109 } else {
78110 module .getTopLevelDecls (ret);
79111 }
80112 return ret;
81113}
82114
83- static void extractDeclarations (const SwiftExtractorConfiguration& config,
84- swift::CompilerInstance& compiler,
85- swift::ModuleDecl& module ,
86- swift::SourceFile* primaryFile = nullptr ) {
115+ static std::unordered_set<swift::ModuleDecl*> extractDeclarations (
116+ const SwiftExtractorConfiguration& config,
117+ swift::CompilerInstance& compiler,
118+ swift::ModuleDecl& module ,
119+ swift::SourceFile* primaryFile = nullptr ) {
87120 auto filename = getFilename (module , primaryFile);
88121
89122 // The extractor can be called several times from different processes with
@@ -92,7 +125,7 @@ static void extractDeclarations(const SwiftExtractorConfiguration& config,
92125 auto trapTarget = createTargetTrapFile (config, filename);
93126 if (!trapTarget) {
94127 // another process arrived first, nothing to do for us
95- return ;
128+ return {} ;
96129 }
97130 TrapDomain trap{*trapTarget};
98131
@@ -116,6 +149,7 @@ static void extractDeclarations(const SwiftExtractorConfiguration& config,
116149 for (auto & comment : comments) {
117150 visitor.extract (comment);
118151 }
152+ return std::move (visitor).getEncounteredModules ();
119153}
120154
121155static std::unordered_set<std::string> collectInputFilenames (swift::CompilerInstance& compiler) {
@@ -132,40 +166,27 @@ static std::unordered_set<std::string> collectInputFilenames(swift::CompilerInst
132166 return sourceFiles;
133167}
134168
135- static std::unordered_set<swift::ModuleDecl*> collectModules (swift::CompilerInstance& compiler) {
136- // getASTContext().getLoadedModules() does not provide all the modules available within the
137- // program.
138- // We need to iterate over all the imported modules (recursively) to see the whole "universe."
139- std::unordered_set<swift::ModuleDecl*> allModules;
140- std::queue<swift::ModuleDecl*> worklist;
141- for (auto & [_, module ] : compiler.getASTContext ().getLoadedModules ()) {
142- worklist.push (module );
143- allModules.insert (module );
144- }
145-
146- while (!worklist.empty ()) {
147- auto module = worklist.front ();
148- worklist.pop ();
149- llvm::SmallVector<swift::ImportedModule> importedModules;
150- // TODO: we may need more than just Exported ones
151- module ->getImportedModules (importedModules, swift::ModuleDecl::ImportFilterKind::Exported);
152- for (auto & imported : importedModules) {
153- if (allModules.count (imported.importedModule ) == 0 ) {
154- worklist.push (imported.importedModule );
155- allModules.insert (imported.importedModule );
156- }
157- }
169+ static std::vector<swift::ModuleDecl*> collectLoadedModules (swift::CompilerInstance& compiler) {
170+ std::vector<swift::ModuleDecl*> ret;
171+ for (const auto & [id, module ] : compiler.getASTContext ().getLoadedModules ()) {
172+ std::ignore = id;
173+ ret.push_back (module );
158174 }
159- return allModules ;
175+ return ret ;
160176}
161177
162178void codeql::extractSwiftFiles (const SwiftExtractorConfiguration& config,
163179 swift::CompilerInstance& compiler) {
164180 auto inputFiles = collectInputFilenames (compiler);
165- auto modules = collectModules (compiler);
181+ std::vector<swift::ModuleDecl*> todo = collectLoadedModules (compiler);
182+ std::unordered_set<swift::ModuleDecl*> seen{todo.begin (), todo.end ()};
166183
167- for (auto & module : modules) {
184+ while (!todo.empty ()) {
185+ auto module = todo.back ();
186+ todo.pop_back ();
187+ llvm::errs () << " processing module " << module ->getName () << ' \n ' ;
168188 bool isFromSourceFile = false ;
189+ std::unordered_set<swift::ModuleDecl*> encounteredModules;
169190 for (auto file : module ->getFiles ()) {
170191 auto sourceFile = llvm::dyn_cast<swift::SourceFile>(file);
171192 if (!sourceFile) {
@@ -176,10 +197,16 @@ void codeql::extractSwiftFiles(const SwiftExtractorConfiguration& config,
176197 continue ;
177198 }
178199 archiveFile (config, *sourceFile);
179- extractDeclarations (config, compiler, *module , sourceFile);
200+ encounteredModules = extractDeclarations (config, compiler, *module , sourceFile);
180201 }
181202 if (!isFromSourceFile) {
182- extractDeclarations (config, compiler, *module );
203+ encounteredModules = extractDeclarations (config, compiler, *module );
204+ }
205+ for (auto encountered : encounteredModules) {
206+ if (seen.count (encountered) == 0 ) {
207+ todo.push_back (encountered);
208+ seen.insert (encountered);
209+ }
183210 }
184211 }
185212}
0 commit comments