diff --git a/include/swift/DependencyScan/DependencyScanningTool.h b/include/swift/DependencyScan/DependencyScanningTool.h index dd74ebe42146e..71f3160f063cf 100644 --- a/include/swift/DependencyScan/DependencyScanningTool.h +++ b/include/swift/DependencyScan/DependencyScanningTool.h @@ -28,7 +28,7 @@ class DependencyScanDiagnosticCollector; struct ScanQueryInstance { std::unique_ptr ScanInstance; - std::unique_ptr ScanDiagnostics; + std::shared_ptr ScanDiagnostics; }; /// Diagnostic consumer that simply collects the diagnostics emitted so-far @@ -124,15 +124,10 @@ class DependencyScanningTool { /// that will be used for this scan. llvm::ErrorOr initCompilerInstanceForScan(ArrayRef Command, - StringRef WorkingDirectory); + StringRef WorkingDirectory, + std::shared_ptr scannerDiagnosticsCollector); private: - /// Using the specified invocation command, initialize the scanner instance - /// for this scan. Returns the `CompilerInstance` that will be used. - llvm::ErrorOr - initScannerForAction(ArrayRef Command, - StringRef WorkingDirectory); - /// Shared cache of module dependencies, re-used by individual full-scan queries /// during the lifetime of this Tool. std::unique_ptr ScanningService; @@ -150,6 +145,8 @@ class DependencyScanningTool { llvm::StringSaver Saver; }; +swiftscan_diagnostic_set_t *mapCollectedDiagnosticsForOutput(const DependencyScanDiagnosticCollector *diagnosticCollector); + } // end namespace dependencies } // end namespace swift diff --git a/lib/DependencyScan/DependencyScanningTool.cpp b/lib/DependencyScan/DependencyScanningTool.cpp index bdb6759fe1a2b..3ef7648d7a198 100644 --- a/lib/DependencyScan/DependencyScanningTool.cpp +++ b/lib/DependencyScan/DependencyScanningTool.cpp @@ -140,6 +140,132 @@ void LockingDependencyScanDiagnosticCollector::addDiagnostic( DependencyScanDiagnosticCollector::addDiagnostic(SM, Info); } +swiftscan_diagnostic_set_t *mapCollectedDiagnosticsForOutput( + const DependencyScanDiagnosticCollector *diagnosticCollector) { + auto collectedDiagnostics = diagnosticCollector->getDiagnostics(); + auto numDiagnostics = collectedDiagnostics.size(); + swiftscan_diagnostic_set_t *diagnosticOutput = new swiftscan_diagnostic_set_t; + diagnosticOutput->count = numDiagnostics; + diagnosticOutput->diagnostics = + new swiftscan_diagnostic_info_t[numDiagnostics]; + for (size_t i = 0; i < numDiagnostics; ++i) { + const auto &Diagnostic = collectedDiagnostics[i]; + swiftscan_diagnostic_info_s *diagnosticInfo = + new swiftscan_diagnostic_info_s; + diagnosticInfo->message = + swift::c_string_utils::create_clone(Diagnostic.Message.c_str()); + switch (Diagnostic.Severity) { + case llvm::SourceMgr::DK_Error: + diagnosticInfo->severity = SWIFTSCAN_DIAGNOSTIC_SEVERITY_ERROR; + break; + case llvm::SourceMgr::DK_Warning: + diagnosticInfo->severity = SWIFTSCAN_DIAGNOSTIC_SEVERITY_WARNING; + break; + case llvm::SourceMgr::DK_Note: + diagnosticInfo->severity = SWIFTSCAN_DIAGNOSTIC_SEVERITY_NOTE; + break; + case llvm::SourceMgr::DK_Remark: + diagnosticInfo->severity = SWIFTSCAN_DIAGNOSTIC_SEVERITY_REMARK; + break; + } + + if (Diagnostic.ImportLocation.has_value()) { + auto importLocation = Diagnostic.ImportLocation.value(); + swiftscan_source_location_s *sourceLoc = new swiftscan_source_location_s; + if (importLocation.bufferIdentifier.empty()) + sourceLoc->buffer_identifier = swift::c_string_utils::create_null(); + else + sourceLoc->buffer_identifier = swift::c_string_utils::create_clone( + importLocation.bufferIdentifier.c_str()); + sourceLoc->line_number = importLocation.lineNumber; + sourceLoc->column_number = importLocation.columnNumber; + diagnosticInfo->source_location = sourceLoc; + } else { + diagnosticInfo->source_location = nullptr; + } + + diagnosticOutput->diagnostics[i] = diagnosticInfo; + } + return diagnosticOutput; +} + +// Generate an instance of the `swiftscan_dependency_graph_s` which contains no +// module dependnecies but captures the diagnostics emitted during the attempted +// scan query. +static swiftscan_dependency_graph_t generateHollowDiagnosticOutput( + const DependencyScanDiagnosticCollector &ScanDiagnosticConsumer) { + // Create a dependency graph instance + swiftscan_dependency_graph_t hollowResult = new swiftscan_dependency_graph_s; + + // Populate the `modules` with a single info for the main module + // containing no dependencies + swiftscan_dependency_set_t *dependencySet = new swiftscan_dependency_set_t; + dependencySet->count = 1; + dependencySet->modules = new swiftscan_dependency_info_t[1]; + swiftscan_dependency_info_s *hollowMainModuleInfo = + new swiftscan_dependency_info_s; + dependencySet->modules[0] = hollowMainModuleInfo; + hollowResult->dependencies = dependencySet; + + // Other main module details empty + hollowMainModuleInfo->direct_dependencies = + c_string_utils::create_empty_set(); + hollowMainModuleInfo->source_files = c_string_utils::create_empty_set(); + hollowMainModuleInfo->module_path = c_string_utils::create_null(); + hollowResult->main_module_name = c_string_utils::create_clone("unknown"); + hollowMainModuleInfo->module_name = + c_string_utils::create_clone("swiftTextual:unknown"); + + // Hollow info details + swiftscan_module_details_s *hollowDetails = new swiftscan_module_details_s; + hollowDetails->kind = SWIFTSCAN_DEPENDENCY_INFO_SWIFT_TEXTUAL; + swiftscan_macro_dependency_set_t *hollowMacroSet = new swiftscan_macro_dependency_set_t; + hollowMacroSet->count = 0; + hollowMacroSet->macro_dependencies = nullptr; + hollowDetails->swift_textual_details = {c_string_utils::create_null(), + c_string_utils::create_empty_set(), + c_string_utils::create_null(), + c_string_utils::create_empty_set(), + c_string_utils::create_empty_set(), + c_string_utils::create_empty_set(), + c_string_utils::create_empty_set(), + c_string_utils::create_empty_set(), + c_string_utils::create_empty_set(), + c_string_utils::create_null(), + false, + false, + c_string_utils::create_null(), + c_string_utils::create_null(), + c_string_utils::create_null(), + hollowMacroSet}; + hollowMainModuleInfo->details = hollowDetails; + + // Empty Link Library set + swiftscan_link_library_set_t *hollowLinkLibrarySet = + new swiftscan_link_library_set_t; + hollowLinkLibrarySet->count = 0; + hollowLinkLibrarySet->link_libraries = nullptr; + hollowMainModuleInfo->link_libraries = hollowLinkLibrarySet; + + // Populate the diagnostic info + hollowResult->diagnostics = + mapCollectedDiagnosticsForOutput(&ScanDiagnosticConsumer); + return hollowResult; +} + +// Generate an instance of the `swiftscan_import_set_t` which contains no +// imports but captures the diagnostics emitted during the attempted +// scan query. +static swiftscan_import_set_t generateHollowDiagnosticOutputImportSet( + const DependencyScanDiagnosticCollector &ScanDiagnosticConsumer) { + // Create an dependency graph instance + swiftscan_import_set_t hollowResult = new swiftscan_import_set_s; + hollowResult->imports = c_string_utils::create_empty_set(); + hollowResult->diagnostics = + mapCollectedDiagnosticsForOutput(&ScanDiagnosticConsumer); + return hollowResult; +} + DependencyScanningTool::DependencyScanningTool() : ScanningService(std::make_unique()), VersionedPCMInstanceCacheCache( @@ -148,12 +274,20 @@ DependencyScanningTool::DependencyScanningTool() llvm::ErrorOr DependencyScanningTool::getDependencies( - ArrayRef Command, const llvm::StringSet<> &PlaceholderModules, + ArrayRef Command, + const llvm::StringSet<> &PlaceholderModules, StringRef WorkingDirectory) { + // There may be errors as early as in instance initialization, so we must ensure + // we can catch those. + auto ScanDiagnosticConsumer = std::make_shared(); + // The primary instance used to scan the query Swift source-code - auto QueryContextOrErr = initScannerForAction(Command, WorkingDirectory); - if (std::error_code EC = QueryContextOrErr.getError()) - return EC; + auto QueryContextOrErr = initCompilerInstanceForScan(Command, + WorkingDirectory, + ScanDiagnosticConsumer); + if (QueryContextOrErr.getError()) + return generateHollowDiagnosticOutput(*ScanDiagnosticConsumer); + auto QueryContext = std::move(*QueryContextOrErr); // Local scan cache instance, wrapping the shared global cache. @@ -166,19 +300,24 @@ DependencyScanningTool::getDependencies( QueryContext.ScanDiagnostics.get(), cache); if (DependenciesOrErr.getError()) - return std::make_error_code(std::errc::not_supported); - auto Dependencies = std::move(*DependenciesOrErr); + return generateHollowDiagnosticOutput(*ScanDiagnosticConsumer); - return Dependencies; + return std::move(*DependenciesOrErr); } llvm::ErrorOr DependencyScanningTool::getImports(ArrayRef Command, StringRef WorkingDirectory) { + // There may be errors as early as in instance initialization, so we must ensure + // we can catch those + auto ScanDiagnosticConsumer = std::make_shared(); // The primary instance used to scan the query Swift source-code - auto QueryContextOrErr = initScannerForAction(Command, WorkingDirectory); - if (std::error_code EC = QueryContextOrErr.getError()) - return EC; + auto QueryContextOrErr = initCompilerInstanceForScan(Command, + WorkingDirectory, + ScanDiagnosticConsumer); + if (QueryContextOrErr.getError()) + return generateHollowDiagnosticOutputImportSet(*ScanDiagnosticConsumer); + auto QueryContext = std::move(*QueryContextOrErr); // Local scan cache instance, wrapping the shared global cache. @@ -190,10 +329,9 @@ DependencyScanningTool::getImports(ArrayRef Command, QueryContext.ScanDiagnostics.get(), cache); if (DependenciesOrErr.getError()) - return std::make_error_code(std::errc::not_supported); - auto Dependencies = std::move(*DependenciesOrErr); + return generateHollowDiagnosticOutputImportSet(*ScanDiagnosticConsumer); - return Dependencies; + return std::move(*DependenciesOrErr); } std::vector> @@ -202,10 +340,14 @@ DependencyScanningTool::getDependencies( const std::vector &BatchInput, const llvm::StringSet<> &PlaceholderModules, StringRef WorkingDirectory) { // The primary instance used to scan Swift modules - auto QueryContextOrErr = initScannerForAction(Command, WorkingDirectory); + auto ScanDiagnosticConsumer = std::make_shared(); + auto QueryContextOrErr = initCompilerInstanceForScan(Command, + WorkingDirectory, + ScanDiagnosticConsumer); if (std::error_code EC = QueryContextOrErr.getError()) return std::vector>( BatchInput.size(), std::make_error_code(std::errc::invalid_argument)); + auto QueryContext = std::move(*QueryContextOrErr); // Local scan cache instance, wrapping the shared global cache. @@ -264,26 +406,26 @@ void DependencyScanningTool::resetDiagnostics() { } llvm::ErrorOr -DependencyScanningTool::initScannerForAction( - ArrayRef Command, StringRef WorkingDirectory) { +DependencyScanningTool::initCompilerInstanceForScan( + ArrayRef CommandArgs, + StringRef WorkingDir, + std::shared_ptr scannerDiagnosticsCollector) { // The remainder of this method operates on shared state in the // scanning service and global LLVM state with: // llvm::cl::ResetAllOptionOccurrences llvm::sys::SmartScopedLock Lock(DependencyScanningToolStateLock); - return initCompilerInstanceForScan(Command, WorkingDirectory); -} + // FIXME: Instead, target-info and supported-features queries must use + // `DependencyScanningToolStateLock`, but this currently requires further + // client-side API plumbing. + llvm::sys::SmartScopedLock TargetInfoLock(TargetInfoMutex); -llvm::ErrorOr -DependencyScanningTool::initCompilerInstanceForScan( - ArrayRef CommandArgs, StringRef WorkingDir) { // State unique to an individual scan auto Instance = std::make_unique(); - auto ScanDiagnosticConsumer = std::make_unique(); // FIXME: The shared CDC must be deprecated once all clients have switched // to using per-scan diagnostic output embedded in the `swiftscan_dependency_graph_s` Instance->addDiagnosticConsumer(&CDC); - Instance->addDiagnosticConsumer(ScanDiagnosticConsumer.get()); + Instance->addDiagnosticConsumer(scannerDiagnosticsCollector.get()); // Basic error checking on the arguments if (CommandArgs.empty()) { @@ -327,6 +469,7 @@ DependencyScanningTool::initCompilerInstanceForScan( if (Instance->setup(Invocation, InstanceSetupError)) { return std::make_error_code(std::errc::not_supported); } + Invocation.getFrontendOptions().LLVMArgs.clear(); // Setup the caching service after the instance finishes setup. if (ScanningService->setupCachingDependencyScanningService(*Instance)) @@ -335,7 +478,7 @@ DependencyScanningTool::initCompilerInstanceForScan( (void)Instance->getMainModule(); return ScanQueryInstance{std::move(Instance), - std::move(ScanDiagnosticConsumer)}; + scannerDiagnosticsCollector}; } } // namespace dependencies diff --git a/lib/DependencyScan/ModuleDependencyScanner.cpp b/lib/DependencyScan/ModuleDependencyScanner.cpp index 84bb87915e079..1cc7a3f2ef5a6 100644 --- a/lib/DependencyScan/ModuleDependencyScanner.cpp +++ b/lib/DependencyScan/ModuleDependencyScanner.cpp @@ -207,6 +207,8 @@ ModuleDependencyScanningWorker::ModuleDependencyScanningWorker( ScanASTContext.getModuleInterfaceChecker()), &DependencyTracker, ScanCompilerInvocation.getSearchPathOptions().ModuleLoadMode); + + llvm::cl::ResetAllOptionOccurrences(); } ModuleDependencyVector diff --git a/lib/DependencyScan/ScanDependencies.cpp b/lib/DependencyScan/ScanDependencies.cpp index 415dac694a685..2bb45cad59ea3 100644 --- a/lib/DependencyScan/ScanDependencies.cpp +++ b/lib/DependencyScan/ScanDependencies.cpp @@ -589,56 +589,6 @@ static void bridgeDependencyIDs(const ArrayRef dependencies, } } -static swiftscan_diagnostic_set_t *mapCollectedDiagnosticsForOutput( - const SourceManager &SM, - const DependencyScanDiagnosticCollector *diagnosticCollector) { - auto collectedDiagnostics = diagnosticCollector->getDiagnostics(); - auto numDiagnostics = collectedDiagnostics.size(); - swiftscan_diagnostic_set_t *diagnosticOutput = new swiftscan_diagnostic_set_t; - diagnosticOutput->count = numDiagnostics; - diagnosticOutput->diagnostics = - new swiftscan_diagnostic_info_t[numDiagnostics]; - for (size_t i = 0; i < numDiagnostics; ++i) { - const auto &Diagnostic = collectedDiagnostics[i]; - swiftscan_diagnostic_info_s *diagnosticInfo = - new swiftscan_diagnostic_info_s; - diagnosticInfo->message = - swift::c_string_utils::create_clone(Diagnostic.Message.c_str()); - switch (Diagnostic.Severity) { - case llvm::SourceMgr::DK_Error: - diagnosticInfo->severity = SWIFTSCAN_DIAGNOSTIC_SEVERITY_ERROR; - break; - case llvm::SourceMgr::DK_Warning: - diagnosticInfo->severity = SWIFTSCAN_DIAGNOSTIC_SEVERITY_WARNING; - break; - case llvm::SourceMgr::DK_Note: - diagnosticInfo->severity = SWIFTSCAN_DIAGNOSTIC_SEVERITY_NOTE; - break; - case llvm::SourceMgr::DK_Remark: - diagnosticInfo->severity = SWIFTSCAN_DIAGNOSTIC_SEVERITY_REMARK; - break; - } - - if (Diagnostic.ImportLocation.has_value()) { - auto importLocation = Diagnostic.ImportLocation.value(); - swiftscan_source_location_s *sourceLoc = new swiftscan_source_location_s; - if (importLocation.bufferIdentifier.empty()) - sourceLoc->buffer_identifier = swift::c_string_utils::create_null(); - else - sourceLoc->buffer_identifier = swift::c_string_utils::create_clone( - importLocation.bufferIdentifier.c_str()); - sourceLoc->line_number = importLocation.lineNumber; - sourceLoc->column_number = importLocation.columnNumber; - diagnosticInfo->source_location = sourceLoc; - } else { - diagnosticInfo->source_location = nullptr; - } - - diagnosticOutput->diagnostics[i] = diagnosticInfo; - } - return diagnosticOutput; -} - static swiftscan_macro_dependency_set_t *createMacroDependencySet( const std::map ¯oDeps) { swiftscan_macro_dependency_set_t *set = new swiftscan_macro_dependency_set_t; @@ -867,8 +817,7 @@ generateFullDependencyGraph(const CompilerInstance &instance, result->dependencies = dependencySet; result->diagnostics = diagnosticCollector - ? mapCollectedDiagnosticsForOutput(instance.getSourceMgr(), - diagnosticCollector) + ? mapCollectedDiagnosticsForOutput(diagnosticCollector) : nullptr; return result; } @@ -1574,13 +1523,11 @@ swift::dependencies::performModulePrescan(CompilerInstance &instance, importSet->imports = create_set(importIdentifiers); importSet->diagnostics = diagnosticCollector - ? mapCollectedDiagnosticsForOutput(instance.getSourceMgr(), - diagnosticCollector) + ? mapCollectedDiagnosticsForOutput(diagnosticCollector) : nullptr; importSet->diagnostics = diagnosticCollector - ? mapCollectedDiagnosticsForOutput(instance.getSourceMgr(), - diagnosticCollector) + ? mapCollectedDiagnosticsForOutput(diagnosticCollector) : nullptr; return importSet; } diff --git a/tools/libSwiftScan/libSwiftScan.cpp b/tools/libSwiftScan/libSwiftScan.cpp index 5ce6a1fbd16f1..37a76720df6e2 100644 --- a/tools/libSwiftScan/libSwiftScan.cpp +++ b/tools/libSwiftScan/libSwiftScan.cpp @@ -126,11 +126,13 @@ void swiftscan_dependency_info_dispose(swiftscan_dependency_info_t info) { } void swiftscan_dependency_set_dispose(swiftscan_dependency_set_t *set) { - for (size_t i = 0; i < set->count; ++i) { - swiftscan_dependency_info_dispose(set->modules[i]); + if (set) { + for (size_t i = 0; i < set->count; ++i) { + swiftscan_dependency_info_dispose(set->modules[i]); + } + delete[] set->modules; + delete set; } - delete[] set->modules; - delete set; } //=== Scanner Cache Operations --------------------------------------------===// @@ -753,11 +755,13 @@ void swiftscan_diagnostic_dispose(swiftscan_diagnostic_info_t diagnostic) { void swiftscan_diagnostics_set_dispose(swiftscan_diagnostic_set_t* diagnostics){ - for (size_t i = 0; i < diagnostics->count; ++i) { - swiftscan_diagnostic_dispose(diagnostics->diagnostics[i]); + if (diagnostics) { + for (size_t i = 0; i < diagnostics->count; ++i) { + swiftscan_diagnostic_dispose(diagnostics->diagnostics[i]); + } + delete[] diagnostics->diagnostics; + delete diagnostics; } - delete[] diagnostics->diagnostics; - delete diagnostics; } //=== Source Location -----------------------------------------------------===// diff --git a/unittests/DependencyScan/ModuleDeps.cpp b/unittests/DependencyScan/ModuleDeps.cpp index 88b9b61e466ed..27c8edf24e98a 100644 --- a/unittests/DependencyScan/ModuleDeps.cpp +++ b/unittests/DependencyScan/ModuleDeps.cpp @@ -13,6 +13,7 @@ #include "ScanFixture.h" #include "swift/Basic/Defer.h" #include "swift/Basic/Platform.h" +#include "swift/DependencyScan/DependencyScanImpl.h" #include "llvm/Support/Path.h" #include "llvm/Support/raw_ostream.h" #include "llvm/TargetParser/Host.h" @@ -243,9 +244,88 @@ public func overlayFuncA() { }\n")); CommandB.push_back(command.c_str()); } - auto instanceA = ScannerTool.initCompilerInstanceForScan(CommandA, {}); - auto instanceB = ScannerTool.initCompilerInstanceForScan(CommandB, {}); + auto ScanDiagnosticConsumer = std::make_shared(); + auto instanceA = ScannerTool.initCompilerInstanceForScan(CommandA, {}, ScanDiagnosticConsumer); + auto instanceB = ScannerTool.initCompilerInstanceForScan(CommandB, {}, ScanDiagnosticConsumer); // Ensure that scans that only differ in module name have distinct scanning context hashes ASSERT_NE(instanceA->ScanInstance.get()->getInvocation().getModuleScanningHash(), instanceB->ScanInstance.get()->getInvocation().getModuleScanningHash()); } + +TEST_F(ScanTest, TestModuleCycle) { + SmallString<256> tempDir; + ASSERT_FALSE(llvm::sys::fs::createUniqueDirectory("ScanTest.TestModuleCycle", tempDir)); + SWIFT_DEFER { llvm::sys::fs::remove_directories(tempDir); }; + + // Create test input file + std::string TestPathStr = createFilename(tempDir, "foo.swift"); + ASSERT_FALSE(emitFileWithContents(tempDir, "foo.swift", "import A\n")); + + // Create includes + std::string IncludeDirPath = createFilename(tempDir, "include"); + ASSERT_FALSE(llvm::sys::fs::create_directory(IncludeDirPath)); + std::string SwiftDirPath = createFilename(IncludeDirPath, "Swift"); + ASSERT_FALSE(llvm::sys::fs::create_directory(SwiftDirPath)); + + // Create imported module Swift interface files + ASSERT_FALSE(emitFileWithContents(SwiftDirPath, "A.swiftinterface", + "// swift-interface-format-version: 1.0\n\ +// swift-module-flags: -module-name A\n\ +import Swift\n\ +import B\n\ +public func funcA() { }\n")); + ASSERT_FALSE(emitFileWithContents(SwiftDirPath, "B.swiftinterface", + "// swift-interface-format-version: 1.0\n\ +// swift-module-flags: -module-name B\n\ +import Swift\n\ +import A\n\ +public func funcB() { }\n")); + + // Paths to shims and stdlib + llvm::SmallString<128> ShimsLibDir = StdLibDir; + llvm::sys::path::append(ShimsLibDir, "shims"); + auto Target = llvm::Triple(llvm::sys::getDefaultTargetTriple()); + llvm::sys::path::append(StdLibDir, getPlatformNameForTriple(Target)); + + std::vector BaseCommandStrArr = { + TestPathStr, + std::string("-I ") + SwiftDirPath, + std::string("-I ") + StdLibDir.str().str(), + std::string("-I ") + ShimsLibDir.str().str(), + }; + + std::vector CommandStr = BaseCommandStrArr; + CommandStr.push_back("-module-name"); + CommandStr.push_back("test"); + + // On Windows we need to add an extra escape for path separator characters because otherwise + // the command line tokenizer will treat them as escape characters. + for (size_t i = 0; i < CommandStr.size(); ++i) { + std::replace(CommandStr[i].begin(), CommandStr[i].end(), '\\', '/'); + } + std::vector Command; + for (auto &command : CommandStr) + Command.push_back(command.c_str()); + + auto ScanDiagnosticConsumer = std::make_shared(); + + auto DependenciesOrErr = ScannerTool.getDependencies(Command, {}, {}); + + // Ensure a hollow output with diagnostic info is produced + ASSERT_FALSE(DependenciesOrErr.getError()); + auto Dependencies = DependenciesOrErr.get(); + auto Diagnostics = Dependencies->diagnostics; + ASSERT_TRUE(Diagnostics->count == 1); + auto Diagnostic = Diagnostics->diagnostics[0]; + ASSERT_TRUE(Diagnostic->severity == SWIFTSCAN_DIAGNOSTIC_SEVERITY_ERROR); + auto Message = std::string((const char*)Diagnostic->message.data, + Diagnostic->message.length); + ASSERT_TRUE(Message == "module dependency cycle: 'A.swiftinterface -> B.swiftinterface -> A.swiftinterface'\n"); + + // Ensure hollow output is hollow + ASSERT_TRUE(Dependencies->dependencies->count == 1); + ASSERT_TRUE(Dependencies->dependencies->modules[0]->source_files->count == 0); + ASSERT_TRUE(Dependencies->dependencies->modules[0]->direct_dependencies->count == 0); + ASSERT_TRUE(Dependencies->dependencies->modules[0]->link_libraries->count == 0); + swiftscan_dependency_graph_dispose(Dependencies); +}