Skip to content

Commit acd2da7

Browse files
committed
[Dependency Scanning] Construct a hollow output on query failure to carry diagnostic output
1 parent f9bc227 commit acd2da7

File tree

3 files changed

+154
-40
lines changed

3 files changed

+154
-40
lines changed

lib/DependencyScan/DependencyScanningTool.cpp

Lines changed: 73 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -189,6 +189,71 @@ swiftscan_diagnostic_set_t *mapCollectedDiagnosticsForOutput(
189189
return diagnosticOutput;
190190
}
191191

192+
// Generate an instance of the `swiftscan_dependency_graph_s` which contains no
193+
// module dependnecies but captures the diagnostics emitted during the attempted
194+
// scan query.
195+
static swiftscan_dependency_graph_t generateHollowDiagnosticOutput(
196+
const DependencyScanDiagnosticCollector &ScanDiagnosticConsumer) {
197+
// Create a dependency graph instance
198+
swiftscan_dependency_graph_t hollowResult = new swiftscan_dependency_graph_s;
199+
200+
// Populate the `modules` with a single info for the main module
201+
// containing no dependencies
202+
swiftscan_dependency_set_t *dependencySet = new swiftscan_dependency_set_t;
203+
dependencySet->count = 1;
204+
dependencySet->modules = new swiftscan_dependency_info_t[1];
205+
swiftscan_dependency_info_s *hollowMainModuleInfo =
206+
new swiftscan_dependency_info_s;
207+
dependencySet->modules[0] = hollowMainModuleInfo;
208+
hollowResult->dependencies = dependencySet;
209+
210+
// Other main module details empty
211+
hollowMainModuleInfo->direct_dependencies =
212+
c_string_utils::create_empty_set();
213+
hollowMainModuleInfo->source_files = c_string_utils::create_empty_set();
214+
hollowMainModuleInfo->module_path = c_string_utils::create_null();
215+
hollowResult->main_module_name = c_string_utils::create_clone("unknown");
216+
hollowMainModuleInfo->module_name =
217+
c_string_utils::create_clone("swiftTextual:unknown");
218+
219+
// Hollow info details
220+
swiftscan_module_details_s *hollowDetails = new swiftscan_module_details_s;
221+
hollowDetails->kind = SWIFTSCAN_DEPENDENCY_INFO_SWIFT_TEXTUAL;
222+
hollowDetails->swift_textual_details = {c_string_utils::create_null(),
223+
c_string_utils::create_empty_set(),
224+
c_string_utils::create_null(),
225+
c_string_utils::create_empty_set(),
226+
c_string_utils::create_empty_set(),
227+
c_string_utils::create_empty_set(),
228+
c_string_utils::create_empty_set(),
229+
c_string_utils::create_empty_set(),
230+
c_string_utils::create_empty_set(),
231+
c_string_utils::create_null(),
232+
false,
233+
c_string_utils::create_null(),
234+
c_string_utils::create_null(),
235+
c_string_utils::create_null()};
236+
hollowMainModuleInfo->details = hollowDetails;
237+
238+
// Populate the diagnostic info
239+
hollowResult->diagnostics =
240+
mapCollectedDiagnosticsForOutput(&ScanDiagnosticConsumer);
241+
return hollowResult;
242+
}
243+
244+
// Generate an instance of the `swiftscan_import_set_t` which contains no
245+
// imports but captures the diagnostics emitted during the attempted
246+
// scan query.
247+
static swiftscan_import_set_t generateHollowDiagnosticOutputImportSet(
248+
const DependencyScanDiagnosticCollector &ScanDiagnosticConsumer) {
249+
// Create an dependency graph instance
250+
swiftscan_import_set_t hollowResult = new swiftscan_import_set_s;
251+
hollowResult->imports = c_string_utils::create_empty_set();
252+
hollowResult->diagnostics =
253+
mapCollectedDiagnosticsForOutput(&ScanDiagnosticConsumer);
254+
return hollowResult;
255+
}
256+
192257
DependencyScanningTool::DependencyScanningTool()
193258
: ScanningService(std::make_unique<SwiftDependencyScanningService>()),
194259
VersionedPCMInstanceCacheCache(
@@ -203,18 +268,13 @@ DependencyScanningTool::getDependencies(
203268
// There may be errors as early as in instance initialization, so we must ensure
204269
// we can catch those.
205270
auto ScanDiagnosticConsumer = std::make_shared<DependencyScanDiagnosticCollector>();
206-
auto produceDiagnosticStateOnFailure = [&ScanDiagnosticConsumer]() {
207-
swiftscan_dependency_graph_t result = new swiftscan_dependency_graph_s;
208-
result->diagnostics = mapCollectedDiagnosticsForOutput(ScanDiagnosticConsumer.get());
209-
return result;
210-
};
211271

212272
// The primary instance used to scan the query Swift source-code
213273
auto QueryContextOrErr = initCompilerInstanceForScan(Command,
214274
WorkingDirectory,
215275
ScanDiagnosticConsumer);
216276
if (QueryContextOrErr.getError())
217-
return produceDiagnosticStateOnFailure();
277+
return generateHollowDiagnosticOutput(*ScanDiagnosticConsumer);
218278

219279
auto QueryContext = std::move(*QueryContextOrErr);
220280

@@ -228,9 +288,9 @@ DependencyScanningTool::getDependencies(
228288
QueryContext.ScanDiagnostics.get(),
229289
cache);
230290
if (DependenciesOrErr.getError())
231-
return produceDiagnosticStateOnFailure();
291+
return generateHollowDiagnosticOutput(*ScanDiagnosticConsumer);
232292

233-
return std::move(*DependenciesOrErr);;
293+
return std::move(*DependenciesOrErr);
234294
}
235295

236296
llvm::ErrorOr<swiftscan_import_set_t>
@@ -243,11 +303,9 @@ DependencyScanningTool::getImports(ArrayRef<const char *> Command,
243303
auto QueryContextOrErr = initCompilerInstanceForScan(Command,
244304
WorkingDirectory,
245305
ScanDiagnosticConsumer);
246-
if (QueryContextOrErr.getError()) {
247-
swiftscan_import_set_t result = new swiftscan_import_set_s;
248-
result->diagnostics = mapCollectedDiagnosticsForOutput(ScanDiagnosticConsumer.get());
249-
return result;
250-
}
306+
if (QueryContextOrErr.getError())
307+
return generateHollowDiagnosticOutputImportSet(*ScanDiagnosticConsumer);
308+
251309
auto QueryContext = std::move(*QueryContextOrErr);
252310

253311
// Local scan cache instance, wrapping the shared global cache.
@@ -259,10 +317,9 @@ DependencyScanningTool::getImports(ArrayRef<const char *> Command,
259317
QueryContext.ScanDiagnostics.get(),
260318
cache);
261319
if (DependenciesOrErr.getError())
262-
return std::make_error_code(std::errc::not_supported);
263-
auto Dependencies = std::move(*DependenciesOrErr);
320+
return generateHollowDiagnosticOutputImportSet(*ScanDiagnosticConsumer);
264321

265-
return Dependencies;
322+
return std::move(*DependenciesOrErr);
266323
}
267324

268325
std::vector<llvm::ErrorOr<swiftscan_dependency_graph_t>>

lib/DependencyScan/ScanDependencies.cpp

Lines changed: 0 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -579,28 +579,6 @@ static void bridgeDependencyIDs(const ArrayRef<ModuleDependencyID> dependencies,
579579
}
580580
}
581581

582-
static swiftscan_macro_dependency_set_t *createMacroDependencySet(
583-
const std::map<std::string, MacroPluginDependency> &macroDeps) {
584-
swiftscan_macro_dependency_set_t *set = new swiftscan_macro_dependency_set_t;
585-
if (macroDeps.empty()) {
586-
set->count = 0;
587-
set->macro_dependencies = nullptr;
588-
return set;
589-
}
590-
set->count = macroDeps.size();
591-
set->macro_dependencies = new swiftscan_macro_dependency_t[set->count];
592-
unsigned SI = 0;
593-
for (auto &entry : macroDeps) {
594-
set->macro_dependencies[SI] = new swiftscan_macro_dependency_s;
595-
set->macro_dependencies[SI]->moduleName = create_clone(entry.first.c_str());
596-
set->macro_dependencies[SI]->libraryPath =
597-
create_clone(entry.second.LibraryPath.c_str());
598-
set->macro_dependencies[SI]->executablePath =
599-
create_clone(entry.second.ExecutablePath.c_str());
600-
}
601-
return set;
602-
}
603-
604582
static swiftscan_dependency_graph_t
605583
generateFullDependencyGraph(const CompilerInstance &instance,
606584
const DependencyScanDiagnosticCollector *diagnosticCollector,

unittests/DependencyScan/ModuleDeps.cpp

Lines changed: 81 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
#include "ScanFixture.h"
1414
#include "swift/Basic/Defer.h"
1515
#include "swift/Basic/Platform.h"
16+
#include "swift/DependencyScan/DependencyScanImpl.h"
1617
#include "llvm/Support/Path.h"
1718
#include "llvm/Support/raw_ostream.h"
1819
#include "llvm/TargetParser/Host.h"
@@ -243,9 +244,87 @@ public func overlayFuncA() { }\n"));
243244
CommandB.push_back(command.c_str());
244245
}
245246

246-
auto instanceA = ScannerTool.initCompilerInstanceForScan(CommandA, {});
247-
auto instanceB = ScannerTool.initCompilerInstanceForScan(CommandB, {});
247+
auto ScanDiagnosticConsumer = std::make_shared<DependencyScanDiagnosticCollector>();
248+
auto instanceA = ScannerTool.initCompilerInstanceForScan(CommandA, {}, ScanDiagnosticConsumer);
249+
auto instanceB = ScannerTool.initCompilerInstanceForScan(CommandB, {}, ScanDiagnosticConsumer);
248250
// Ensure that scans that only differ in module name have distinct scanning context hashes
249251
ASSERT_NE(instanceA->ScanInstance.get()->getInvocation().getModuleScanningHash(),
250252
instanceB->ScanInstance.get()->getInvocation().getModuleScanningHash());
251253
}
254+
255+
TEST_F(ScanTest, TestModuleCycle) {
256+
SmallString<256> tempDir;
257+
ASSERT_FALSE(llvm::sys::fs::createUniqueDirectory("ScanTest.TestModuleCycle", tempDir));
258+
SWIFT_DEFER { llvm::sys::fs::remove_directories(tempDir); };
259+
260+
// Create test input file
261+
std::string TestPathStr = createFilename(tempDir, "foo.swift");
262+
ASSERT_FALSE(emitFileWithContents(tempDir, "foo.swift", "import A\n"));
263+
264+
// Create includes
265+
std::string IncludeDirPath = createFilename(tempDir, "include");
266+
ASSERT_FALSE(llvm::sys::fs::create_directory(IncludeDirPath));
267+
std::string SwiftDirPath = createFilename(IncludeDirPath, "Swift");
268+
ASSERT_FALSE(llvm::sys::fs::create_directory(SwiftDirPath));
269+
270+
// Create imported module Swift interface files
271+
ASSERT_FALSE(emitFileWithContents(SwiftDirPath, "A.swiftinterface",
272+
"// swift-interface-format-version: 1.0\n\
273+
// swift-module-flags: -module-name A\n\
274+
import Swift\n\
275+
import B\n\
276+
public func funcA() { }\n"));
277+
ASSERT_FALSE(emitFileWithContents(SwiftDirPath, "B.swiftinterface",
278+
"// swift-interface-format-version: 1.0\n\
279+
// swift-module-flags: -module-name B\n\
280+
import Swift\n\
281+
import A\n\
282+
public func funcB() { }\n"));
283+
284+
// Paths to shims and stdlib
285+
llvm::SmallString<128> ShimsLibDir = StdLibDir;
286+
llvm::sys::path::append(ShimsLibDir, "shims");
287+
auto Target = llvm::Triple(llvm::sys::getDefaultTargetTriple());
288+
llvm::sys::path::append(StdLibDir, getPlatformNameForTriple(Target));
289+
290+
std::vector<std::string> BaseCommandStrArr = {
291+
TestPathStr,
292+
std::string("-I ") + SwiftDirPath,
293+
std::string("-I ") + StdLibDir.str().str(),
294+
std::string("-I ") + ShimsLibDir.str().str(),
295+
};
296+
297+
std::vector<std::string> CommandStr = BaseCommandStrArr;
298+
CommandStr.push_back("-module-name");
299+
CommandStr.push_back("test");
300+
301+
// On Windows we need to add an extra escape for path separator characters because otherwise
302+
// the command line tokenizer will treat them as escape characters.
303+
for (size_t i = 0; i < CommandStr.size(); ++i) {
304+
std::replace(CommandStr[i].begin(), CommandStr[i].end(), '\\', '/');
305+
}
306+
std::vector<const char*> Command;
307+
for (auto &command : CommandStr)
308+
Command.push_back(command.c_str());
309+
310+
auto ScanDiagnosticConsumer = std::make_shared<DependencyScanDiagnosticCollector>();
311+
312+
auto DependenciesOrErr = ScannerTool.getDependencies(Command, {}, {});
313+
314+
// Ensure a hollow output with diagnostic info is produced
315+
ASSERT_FALSE(DependenciesOrErr.getError());
316+
auto Dependencies = DependenciesOrErr.get();
317+
auto Diagnostics = Dependencies->diagnostics;
318+
ASSERT_TRUE(Diagnostics->count == 1);
319+
auto Diagnostic = Diagnostics->diagnostics[0];
320+
ASSERT_TRUE(Diagnostic->severity == SWIFTSCAN_DIAGNOSTIC_SEVERITY_ERROR);
321+
auto Message = std::string((const char*)Diagnostic->message.data,
322+
Diagnostic->message.length);
323+
ASSERT_TRUE(Message == "module dependency cycle: 'A.swiftinterface -> B.swiftinterface -> A.swiftinterface'\n");
324+
325+
// Ensure hollow output is hollow
326+
ASSERT_TRUE(Dependencies->dependencies->count == 1);
327+
ASSERT_TRUE(Dependencies->dependencies->modules[0]->source_files->count == 0);
328+
ASSERT_TRUE(Dependencies->dependencies->modules[0]->direct_dependencies->count == 0);
329+
swiftscan_dependency_graph_dispose(Dependencies);
330+
}

0 commit comments

Comments
 (0)