Skip to content

[Dependency Scanning] Keep track of each imported module's access control #82169

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Jun 12, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 39 additions & 2 deletions include/swift-c/DependencyScan/DependencyScan.h
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand All @@ -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;

Expand Down Expand Up @@ -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;
Expand All @@ -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;
Expand Down Expand Up @@ -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);
Expand Down
26 changes: 17 additions & 9 deletions include/swift/AST/ModuleDependencies.h
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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
Expand All @@ -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());
Expand Down
15 changes: 12 additions & 3 deletions include/swift/DependencyScan/DependencyScanImpl.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;
};
Expand All @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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>;
Expand Down Expand Up @@ -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>;
Expand Down
3 changes: 1 addition & 2 deletions include/swift/Serialization/SerializedModuleLoader.h
Original file line number Diff line number Diff line change
Expand Up @@ -170,8 +170,7 @@ class SerializedModuleLoaderBase : public ModuleLoader {
bool isTestableImport, bool isCandidateForTextualModule);

struct BinaryModuleImports {
llvm::StringSet<> moduleImports;
llvm::StringSet<> exportedModules;
std::vector<ScannerImportStatementInfo> moduleImports;
std::string headerImport;
};

Expand Down
51 changes: 39 additions & 12 deletions lib/AST/ModuleDependencies.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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 =
Expand All @@ -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;
}
}
Expand All @@ -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()) {
Expand All @@ -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(
Expand Down Expand Up @@ -205,6 +231,7 @@ void ModuleDependencyInfo::addModuleImports(
continue;

addModuleImport(realPath, importDecl->isExported(),
importDecl->getAccessLevel(),
&alreadyAddedModules, sourceManager,
importDecl->getLoc());

Expand Down
3 changes: 2 additions & 1 deletion lib/ClangImporter/ClangModuleDependencyScanner.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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});
}
Expand Down
Loading