From 6816922dd46ab0bd05e206315793f8f56a60942c Mon Sep 17 00:00:00 2001 From: Artem Chikin Date: Tue, 10 Jun 2025 16:05:54 -0700 Subject: [PATCH 1/2] [Dependency Scanning] Keep track of each imported module's access control Adds an access control field for each imported module identified. When multiple imports of the same module are found, this keeps track of the most "open" access specifier. --- include/swift/AST/ModuleDependencies.h | 26 ++++++--- .../SerializedModuleDependencyCacheFormat.h | 6 +- .../Serialization/SerializedModuleLoader.h | 3 +- lib/AST/ModuleDependencies.cpp | 51 ++++++++++++---- .../ClangModuleDependencyScanner.cpp | 3 +- .../ModuleDependencyCacheSerialization.cpp | 14 +++-- .../ModuleDependencyScanner.cpp | 12 +++- lib/Serialization/ScanningLoaders.cpp | 2 + lib/Serialization/SerializedModuleLoader.cpp | 58 +++++++------------ 9 files changed, 105 insertions(+), 70 deletions(-) diff --git a/include/swift/AST/ModuleDependencies.h b/include/swift/AST/ModuleDependencies.h index e6d0581dab5a4..4846c4bcca735 100644 --- a/include/swift/AST/ModuleDependencies.h +++ b/include/swift/AST/ModuleDependencies.h @@ -156,30 +156,35 @@ struct ScannerImportStatementInfo { uint32_t columnNumber; }; - ScannerImportStatementInfo(std::string importIdentifier, bool isExported) - : importLocations(), importIdentifier(importIdentifier), - isExported(isExported) {} + ScannerImportStatementInfo(std::string importIdentifier, bool isExported, + AccessLevel accessLevel) + : importIdentifier(importIdentifier), importLocations(), + isExported(isExported), accessLevel(accessLevel) {} ScannerImportStatementInfo(std::string importIdentifier, bool isExported, + AccessLevel accessLevel, ImportDiagnosticLocationInfo location) - : importLocations({location}), importIdentifier(importIdentifier), - isExported(isExported) {} + : importIdentifier(importIdentifier), importLocations({location}), + isExported(isExported), accessLevel(accessLevel) {} ScannerImportStatementInfo(std::string importIdentifier, bool isExported, + AccessLevel accessLevel, SmallVector locations) - : importLocations(locations), importIdentifier(importIdentifier), - isExported(isExported) {} + : importIdentifier(importIdentifier), importLocations(locations), + isExported(isExported), accessLevel(accessLevel) {} void addImportLocation(ImportDiagnosticLocationInfo location) { importLocations.push_back(location); } - /// Buffer, line & column number of the import statement - SmallVector importLocations; /// Imported module string. e.g. "Foo.Bar" in 'import Foo.Bar' std::string importIdentifier; + /// Buffer, line & column number of the import statement + SmallVector importLocations; /// Is this an @_exported import bool isExported; + /// Access level of this dependency + AccessLevel accessLevel; }; /// Base class for the variant storage of ModuleDependencyInfo. @@ -942,6 +947,7 @@ class ModuleDependencyInfo { /// Add a dependency on the given module, if it was not already in the set. void addOptionalModuleImport(StringRef module, bool isExported, + AccessLevel accessLevel, llvm::StringSet<> *alreadyAddedModules = nullptr); /// Add all of the module imports in the given source @@ -952,12 +958,14 @@ class ModuleDependencyInfo { /// Add a dependency on the given module, if it was not already in the set. void addModuleImport(ImportPath::Module module, bool isExported, + AccessLevel accessLevel, llvm::StringSet<> *alreadyAddedModules = nullptr, const SourceManager *sourceManager = nullptr, SourceLoc sourceLocation = SourceLoc()); /// Add a dependency on the given module, if it was not already in the set. void addModuleImport(StringRef module, bool isExported, + AccessLevel accessLevel, llvm::StringSet<> *alreadyAddedModules = nullptr, const SourceManager *sourceManager = nullptr, SourceLoc sourceLocation = SourceLoc()); diff --git a/include/swift/DependencyScan/SerializedModuleDependencyCacheFormat.h b/include/swift/DependencyScan/SerializedModuleDependencyCacheFormat.h index 8a5c4d074cd90..f48f0e4e1ec64 100644 --- a/include/swift/DependencyScan/SerializedModuleDependencyCacheFormat.h +++ b/include/swift/DependencyScan/SerializedModuleDependencyCacheFormat.h @@ -67,6 +67,9 @@ using IsExportedImport = BCFixed<1>; using LineNumberField = BCFixed<32>; using ColumnNumberField = BCFixed<32>; +/// Access level of an import +using AccessLevelField = BCFixed<8>; + /// Arrays of various identifiers, distinguished for readability using IdentifierIDArryField = llvm::BCArray; using ModuleIDArryField = llvm::BCArray; @@ -192,7 +195,8 @@ using ImportStatementLayout = LineNumberField, // lineNumber ColumnNumberField, // columnNumber IsOptionalImport, // isOptional - IsExportedImport // isExported + IsExportedImport, // isExported + AccessLevelField // accessLevel >; using ImportStatementArrayLayout = BCRecordLayout; diff --git a/include/swift/Serialization/SerializedModuleLoader.h b/include/swift/Serialization/SerializedModuleLoader.h index 137a9dcd24783..3ae80b98b3193 100644 --- a/include/swift/Serialization/SerializedModuleLoader.h +++ b/include/swift/Serialization/SerializedModuleLoader.h @@ -170,8 +170,7 @@ class SerializedModuleLoaderBase : public ModuleLoader { bool isTestableImport, bool isCandidateForTextualModule); struct BinaryModuleImports { - llvm::StringSet<> moduleImports; - llvm::StringSet<> exportedModules; + std::vector moduleImports; std::string headerImport; }; diff --git a/lib/AST/ModuleDependencies.cpp b/lib/AST/ModuleDependencies.cpp index dbd5e44c65ffb..dacb4198b4c9a 100644 --- a/lib/AST/ModuleDependencies.cpp +++ b/lib/AST/ModuleDependencies.cpp @@ -116,14 +116,34 @@ bool ModuleDependencyInfo::isTestableImport(StringRef moduleName) const { } void ModuleDependencyInfo::addOptionalModuleImport( - StringRef module, bool isExported, llvm::StringSet<> *alreadyAddedModules) { - if (!alreadyAddedModules || alreadyAddedModules->insert(module).second) - storage->optionalModuleImports.push_back({module.str(), isExported}); + StringRef module, bool isExported, AccessLevel accessLevel, + llvm::StringSet<> *alreadyAddedModules) { + + if (alreadyAddedModules && alreadyAddedModules->contains(module)) { + // Find a prior import of this module and add import location + // and adjust whether or not this module is ever imported as exported + // as well as the access level + for (auto &existingImport : storage->optionalModuleImports) { + if (existingImport.importIdentifier == module) { + existingImport.isExported |= isExported; + existingImport.accessLevel = std::max(existingImport.accessLevel, + accessLevel); + break; + } + } + } else { + if (alreadyAddedModules) + alreadyAddedModules->insert(module); + + storage->optionalModuleImports.push_back( + {module.str(), isExported, accessLevel}); + } } void ModuleDependencyInfo::addModuleImport( - StringRef module, bool isExported, llvm::StringSet<> *alreadyAddedModules, - const SourceManager *sourceManager, SourceLoc sourceLocation) { + StringRef module, bool isExported, AccessLevel accessLevel, + llvm::StringSet<> *alreadyAddedModules, const SourceManager *sourceManager, + SourceLoc sourceLocation) { auto scannerImportLocToDiagnosticLocInfo = [&sourceManager](SourceLoc sourceLocation) { auto lineAndColumnNumbers = @@ -138,13 +158,16 @@ void ModuleDependencyInfo::addModuleImport( if (alreadyAddedModules && alreadyAddedModules->contains(module)) { // Find a prior import of this module and add import location // and adjust whether or not this module is ever imported as exported + // as well as the access level for (auto &existingImport : storage->moduleImports) { if (existingImport.importIdentifier == module) { if (validSourceLocation) { existingImport.addImportLocation( - scannerImportLocToDiagnosticLocInfo(sourceLocation)); + scannerImportLocToDiagnosticLocInfo(sourceLocation)); } existingImport.isExported |= isExported; + existingImport.accessLevel = std::max(existingImport.accessLevel, + accessLevel); break; } } @@ -154,16 +177,18 @@ void ModuleDependencyInfo::addModuleImport( if (validSourceLocation) storage->moduleImports.push_back(ScannerImportStatementInfo( - module.str(), isExported, scannerImportLocToDiagnosticLocInfo(sourceLocation))); + module.str(), isExported, accessLevel, + scannerImportLocToDiagnosticLocInfo(sourceLocation))); else storage->moduleImports.push_back( - ScannerImportStatementInfo(module.str(), isExported)); + ScannerImportStatementInfo(module.str(), isExported, accessLevel)); } } void ModuleDependencyInfo::addModuleImport( - ImportPath::Module module, bool isExported, llvm::StringSet<> *alreadyAddedModules, - const SourceManager *sourceManager, SourceLoc sourceLocation) { + ImportPath::Module module, bool isExported, AccessLevel accessLevel, + llvm::StringSet<> *alreadyAddedModules, const SourceManager *sourceManager, + SourceLoc sourceLocation) { std::string ImportedModuleName = module.front().Item.str().str(); auto submodulePath = module.getSubmodulePath(); if (submodulePath.size() > 0 && !submodulePath[0].Item.empty()) { @@ -172,11 +197,12 @@ void ModuleDependencyInfo::addModuleImport( // module named "Foo_Private". ClangImporter has special support for this. if (submoduleComponent.Item.str() == "Private") addOptionalModuleImport(ImportedModuleName + "_Private", + isExported, accessLevel, alreadyAddedModules); } - addModuleImport(ImportedModuleName, isExported, alreadyAddedModules, - sourceManager, sourceLocation); + addModuleImport(ImportedModuleName, isExported, accessLevel, + alreadyAddedModules, sourceManager, sourceLocation); } void ModuleDependencyInfo::addModuleImports( @@ -205,6 +231,7 @@ void ModuleDependencyInfo::addModuleImports( continue; addModuleImport(realPath, importDecl->isExported(), + importDecl->getAccessLevel(), &alreadyAddedModules, sourceManager, importDecl->getLoc()); diff --git a/lib/ClangImporter/ClangModuleDependencyScanner.cpp b/lib/ClangImporter/ClangModuleDependencyScanner.cpp index cc054f3de524a..4e63580f3cce4 100644 --- a/lib/ClangImporter/ClangModuleDependencyScanner.cpp +++ b/lib/ClangImporter/ClangModuleDependencyScanner.cpp @@ -191,7 +191,8 @@ ModuleDependencyVector ClangImporter::bridgeClangModuleDependencies( // FIXME: This assumes, conservatively, that all Clang module imports // are exported. We need to fix this once the clang scanner gains the appropriate // API to query this. - dependencies.addModuleImport(moduleName.ModuleName, /* isExported */ true, &alreadyAddedModules); + dependencies.addModuleImport(moduleName.ModuleName, /* isExported */ true, + AccessLevel::Public, &alreadyAddedModules); // It is safe to assume that all dependencies of a Clang module are Clang modules. directDependencyIDs.push_back({moduleName.ModuleName, ModuleDependencyKind::Clang}); } diff --git a/lib/DependencyScan/ModuleDependencyCacheSerialization.cpp b/lib/DependencyScan/ModuleDependencyCacheSerialization.cpp index 1ca3d8e444ad2..d5817397c0902 100644 --- a/lib/DependencyScan/ModuleDependencyCacheSerialization.cpp +++ b/lib/DependencyScan/ModuleDependencyCacheSerialization.cpp @@ -415,9 +415,11 @@ bool ModuleDependenciesCacheDeserializer::readGraph( unsigned importIdentifierID, bufferIdentifierID; unsigned lineNumber, columnNumber; bool isOptional, isExported; + uint8_t rawAccessLevel; ImportStatementLayout::readRecord(Scratch, importIdentifierID, bufferIdentifierID, lineNumber, - columnNumber, isOptional, isExported); + columnNumber, isOptional, isExported, + rawAccessLevel); auto importIdentifier = getIdentifier(importIdentifierID); if (!importIdentifier) llvm::report_fatal_error("Bad import statement info: no import name"); @@ -428,10 +430,10 @@ bool ModuleDependenciesCacheDeserializer::readGraph( "Bad import statement info: no buffer identifier"); if (bufferIdentifier->empty()) ImportStatements.push_back(ScannerImportStatementInfo( - *importIdentifier, isExported)); + *importIdentifier, isExported, AccessLevel(rawAccessLevel))); else ImportStatements.push_back(ScannerImportStatementInfo( - *importIdentifier, isExported, + *importIdentifier, isExported, AccessLevel(rawAccessLevel), ScannerImportStatementInfo::ImportDiagnosticLocationInfo( *bufferIdentifier, lineNumber, columnNumber))); break; @@ -1527,7 +1529,8 @@ unsigned ModuleDependenciesCacheSerializer::writeImportStatementInfos( ImportStatementLayout::emitRecord( Out, ScratchRecord, AbbrCodes[ImportStatementLayout::Code], getIdentifier(importInfo.importIdentifier), - 0, 0, 0, isOptional, importInfo.isExported); + 0, 0, 0, isOptional, importInfo.isExported, + static_cast::type>(importInfo.accessLevel)); count++; } else { for (auto &importLoc : importInfo.importLocations) { @@ -1535,7 +1538,8 @@ unsigned ModuleDependenciesCacheSerializer::writeImportStatementInfos( Out, ScratchRecord, AbbrCodes[ImportStatementLayout::Code], getIdentifier(importInfo.importIdentifier), getIdentifier(importLoc.bufferIdentifier), importLoc.lineNumber, - importLoc.columnNumber, isOptional, importInfo.isExported); + importLoc.columnNumber, isOptional, importInfo.isExported, + static_cast::type>(importInfo.accessLevel)); count++; } } diff --git a/lib/DependencyScan/ModuleDependencyScanner.cpp b/lib/DependencyScan/ModuleDependencyScanner.cpp index fadd930d05c2d..b759c4369a365 100644 --- a/lib/DependencyScan/ModuleDependencyScanner.cpp +++ b/lib/DependencyScan/ModuleDependencyScanner.cpp @@ -544,6 +544,7 @@ ModuleDependencyScanner::getMainModuleDependencyInfo(ModuleDecl *mainModule) { case ImplicitStdlibKind::Stdlib: mainDependencies.addModuleImport("Swift", /* isExported */false, + AccessLevel::Public, &alreadyAddedModules); break; } @@ -552,6 +553,7 @@ ModuleDependencyScanner::getMainModuleDependencyInfo(ModuleDecl *mainModule) { for (const auto &import : importInfo.AdditionalUnloadedImports) { mainDependencies.addModuleImport(import.module.getModulePath(), import.options.contains(ImportFlags::Exported), + import.accessLevel, &alreadyAddedModules, &ScanASTContext.SourceMgr); } @@ -561,6 +563,7 @@ ModuleDependencyScanner::getMainModuleDependencyInfo(ModuleDecl *mainModule) { mainDependencies.addModuleImport( import.module.importedModule->getNameStr(), import.options.contains(ImportFlags::Exported), + import.accessLevel, &alreadyAddedModules); } @@ -574,6 +577,7 @@ ModuleDependencyScanner::getMainModuleDependencyInfo(ModuleDecl *mainModule) { if (importInfo.ShouldImportUnderlyingModule) { mainDependencies.addModuleImport(mainModule->getName().str(), /* isExported */ true, + AccessLevel::Public, &alreadyAddedModules); } @@ -584,6 +588,7 @@ ModuleDependencyScanner::getMainModuleDependencyInfo(ModuleDecl *mainModule) { ScanCompilerInvocation.getTBDGenOptions().embedSymbolsFromModules) { mainDependencies.addModuleImport(tbdSymbolModule, /* isExported */ false, + AccessLevel::Public, &alreadyAddedModules); } } @@ -948,7 +953,8 @@ void ModuleDependencyScanner::resolveAllClangModuleDependencies( if (importInfo.importIdentifier == ScanASTContext.Id_CxxStdlib.str()) { auto canonicalImportInfo = ScannerImportStatementInfo( - "std", importInfo.isExported, importInfo.importLocations); + "std", importInfo.isExported, importInfo.accessLevel, + importInfo.importLocations); unresolvedImports.push_back(canonicalImportInfo); unresolvedImportIdentifiers.insert( canonicalImportInfo.importIdentifier); @@ -1475,7 +1481,9 @@ void ModuleDependencyScanner::resolveCrossImportOverlayDependencies( std::for_each(newOverlays.begin(), newOverlays.end(), [&](Identifier modName) { dummyMainDependencies.addModuleImport(modName.str(), - /* isExported */ false); + /* isExported */ false, + // TODO: What is the right access level for a cross-import overlay? + AccessLevel::Public); }); // Record the dummy main module's direct dependencies. The dummy main module diff --git a/lib/Serialization/ScanningLoaders.cpp b/lib/Serialization/ScanningLoaders.cpp index 2b90236952336..28dbb3315d53e 100644 --- a/lib/Serialization/ScanningLoaders.cpp +++ b/lib/Serialization/ScanningLoaders.cpp @@ -242,6 +242,7 @@ SwiftModuleScanner::scanInterfaceFile(Twine moduleInterfacePath, for (auto import : imInfo.AdditionalUnloadedImports) { Result->addModuleImport(import.module.getModulePath(), import.options.contains(ImportFlags::Exported), + import.accessLevel, &alreadyAddedModules, &Ctx.SourceMgr); } @@ -270,6 +271,7 @@ SwiftModuleScanner::scanInterfaceFile(Twine moduleInterfacePath, if (!alreadyAddedModules.contains(requiredImport.importIdentifier)) Result->addModuleImport(requiredImport.importIdentifier, requiredImport.isExported, + requiredImport.accessLevel, &alreadyAddedModules); } } diff --git a/lib/Serialization/SerializedModuleLoader.cpp b/lib/Serialization/SerializedModuleLoader.cpp index 5862adf180171..e2389b805bc27 100644 --- a/lib/Serialization/SerializedModuleLoader.cpp +++ b/lib/Serialization/SerializedModuleLoader.cpp @@ -373,7 +373,8 @@ SerializedModuleLoaderBase::getMatchingPackageOnlyImportsOfModule( if (dotPos != std::string::npos) moduleName = moduleName.slice(0, dotPos); - importedModuleNames.push_back({moduleName.str(), dependency.isExported()}); + importedModuleNames.push_back({moduleName.str(), dependency.isExported(), + dependency.isInternalOrBelow() ? AccessLevel::Internal : AccessLevel::Public}); } return importedModuleNames; @@ -478,8 +479,7 @@ SerializedModuleLoaderBase::getImportsOfModule( const ModuleFileSharedCore &loadedModuleFile, ModuleLoadingBehavior transitiveBehavior, StringRef packageName, bool isTestableImport) { - llvm::StringSet<> importedModuleNames; - llvm::StringSet<> importedExportedModuleNames; + std::vector moduleImports; std::string importedHeader = ""; for (const auto &dependency : loadedModuleFile.getDependencies()) { if (dependency.isHeader()) { @@ -513,13 +513,13 @@ SerializedModuleLoaderBase::getImportsOfModule( if (moduleName == Ctx.Id_CxxStdlib.str()) moduleName = "std"; - importedModuleNames.insert(moduleName); - if (dependency.isExported()) - importedExportedModuleNames.insert(moduleName); + moduleImports.push_back(ScannerImportStatementInfo( + moduleName.str(), dependency.isExported(), + dependency.isInternalOrBelow() ? AccessLevel::Internal + : AccessLevel::Public)); } - return SerializedModuleLoaderBase::BinaryModuleImports{importedModuleNames, - importedExportedModuleNames, + return SerializedModuleLoaderBase::BinaryModuleImports{moduleImports, importedHeader}; } @@ -601,51 +601,33 @@ SerializedModuleLoaderBase::scanModuleFile(Twine modulePath, bool isFramework, getImportsOfModule(*loadedModuleFile, ModuleLoadingBehavior::Optional, Ctx.LangOpts.PackageName, isTestableImport); - auto importedModuleSet = binaryModuleImports->moduleImports; - std::vector moduleImports; - moduleImports.reserve(importedModuleSet.size()); - llvm::transform(importedModuleSet.keys(), std::back_inserter(moduleImports), - [&binaryModuleImports](llvm::StringRef N) { - return ScannerImportStatementInfo( - N.str(), - binaryModuleImports->exportedModules.contains(N)); - }); - - auto importedHeader = binaryModuleImports->headerImport; - auto &importedOptionalModuleSet = binaryModuleOptionalImports->moduleImports; - auto &importedExportedOptionalModuleSet = - binaryModuleOptionalImports->exportedModules; - std::vector optionalModuleImports; - for (const auto optionalImportedModule : importedOptionalModuleSet.keys()) - if (!importedModuleSet.contains(optionalImportedModule)) - optionalModuleImports.push_back( - {optionalImportedModule.str(), - importedExportedOptionalModuleSet.contains(optionalImportedModule)}); - std::vector linkLibraries; { linkLibraries.reserve(loadedModuleFile->getLinkLibraries().size()); llvm::copy(loadedModuleFile->getLinkLibraries(), std::back_inserter(linkLibraries)); if (loadedModuleFile->isFramework()) - linkLibraries.emplace_back( - loadedModuleFile->getName(), LibraryKind::Framework, - loadedModuleFile->isStaticLibrary()); + linkLibraries.emplace_back(loadedModuleFile->getName(), + LibraryKind::Framework, + loadedModuleFile->isStaticLibrary()); } // Attempt to resolve the module's defining .swiftinterface path std::string definingModulePath = - loadedModuleFile->resolveModuleDefiningFilePath(Ctx.SearchPathOpts.getSDKPath()); + loadedModuleFile->resolveModuleDefiningFilePath( + Ctx.SearchPathOpts.getSDKPath()); - std::string userModuleVer = loadedModuleFile->getUserModuleVersion().getAsString(); + std::string userModuleVer = + loadedModuleFile->getUserModuleVersion().getAsString(); std::vector serializedSearchPaths; llvm::copy(loadedModuleFile->getSearchPaths(), std::back_inserter(serializedSearchPaths)); - + // Map the set of dependencies over to the "module dependencies". auto dependencies = ModuleDependencyInfo::forSwiftBinaryModule( - modulePath.str(), moduleDocPath, sourceInfoPath, moduleImports, - optionalModuleImports, linkLibraries, serializedSearchPaths, - importedHeader, definingModulePath, isFramework, + modulePath.str(), moduleDocPath, sourceInfoPath, + binaryModuleImports->moduleImports, + binaryModuleOptionalImports->moduleImports, linkLibraries, serializedSearchPaths, + binaryModuleImports->headerImport, definingModulePath, isFramework, loadedModuleFile->isStaticLibrary(), /*module-cache-key*/ "", userModuleVer); From c3ce91e07159bf7d0457eef871397b0c8361c277 Mon Sep 17 00:00:00 2001 From: Artem Chikin Date: Wed, 11 Jun 2025 11:45:48 -0700 Subject: [PATCH 2/2] [Dependency Scanning] Add libSwiftScan API and JSON output for source import information --- .../swift-c/DependencyScan/DependencyScan.h | 41 +++++++- .../swift/DependencyScan/DependencyScanImpl.h | 15 ++- lib/DependencyScan/DependencyScanJSON.cpp | 97 ++++++++++++++++++- lib/DependencyScan/DependencyScanningTool.cpp | 7 ++ lib/DependencyScan/ScanDependencies.cpp | 37 ++++++- lib/Tooling/libSwiftScan/libSwiftScan.cpp | 31 +++++- lib/Tooling/libSwiftScan/libSwiftScan.exports | 4 + .../eliminate_unused_vfs.swift | 5 +- test/ScanDependencies/import_infos.swift | 72 ++++++++++++++ .../module_deps_cache_reuse.swift | 2 +- ...module_deps_different_paths_no_reuse.swift | 4 +- 11 files changed, 295 insertions(+), 20 deletions(-) create mode 100644 test/ScanDependencies/import_infos.swift diff --git a/include/swift-c/DependencyScan/DependencyScan.h b/include/swift-c/DependencyScan/DependencyScan.h index 93a9ec2bdc31b..223816b363661 100644 --- a/include/swift-c/DependencyScan/DependencyScan.h +++ b/include/swift-c/DependencyScan/DependencyScan.h @@ -25,7 +25,7 @@ /// SWIFTSCAN_VERSION_MINOR should increase when there are API additions. /// SWIFTSCAN_VERSION_MAJOR is intended for "major" source/ABI breaking changes. #define SWIFTSCAN_VERSION_MAJOR 2 -#define SWIFTSCAN_VERSION_MINOR 1 +#define SWIFTSCAN_VERSION_MINOR 2 SWIFTSCAN_BEGIN_DECLS @@ -49,6 +49,9 @@ typedef struct swiftscan_dependency_info_s *swiftscan_dependency_info_t; /// Opaque container to a link library info. typedef struct swiftscan_link_library_info_s *swiftscan_link_library_info_t; +/// Opaque container to an import info. +typedef struct swiftscan_import_info_s *swiftscan_import_info_t; + /// Opaque container to a macro dependency. typedef struct swiftscan_macro_dependency_s *swiftscan_macro_dependency_t; @@ -76,6 +79,18 @@ typedef struct { size_t count; } swiftscan_link_library_set_t; +/// Set of details about source imports +typedef struct { + swiftscan_import_info_t *imports; + size_t count; +} swiftscan_import_info_set_t; + +/// Set of source location infos +typedef struct { + swiftscan_source_location_t *source_locations; + size_t count; +} swiftscan_source_location_set_t; + /// Set of macro dependency typedef struct { swiftscan_macro_dependency_t *macro_dependencies; @@ -89,6 +104,15 @@ typedef enum { SWIFTSCAN_DIAGNOSTIC_SEVERITY_REMARK = 3 } swiftscan_diagnostic_severity_t; +// Must maintain consistency with swift::AccessLevel +typedef enum { + SWIFTSCAN_ACCESS_LEVEL_PRIVATE = 0, + SWIFTSCAN_ACCESS_LEVEL_FILEPRIVATE = 1, + SWIFTSCAN_ACCESS_LEVEL_INTERNAL = 2, + SWIFTSCAN_ACCESS_LEVEL_PACKAGE = 3, + SWIFTSCAN_ACCESS_LEVEL_PUBLIC = 4 +} swiftscan_access_level_t; + typedef struct { swiftscan_diagnostic_info_t *diagnostics; size_t count; @@ -148,10 +172,23 @@ swiftscan_module_info_get_direct_dependencies(swiftscan_dependency_info_t info); SWIFTSCAN_PUBLIC swiftscan_link_library_set_t * swiftscan_module_info_get_link_libraries(swiftscan_dependency_info_t info); +SWIFTSCAN_PUBLIC swiftscan_import_info_set_t * +swiftscan_module_info_get_imports(swiftscan_dependency_info_t info); + SWIFTSCAN_PUBLIC swiftscan_module_details_t swiftscan_module_info_get_details(swiftscan_dependency_info_t info); -//=== Link Library Info Functions ------------------------------------===// +//=== Import Details Functions -------------------------------------------===// +SWIFTSCAN_PUBLIC swiftscan_source_location_set_t * +swiftscan_import_info_get_source_locations(swiftscan_import_info_t info); + +SWIFTSCAN_PUBLIC swiftscan_string_ref_t +swiftscan_import_info_get_identifier(swiftscan_import_info_t info); + +SWIFTSCAN_PUBLIC swiftscan_access_level_t +swiftscan_import_info_get_access_level(swiftscan_import_info_t info); + +//=== Link Library Info Functions ----------------------------------------===// SWIFTSCAN_PUBLIC swiftscan_string_ref_t swiftscan_link_library_info_get_link_name( swiftscan_link_library_info_t info); diff --git a/include/swift/DependencyScan/DependencyScanImpl.h b/include/swift/DependencyScan/DependencyScanImpl.h index 66f95615269b6..ec70611550fcc 100644 --- a/include/swift/DependencyScan/DependencyScanImpl.h +++ b/include/swift/DependencyScan/DependencyScanImpl.h @@ -63,6 +63,9 @@ struct swiftscan_dependency_info_s { /// The list of link libraries for this module. swiftscan_link_library_set_t *link_libraries; + /// The list of source import infos. + swiftscan_import_info_set_t *imports; + /// Specific details of a particular kind of module. swiftscan_module_details_t details; }; @@ -74,10 +77,16 @@ struct swiftscan_link_library_info_s { bool forceLoad; }; +struct swiftscan_import_info_s { + swiftscan_string_ref_t import_identifier; + swiftscan_source_location_set_t *source_locations; + swiftscan_access_level_t access_level; +}; + struct swiftscan_macro_dependency_s { - swiftscan_string_ref_t moduleName; - swiftscan_string_ref_t libraryPath; - swiftscan_string_ref_t executablePath; + swiftscan_string_ref_t module_name; + swiftscan_string_ref_t library_path; + swiftscan_string_ref_t executable_path; }; /// Swift modules to be built from a module interface, may have a bridging diff --git a/lib/DependencyScan/DependencyScanJSON.cpp b/lib/DependencyScan/DependencyScanJSON.cpp index b41efc6883cca..f847bf572465e 100644 --- a/lib/DependencyScan/DependencyScanJSON.cpp +++ b/lib/DependencyScan/DependencyScanJSON.cpp @@ -77,6 +77,11 @@ void writeJSONValue(llvm::raw_ostream &out, bool value, unsigned indentLevel) { out.write_escaped(value ? "true" : "false"); } +/// Write a boolean value as JSON. +void writeJSONValue(llvm::raw_ostream &out, uint32_t value, unsigned indentLevel) { + out.write_escaped(std::to_string(value)); +} + /// Write a JSON array. template void writeJSONValue(llvm::raw_ostream &out, ArrayRef values, @@ -226,6 +231,90 @@ void writeLinkLibraries(llvm::raw_ostream &out, out << "\n"; } +void writeImportInfos(llvm::raw_ostream &out, + const swiftscan_import_info_set_t *imports, + unsigned indentLevel, bool trailingComma) { + out.indent(indentLevel * 2); + out << "\"imports\": "; + out << "[\n"; + + for (size_t i = 0; i < imports->count; ++i) { + const auto &iInfo = *imports->imports[i]; + out.indent((indentLevel + 1) * 2); + out << "{\n"; + auto entryIndentLevel = ((indentLevel + 2) * 2); + out.indent(entryIndentLevel); + out << "\"identifier\": "; + writeJSONValue(out, iInfo.import_identifier, indentLevel); + out << ",\n"; + out.indent(entryIndentLevel); + out << "\"accessLevel\": "; + switch (iInfo.access_level) { + case SWIFTSCAN_ACCESS_LEVEL_PRIVATE: + writeJSONValue(out, StringRef("private"), entryIndentLevel); + break; + case SWIFTSCAN_ACCESS_LEVEL_FILEPRIVATE: + writeJSONValue(out, StringRef("fileprivate"), entryIndentLevel); + break; + case SWIFTSCAN_ACCESS_LEVEL_INTERNAL: + writeJSONValue(out, StringRef("internal"), entryIndentLevel); + break; + case SWIFTSCAN_ACCESS_LEVEL_PACKAGE: + writeJSONValue(out, StringRef("package"), entryIndentLevel); + break; + case SWIFTSCAN_ACCESS_LEVEL_PUBLIC: + writeJSONValue(out, StringRef("public"), entryIndentLevel); + break; + } + + if (iInfo.source_locations->count) { + out << ",\n"; + out.indent(entryIndentLevel); + out << "\"importLocations\": "; + out << "[\n"; + auto slIndentLevel = ((entryIndentLevel + 4)); + for (size_t i = 0; i < iInfo.source_locations->count; ++i) { + out.indent(entryIndentLevel + 2); + out << "{\n"; + const auto &sl = *iInfo.source_locations->source_locations[i]; + out.indent(slIndentLevel); + out << "\"bufferIdentifier\": "; + writeJSONValue(out, sl.buffer_identifier, indentLevel); + out << ",\n"; + out.indent(slIndentLevel); + out << "\"linuNumber\": "; + writeJSONValue(out, sl.line_number, indentLevel); + out << ",\n"; + out.indent(slIndentLevel); + out << "\"columnNumber\": "; + writeJSONValue(out, sl.column_number, indentLevel); + out << "\n"; + out.indent(entryIndentLevel + 2); + out << "}"; + if (i != iInfo.source_locations->count - 1) + out << ","; + out << "\n"; + } + out.indent(entryIndentLevel); + out << "]\n"; + } else { + out << "\n"; + } + out.indent((indentLevel + 1) * 2); + out << "}"; + if (i != imports->count - 1) + out << ","; + out << "\n"; + } + + out.indent(indentLevel * 2); + out << "]"; + + if (trailingComma) + out << ","; + out << "\n"; +} + static void writeMacroDependencies(llvm::raw_ostream &out, const swiftscan_macro_dependency_set_t *macro_deps, @@ -243,15 +332,15 @@ writeMacroDependencies(llvm::raw_ostream &out, auto entryIndentLevel = ((indentLevel + 2) * 2); out.indent(entryIndentLevel); out << "\"moduleName\": "; - writeJSONValue(out, macroInfo.moduleName, indentLevel); + writeJSONValue(out, macroInfo.module_name, indentLevel); out << ",\n"; out.indent(entryIndentLevel); out << "\"libraryPath\": "; - writeJSONValue(out, macroInfo.libraryPath, entryIndentLevel); + writeJSONValue(out, macroInfo.library_path, entryIndentLevel); out << ",\n"; out.indent(entryIndentLevel); out << "\"executablePath\": "; - writeJSONValue(out, macroInfo.executablePath, entryIndentLevel); + writeJSONValue(out, macroInfo.executable_path, entryIndentLevel); out << "\n"; out.indent((indentLevel + 1) * 2); out << "}"; @@ -368,6 +457,8 @@ void writeJSON(llvm::raw_ostream &out, /*trailingComma=*/true); writeLinkLibraries(out, moduleInfo.link_libraries, 3, /*trailingComma=*/true); + writeImportInfos(out, moduleInfo.imports, + 3, /*trailingComma=*/true); } // Swift and Clang-specific details. out.indent(3 * 2); diff --git a/lib/DependencyScan/DependencyScanningTool.cpp b/lib/DependencyScan/DependencyScanningTool.cpp index e2651976c16da..157cefec7da64 100644 --- a/lib/DependencyScan/DependencyScanningTool.cpp +++ b/lib/DependencyScan/DependencyScanningTool.cpp @@ -238,6 +238,13 @@ static swiftscan_dependency_graph_t generateHollowDiagnosticOutput( hollowLinkLibrarySet->link_libraries = nullptr; hollowMainModuleInfo->link_libraries = hollowLinkLibrarySet; + // Empty Import set + swiftscan_import_info_set_t *hollowImportInfoSet = + new swiftscan_import_info_set_t; + hollowImportInfoSet->count = 0; + hollowImportInfoSet->imports = nullptr; + hollowMainModuleInfo->imports = hollowImportInfoSet; + // Populate the diagnostic info hollowResult->diagnostics = mapCollectedDiagnosticsForOutput(&ScanDiagnosticConsumer); diff --git a/lib/DependencyScan/ScanDependencies.cpp b/lib/DependencyScan/ScanDependencies.cpp index 054b237ba82cd..42526995457da 100644 --- a/lib/DependencyScan/ScanDependencies.cpp +++ b/lib/DependencyScan/ScanDependencies.cpp @@ -747,10 +747,10 @@ static swiftscan_macro_dependency_set_t *createMacroDependencySet( unsigned SI = 0; for (auto &entry : macroDeps) { set->macro_dependencies[SI] = new swiftscan_macro_dependency_s; - set->macro_dependencies[SI]->moduleName = create_clone(entry.first.c_str()); - set->macro_dependencies[SI]->libraryPath = + set->macro_dependencies[SI]->module_name = create_clone(entry.first.c_str()); + set->macro_dependencies[SI]->library_path = create_clone(entry.second.LibraryPath.c_str()); - set->macro_dependencies[SI]->executablePath = + set->macro_dependencies[SI]->executable_path = create_clone(entry.second.ExecutablePath.c_str()); ++ SI; } @@ -976,6 +976,37 @@ generateFullDependencyGraph(const CompilerInstance &instance, linkLibrarySet->link_libraries[i] = llInfo; } moduleInfo->link_libraries = linkLibrarySet; + + // Create source import infos set for this module + auto imports = moduleDependencyInfo.getModuleImports(); + swiftscan_import_info_set_t *importInfoSet = + new swiftscan_import_info_set_t; + importInfoSet->count = imports.size(); + importInfoSet->imports = new swiftscan_import_info_t[importInfoSet->count]; + for (size_t i = 0; i < imports.size(); ++i) { + const auto &ii = imports[i]; + swiftscan_import_info_s *iInfo = new swiftscan_import_info_s; + iInfo->import_identifier = create_clone(ii.importIdentifier.c_str()); + iInfo->access_level = static_cast(ii.accessLevel); + + const auto &sourceLocations = ii.importLocations; + swiftscan_source_location_set_t *sourceLocSet = + new swiftscan_source_location_set_t; + sourceLocSet->count = sourceLocations.size(); + sourceLocSet->source_locations = + new swiftscan_source_location_t[sourceLocSet->count]; + for (size_t j = 0; j < sourceLocations.size(); ++j) { + const auto &sl = sourceLocations[j]; + swiftscan_source_location_s *slInfo = new swiftscan_source_location_s; + slInfo->buffer_identifier = create_clone(sl.bufferIdentifier.c_str()); + slInfo->line_number = sl.lineNumber; + slInfo->column_number = sl.columnNumber; + sourceLocSet->source_locations[j] = slInfo; + } + iInfo->source_locations = sourceLocSet; + importInfoSet->imports[i] = iInfo; + } + moduleInfo->imports = importInfoSet; } swiftscan_dependency_graph_t result = new swiftscan_dependency_graph_s; diff --git a/lib/Tooling/libSwiftScan/libSwiftScan.cpp b/lib/Tooling/libSwiftScan/libSwiftScan.cpp index e51f8191b8c41..e3c672fdd3cc7 100644 --- a/lib/Tooling/libSwiftScan/libSwiftScan.cpp +++ b/lib/Tooling/libSwiftScan/libSwiftScan.cpp @@ -35,9 +35,9 @@ void swiftscan_macro_dependency_dispose( return; for (unsigned i = 0; i < macro->count; ++i) { - swiftscan_string_dispose(macro->macro_dependencies[i]->moduleName); - swiftscan_string_dispose(macro->macro_dependencies[i]->libraryPath); - swiftscan_string_dispose(macro->macro_dependencies[i]->executablePath); + swiftscan_string_dispose(macro->macro_dependencies[i]->module_name); + swiftscan_string_dispose(macro->macro_dependencies[i]->library_path); + swiftscan_string_dispose(macro->macro_dependencies[i]->executable_path); delete macro->macro_dependencies[i]; } delete[] macro->macro_dependencies; @@ -240,12 +240,17 @@ swiftscan_link_library_set_t *swiftscan_module_info_get_link_libraries( return info->link_libraries; } +swiftscan_import_info_set_t *swiftscan_module_info_get_imports( + swiftscan_dependency_info_t info) { + return info->imports; +} + swiftscan_module_details_t swiftscan_module_info_get_details(swiftscan_dependency_info_t info) { return info->details; } -//=== Link Library Info query APIs -----------------------------------===// +//=== Link Library Info query APIs ---------------------------------------===// swiftscan_string_ref_t swiftscan_link_library_info_get_link_name(swiftscan_link_library_info_t info) { @@ -267,7 +272,23 @@ swiftscan_link_library_info_get_should_force_load(swiftscan_link_library_info_t return info->forceLoad; } -//=== Swift Textual Module Details query APIs -----------------------------===// +//=== Import Details Query APIs ------------------------------------------===// +swiftscan_source_location_set_t * +swiftscan_import_info_get_source_locations(swiftscan_import_info_t info) { + return info->source_locations; +} + +swiftscan_string_ref_t +swiftscan_import_info_get_identifier(swiftscan_import_info_t info) { + return info->import_identifier; +} + +swiftscan_access_level_t +swiftscan_import_info_get_access_level(swiftscan_import_info_t info) { + return info->access_level; +} + +//=== Swift Textual Module Details query APIs ----------------------------===// swiftscan_dependency_info_kind_t swiftscan_module_detail_get_kind(swiftscan_module_details_t details) { diff --git a/lib/Tooling/libSwiftScan/libSwiftScan.exports b/lib/Tooling/libSwiftScan/libSwiftScan.exports index 540c0d723baf7..9eb60935f12fc 100644 --- a/lib/Tooling/libSwiftScan/libSwiftScan.exports +++ b/lib/Tooling/libSwiftScan/libSwiftScan.exports @@ -5,11 +5,15 @@ swiftscan_link_library_info_get_link_name swiftscan_link_library_info_get_is_static swiftscan_link_library_info_get_is_framework swiftscan_link_library_info_get_should_force_load +swiftscan_import_info_get_source_locations +swiftscan_import_info_get_identifier +swiftscan_import_info_get_access_level swiftscan_module_info_get_module_name swiftscan_module_info_get_module_path swiftscan_module_info_get_source_files swiftscan_module_info_get_direct_dependencies swiftscan_module_info_get_link_libraries +swiftscan_module_info_get_imports swiftscan_module_info_get_details swiftscan_module_detail_get_kind swiftscan_swift_textual_detail_get_module_interface_path diff --git a/test/ScanDependencies/eliminate_unused_vfs.swift b/test/ScanDependencies/eliminate_unused_vfs.swift index 854d6d290139e..d59fb245b28d5 100644 --- a/test/ScanDependencies/eliminate_unused_vfs.swift +++ b/test/ScanDependencies/eliminate_unused_vfs.swift @@ -75,7 +75,8 @@ import F /// Check that the dependency swift module hashes are identical when the vfs overlays are ignored. // MOD-HASH: "mainModuleName": "deps", // MOD-HASH: "linkLibraries": [], -// MOD-HASH-NEXT: "details": { +// MOD-HASH: "imports": [ +// MOD-HASH: "details": { // MOD-HASH-NEXT: "swift": { // MOD-HASH-NEXT: "moduleInterfacePath": "{{.*}}{{/|\\}}F.swiftinterface", // MOD-HASH: "commandLine": [ @@ -86,6 +87,7 @@ import F // MOD-HASH: ], // MOD-HASH: "mainModuleName": "deps1", // MOD-HASH: "linkLibraries": [], +// MOD-HASH: "imports": [ // MOD-HASH: "details": { // MOD-HASH-NEXT: "swift": { // MOD-HASH-NEXT: "moduleInterfacePath": "{{.*}}{{/|\\}}F.swiftinterface", @@ -97,6 +99,7 @@ import F // MOD-HASH: ], // MOD-HASH: "mainModuleName": "deps2", // MOD-HASH: "linkLibraries": [], +// MOD-HASH: "imports": [ // MOD-HASH: "details": { // MOD-HASH-NEXT: "swift": { // MOD-HASH-NEXT: "moduleInterfacePath": "{{.*}}{{/|\\}}F.swiftinterface", diff --git a/test/ScanDependencies/import_infos.swift b/test/ScanDependencies/import_infos.swift new file mode 100644 index 0000000000000..021fd84783943 --- /dev/null +++ b/test/ScanDependencies/import_infos.swift @@ -0,0 +1,72 @@ +// RUN: %empty-directory(%t) +// RUN: %empty-directory(%t/module-cache) + +// RUN: %target-swift-frontend -scan-dependencies -module-cache-path %t/module-cache %s -o %t/deps.json -I %S/Inputs/CHeaders -I %S/Inputs/Swift -disable-implicit-string-processing-module-import -disable-implicit-concurrency-module-import +// RUN: %validate-json %t/deps.json | %FileCheck %s + +// CHECK: "mainModuleName": "deps" +// CHECK: "swift": "deps" +// CHECK: "modulePath": "deps.swiftmodule" + +// CHECK: "imports": [ +// CHECK-NEXT: { +// CHECK-NEXT: "identifier": "Swift" +// CHECK-NEXT: "accessLevel": "public" +// CHECK-NEXT: } +// CHECK-NEXT: { +// CHECK-NEXT: "identifier": "SwiftOnoneSupport" +// CHECK-NEXT: "accessLevel": "public" +// CHECK-NEXT: } +// CHECK-NEXT: { +// CHECK-NEXT: "identifier": "C" +// CHECK-NEXT: "accessLevel": "internal" +// CHECK-NEXT: "importLocations": [ +// CHECK-NEXT: { +// CHECK-NEXT: "bufferIdentifier": "{{.*}}import_infos.swift" +// CHECK-NEXT: "linuNumber": 65 +// CHECK-NEXT: "columnNumber": 17 +// CHECK-NEXT: } +// CHECK-NEXT: { +// CHECK-NEXT: "bufferIdentifier": "{{.*}}import_infos.swift" +// CHECK-NEXT: "linuNumber": 72 +// CHECK-NEXT: "columnNumber": 16 +// CHECK-NEXT: } +// CHECK-NEXT: ] +// CHECK-NEXT: } +// CHECK-NEXT: { +// CHECK-NEXT: "identifier": "E" +// CHECK-NEXT: "accessLevel": "public" +// CHECK-NEXT: "importLocations": [ +// CHECK-NEXT: { +// CHECK-NEXT: "bufferIdentifier": "{{.*}}import_infos.swift" +// CHECK-NEXT: "linuNumber": 67 +// CHECK-NEXT: "columnNumber": 15 +// CHECK-NEXT: } +// CHECK-NEXT: { +// CHECK-NEXT: "bufferIdentifier": "{{.*}}import_infos.swift" +// CHECK-NEXT: "linuNumber": 68 +// CHECK-NEXT: "columnNumber": 8 +// CHECK-NEXT: } +// CHECK-NEXT: ] +// CHECK-NEXT: } +// CHECK-NEXT: { +// CHECK-NEXT: "identifier": "G" +// CHECK-NEXT: "accessLevel": "fileprivate" +// CHECK-NEXT: "importLocations": [ +// CHECK-NEXT: { +// CHECK-NEXT: "bufferIdentifier": "{{.*}}import_infos.swift" +// CHECK-NEXT: "linuNumber": 70 +// CHECK-NEXT: "columnNumber": 20 +// CHECK-NEXT: } +// CHECK-NEXT: ] +// CHECK-NEXT: } +// CHECK-NEXT: ] + +internal import C + +public import E +import E + +fileprivate import G + +private import C diff --git a/test/ScanDependencies/module_deps_cache_reuse.swift b/test/ScanDependencies/module_deps_cache_reuse.swift index fc11cfadb2660..2d2d8eba9a45a 100644 --- a/test/ScanDependencies/module_deps_cache_reuse.swift +++ b/test/ScanDependencies/module_deps_cache_reuse.swift @@ -128,7 +128,7 @@ import SubE // CHECK: ], // CHECK-NEXT: "linkLibraries": [ // CHECK-NEXT: ], -// CHECK-NEXT: "details": { +// CHECK: "details": { // CHECK: "commandLine": [ // CHECK: "-compile-module-from-interface" diff --git a/test/ScanDependencies/module_deps_different_paths_no_reuse.swift b/test/ScanDependencies/module_deps_different_paths_no_reuse.swift index f0d76411b46d0..a535a68619e47 100644 --- a/test/ScanDependencies/module_deps_different_paths_no_reuse.swift +++ b/test/ScanDependencies/module_deps_different_paths_no_reuse.swift @@ -29,7 +29,7 @@ import A // CHECK-INITIAL-SCAN: ], // CHECK-INITIAL-SCAN-NEXT: "linkLibraries": [ // CHECK-INITIAL-SCAN-NEXT: ], -// CHECK-INITIAL-SCAN-NEXT: "details": { +// CHECK-INITIAL-SCAN: "details": { // CHECK-INITIAL-SCAN-NEXT: "swift": { // CHECK-INITIAL-SCAN-NEXT: "moduleInterfacePath": "{{.*}}/Swift/A.swiftinterface", @@ -43,6 +43,6 @@ import A // CHECK-DIFFERENT: ], // CHECK-DIFFERENT-NEXT: "linkLibraries": [ // CHECK-DIFFERENT-NEXT: ], -// CHECK-DIFFERENT-NEXT: "details": { +// CHECK-DIFFERENT: "details": { // CHECK-DIFFERENT-NEXT: "swift": { // CHECK-DIFFERENT-NEXT: "moduleInterfacePath": "{{.*}}/SwiftDifferent/A.swiftinterface",