[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.
This commit is contained in:
Artem Chikin
2025-06-10 16:05:54 -07:00
parent 43d8b13cf1
commit 1c9b864738
9 changed files with 105 additions and 70 deletions

View File

@@ -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<ImportDiagnosticLocationInfo, 4> 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<ImportDiagnosticLocationInfo, 4> importLocations;
/// Imported module string. e.g. "Foo.Bar" in 'import Foo.Bar'
std::string importIdentifier;
/// Buffer, line & column number of the import statement
SmallVector<ImportDiagnosticLocationInfo, 4> 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 @@ public:
/// 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 @@ public:
/// 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());

View File

@@ -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<IdentifierIDField>;
using ModuleIDArryField = llvm::BCArray<IdentifierIDField>;
@@ -192,7 +195,8 @@ using ImportStatementLayout =
LineNumberField, // lineNumber
ColumnNumberField, // columnNumber
IsOptionalImport, // isOptional
IsExportedImport // isExported
IsExportedImport, // isExported
AccessLevelField // accessLevel
>;
using ImportStatementArrayLayout =
BCRecordLayout<IMPORT_STATEMENT_ARRAY_NODE, IdentifierIDArryField>;

View File

@@ -170,8 +170,7 @@ protected:
bool isTestableImport, bool isCandidateForTextualModule);
struct BinaryModuleImports {
llvm::StringSet<> moduleImports;
llvm::StringSet<> exportedModules;
std::vector<ScannerImportStatementInfo> moduleImports;
std::string headerImport;
};

View File

@@ -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());

View File

@@ -192,7 +192,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});
}

View File

@@ -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<std::underlying_type<AccessLevel>::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<std::underlying_type<AccessLevel>::type>(importInfo.accessLevel));
count++;
}
}

View File

@@ -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

View File

@@ -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);
}
}

View File

@@ -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<ScannerImportStatementInfo> 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<ScannerImportStatementInfo> 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<ScannerImportStatementInfo> optionalModuleImports;
for (const auto optionalImportedModule : importedOptionalModuleSet.keys())
if (!importedModuleSet.contains(optionalImportedModule))
optionalModuleImports.push_back(
{optionalImportedModule.str(),
importedExportedOptionalModuleSet.contains(optionalImportedModule)});
std::vector<LinkLibrary> 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<serialization::SearchPath> 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);