Skip to content

Commit a47bf36

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

File tree

2 files changed

+167
-18
lines changed

2 files changed

+167
-18
lines changed

lib/DependencyScan/DependencyScanningTool.cpp

Lines changed: 85 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -189,6 +189,83 @@ 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+
swiftscan_macro_dependency_set_t *hollowMacroSet = new swiftscan_macro_dependency_set_t;
223+
hollowMacroSet->count = 0;
224+
hollowMacroSet->macro_dependencies = nullptr;
225+
hollowDetails->swift_textual_details = {c_string_utils::create_null(),
226+
c_string_utils::create_empty_set(),
227+
c_string_utils::create_null(),
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_empty_set(),
232+
c_string_utils::create_empty_set(),
233+
c_string_utils::create_empty_set(),
234+
c_string_utils::create_null(),
235+
false,
236+
false,
237+
c_string_utils::create_null(),
238+
c_string_utils::create_null(),
239+
c_string_utils::create_null(),
240+
hollowMacroSet};
241+
hollowMainModuleInfo->details = hollowDetails;
242+
243+
// Empty Link Library set
244+
swiftscan_link_library_set_t *hollowLinkLibrarySet =
245+
new swiftscan_link_library_set_t;
246+
hollowLinkLibrarySet->count = 0;
247+
hollowLinkLibrarySet->link_libraries = nullptr;
248+
hollowMainModuleInfo->link_libraries = hollowLinkLibrarySet;
249+
250+
// Populate the diagnostic info
251+
hollowResult->diagnostics =
252+
mapCollectedDiagnosticsForOutput(&ScanDiagnosticConsumer);
253+
return hollowResult;
254+
}
255+
256+
// Generate an instance of the `swiftscan_import_set_t` which contains no
257+
// imports but captures the diagnostics emitted during the attempted
258+
// scan query.
259+
static swiftscan_import_set_t generateHollowDiagnosticOutputImportSet(
260+
const DependencyScanDiagnosticCollector &ScanDiagnosticConsumer) {
261+
// Create an dependency graph instance
262+
swiftscan_import_set_t hollowResult = new swiftscan_import_set_s;
263+
hollowResult->imports = c_string_utils::create_empty_set();
264+
hollowResult->diagnostics =
265+
mapCollectedDiagnosticsForOutput(&ScanDiagnosticConsumer);
266+
return hollowResult;
267+
}
268+
192269
DependencyScanningTool::DependencyScanningTool()
193270
: ScanningService(std::make_unique<SwiftDependencyScanningService>()),
194271
VersionedPCMInstanceCacheCache(
@@ -203,18 +280,13 @@ DependencyScanningTool::getDependencies(
203280
// There may be errors as early as in instance initialization, so we must ensure
204281
// we can catch those.
205282
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-
};
211283

212284
// The primary instance used to scan the query Swift source-code
213285
auto QueryContextOrErr = initCompilerInstanceForScan(Command,
214286
WorkingDirectory,
215287
ScanDiagnosticConsumer);
216288
if (QueryContextOrErr.getError())
217-
return produceDiagnosticStateOnFailure();
289+
return generateHollowDiagnosticOutput(*ScanDiagnosticConsumer);
218290

219291
auto QueryContext = std::move(*QueryContextOrErr);
220292

@@ -228,9 +300,9 @@ DependencyScanningTool::getDependencies(
228300
QueryContext.ScanDiagnostics.get(),
229301
cache);
230302
if (DependenciesOrErr.getError())
231-
return produceDiagnosticStateOnFailure();
303+
return generateHollowDiagnosticOutput(*ScanDiagnosticConsumer);
232304

233-
return std::move(*DependenciesOrErr);;
305+
return std::move(*DependenciesOrErr);
234306
}
235307

236308
llvm::ErrorOr<swiftscan_import_set_t>
@@ -243,11 +315,9 @@ DependencyScanningTool::getImports(ArrayRef<const char *> Command,
243315
auto QueryContextOrErr = initCompilerInstanceForScan(Command,
244316
WorkingDirectory,
245317
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-
}
318+
if (QueryContextOrErr.getError())
319+
return generateHollowDiagnosticOutputImportSet(*ScanDiagnosticConsumer);
320+
251321
auto QueryContext = std::move(*QueryContextOrErr);
252322

253323
// Local scan cache instance, wrapping the shared global cache.
@@ -259,10 +329,9 @@ DependencyScanningTool::getImports(ArrayRef<const char *> Command,
259329
QueryContext.ScanDiagnostics.get(),
260330
cache);
261331
if (DependenciesOrErr.getError())
262-
return std::make_error_code(std::errc::not_supported);
263-
auto Dependencies = std::move(*DependenciesOrErr);
332+
return generateHollowDiagnosticOutputImportSet(*ScanDiagnosticConsumer);
264333

265-
return Dependencies;
334+
return std::move(*DependenciesOrErr);
266335
}
267336

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

unittests/DependencyScan/ModuleDeps.cpp

Lines changed: 82 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,88 @@ 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+
ASSERT_TRUE(Dependencies->dependencies->modules[0]->link_libraries->count == 0);
330+
swiftscan_dependency_graph_dispose(Dependencies);
331+
}

0 commit comments

Comments
 (0)