Skip to content

Commit fbac545

Browse files
authored
Merge pull request #64014 from xymus/access-level-import
[Sema] Diagnose exportability of decls limited by a non-public import
2 parents 0bb5425 + 75255d1 commit fbac545

11 files changed

+1233
-123
lines changed

include/swift/AST/DiagnosticsSema.def

+3
Original file line numberDiff line numberDiff line change
@@ -2068,6 +2068,9 @@ ERROR(access_level_on_import_unsupported, none,
20682068
ERROR(access_level_conflict_with_exported,none,
20692069
"'%0' is incompatible with %1; it can only be applied to public imports",
20702070
(DeclAttribute, DeclAttribute))
2071+
NOTE(module_imported_here,none,
2072+
"module %0 imported as '%select{private|fileprivate|internal|package|%ERROR|%ERROR}1' here",
2073+
(Identifier, AccessLevel))
20712074

20722075
// Opaque return types
20732076
ERROR(opaque_type_invalid_constraint,none,

include/swift/AST/SourceFile.h

+7
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,9 @@ enum class RestrictedImportKind {
4545
None // No restriction, i.e. the module is imported publicly.
4646
};
4747

48+
/// Import that limits the access level of imported entities.
49+
using ImportAccessLevel = Optional<AttributedImport<ImportedModule>>;
50+
4851
/// A file containing Swift source code.
4952
///
5053
/// This is a .swift or .sil file (or a virtual file, such as the contents of
@@ -382,6 +385,10 @@ class SourceFile final : public FileUnit {
382385
/// Get the most permissive restriction applied to the imports of \p module.
383386
RestrictedImportKind getRestrictedImportKind(const ModuleDecl *module) const;
384387

388+
/// Return the import of \p targetModule from this file with the most
389+
/// permissive access level.
390+
ImportAccessLevel getImportAccessLevel(const ModuleDecl *targetModule) const;
391+
385392
/// Find all SPI names imported from \p importedModule by this file,
386393
/// collecting the identifiers in \p spiGroups.
387394
virtual void

lib/AST/Module.cpp

+23
Original file line numberDiff line numberDiff line change
@@ -2944,6 +2944,29 @@ RestrictedImportKind SourceFile::getRestrictedImportKind(const ModuleDecl *modul
29442944
return importKind;
29452945
}
29462946

2947+
ImportAccessLevel
2948+
SourceFile::getImportAccessLevel(const ModuleDecl *targetModule) const {
2949+
assert(Imports.hasValue());
2950+
2951+
// Leave it to the caller to avoid calling this service for a self import.
2952+
// We want to return AccessLevel::Public, but there's no import site to return.
2953+
assert(targetModule != getParentModule() &&
2954+
"getImportAccessLevel doesn't support checking for a self-import");
2955+
2956+
auto &imports = getASTContext().getImportCache();
2957+
ImportAccessLevel restrictiveImport = None;
2958+
2959+
for (auto &import : *Imports) {
2960+
if ((!restrictiveImport.has_value() ||
2961+
import.accessLevel > restrictiveImport->accessLevel) &&
2962+
imports.isImportedBy(targetModule, import.module.importedModule)) {
2963+
restrictiveImport = import;
2964+
}
2965+
}
2966+
2967+
return restrictiveImport;
2968+
}
2969+
29472970
bool ModuleDecl::isImportedImplementationOnly(const ModuleDecl *module) const {
29482971
if (module == this) return false;
29492972

lib/Sema/TypeAccessScopeChecker.h

+27-4
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ class TypeAccessScopeChecker {
3131
bool TreatUsableFromInlineAsPublic;
3232

3333
Optional<AccessScope> Scope = AccessScope::getPublic();
34+
ImportAccessLevel ImportRestriction = None;
3435

3536
TypeAccessScopeChecker(const DeclContext *useDC,
3637
bool treatUsableFromInlineAsPublic)
@@ -43,21 +44,41 @@ class TypeAccessScopeChecker {
4344

4445
auto AS = VD->getFormalAccessScope(File, TreatUsableFromInlineAsPublic);
4546
Scope = Scope->intersectWith(AS);
47+
48+
auto targetModule = VD->getDeclContext()->getParentModule();
49+
if (targetModule != File->getParentModule()) {
50+
auto localImportRestriction = File->getImportAccessLevel(targetModule);
51+
52+
if (localImportRestriction.has_value() &&
53+
(!ImportRestriction.has_value() ||
54+
localImportRestriction.value().accessLevel <
55+
ImportRestriction.value().accessLevel)) {
56+
ImportRestriction = localImportRestriction;
57+
}
58+
}
59+
4660
return Scope.has_value();
4761
}
4862

4963
public:
50-
static Optional<AccessScope>
64+
65+
struct Result {
66+
Optional<AccessScope> Scope;
67+
ImportAccessLevel Import;
68+
};
69+
70+
static Result
5171
getAccessScope(TypeRepr *TR, const DeclContext *useDC,
5272
bool treatUsableFromInlineAsPublic = false) {
5373
TypeAccessScopeChecker checker(useDC, treatUsableFromInlineAsPublic);
5474
TR->walk(TypeReprIdentFinder([&](const IdentTypeRepr *typeRepr) {
5575
return checker.visitDecl(typeRepr->getBoundDecl());
5676
}));
57-
return checker.Scope;
77+
return {checker.Scope,
78+
checker.ImportRestriction};
5879
}
5980

60-
static Optional<AccessScope>
81+
static Result
6182
getAccessScope(Type T, const DeclContext *useDC,
6283
bool treatUsableFromInlineAsPublic = false) {
6384
TypeAccessScopeChecker checker(useDC, treatUsableFromInlineAsPublic);
@@ -66,7 +87,9 @@ class TypeAccessScopeChecker {
6687
return TypeWalker::Action::Continue;
6788
return TypeWalker::Action::Stop;
6889
}));
69-
return checker.Scope;
90+
91+
return {checker.Scope,
92+
checker.ImportRestriction};
7093
}
7194
};
7295

0 commit comments

Comments
 (0)