sema: diagnose importation from disallowed modules

This commit is contained in:
Xi Ge
2022-11-25 22:22:50 -08:00
parent 67bbab7e02
commit c3db946517
5 changed files with 54 additions and 0 deletions

View File

@@ -984,6 +984,10 @@ WARNING(module_not_compiled_with_library_evolution,none,
"using it means binary compatibility for %1 can't be guaranteed", "using it means binary compatibility for %1 can't be guaranteed",
(Identifier, Identifier)) (Identifier, Identifier))
ERROR(module_allowable_client_violation,none,
"module %0 doesn't allow importation from module %1",
(Identifier, Identifier))
REMARK(cross_import_added,none, REMARK(cross_import_added,none,
"import of %0 and %1 triggered a cross-import of %2", "import of %0 and %1 triggered a cross-import of %2",
(Identifier, Identifier, Identifier)) (Identifier, Identifier, Identifier))

View File

@@ -424,6 +424,7 @@ public:
ArrayRef<Identifier> getAllowableClientNames() const { ArrayRef<Identifier> getAllowableClientNames() const {
return allowableClientNames; return allowableClientNames;
} }
bool allowImportedBy(ModuleDecl *importer) const;
private: private:
/// An array of module names that are allowed to import this one. /// An array of module names that are allowed to import this one.

View File

@@ -1923,6 +1923,18 @@ Identifier ModuleDecl::getRealName() const {
return getASTContext().getRealModuleName(getName()); return getASTContext().getRealModuleName(getName());
} }
bool ModuleDecl::allowImportedBy(ModuleDecl *importer) const {
if (allowableClientNames.empty())
return true;
for (auto id: allowableClientNames) {
if (importer->getRealName() == id)
return true;
if (importer->getABIName() == id)
return true;
}
return false;
}
Identifier ModuleDecl::getABIName() const { Identifier ModuleDecl::getABIName() const {
if (!ModuleABIName.empty()) if (!ModuleABIName.empty())
return ModuleABIName; return ModuleABIName;

View File

@@ -137,6 +137,7 @@ private:
void validateTestable(ModuleDecl *topLevelModule); void validateTestable(ModuleDecl *topLevelModule);
void validateResilience(NullablePtr<ModuleDecl> topLevelModule, void validateResilience(NullablePtr<ModuleDecl> topLevelModule,
SourceFile &SF); SourceFile &SF);
void validateAllowableClient(ModuleDecl *topLevelModule, SourceFile &SF);
/// Diagnoses an inability to import \p modulePath in this situation and, if /// Diagnoses an inability to import \p modulePath in this situation and, if
/// \p attrs is provided and has an \p attrKind, invalidates the attribute and /// \p attrs is provided and has an \p attrKind, invalidates the attribute and
@@ -621,6 +622,7 @@ void UnboundImport::validateOptions(NullablePtr<ModuleDecl> topLevelModule,
// changing behavior, but it smells funny. // changing behavior, but it smells funny.
validateTestable(top); validateTestable(top);
validatePrivate(top); validatePrivate(top);
validateAllowableClient(top, SF);
} }
validateResilience(topLevelModule, SF); validateResilience(topLevelModule, SF);
@@ -712,6 +714,19 @@ void UnboundImport::validateTestable(ModuleDecl *topLevelModule) {
diagnoseInvalidAttr(DAK_Testable, ctx.Diags, diag::module_not_testable); diagnoseInvalidAttr(DAK_Testable, ctx.Diags, diag::module_not_testable);
} }
void UnboundImport::validateAllowableClient(ModuleDecl *importee,
SourceFile &SF) {
assert(importee);
auto *importer = SF.getParentModule();
if (!importee->allowImportedBy(importer)) {
ASTContext &ctx = SF.getASTContext();
ctx.Diags.diagnose(import.module.getModulePath().front().Loc,
diag::module_allowable_client_violation,
importee->getName(),
importer->getName());
}
}
void UnboundImport::validateResilience(NullablePtr<ModuleDecl> topLevelModule, void UnboundImport::validateResilience(NullablePtr<ModuleDecl> topLevelModule,
SourceFile &SF) { SourceFile &SF) {
if (import.options.contains(ImportFlags::ImplementationOnly)) if (import.options.contains(ImportFlags::ImplementationOnly))

View File

@@ -0,0 +1,22 @@
// RUN: %empty-directory(%t)
// RUN: %empty-directory(%t/binary)
// RUN: %empty-directory(%t/textual)
// RUN: %empty-directory(%t/module-cache)
// RUN: %target-swift-frontend -emit-module -emit-module-path %t/binary/Foo.swiftmodule -parse-as-library %s -enable-library-evolution -allowable-client Bar -allowable-client FooBar -module-name Foo -DFOO -emit-module-interface-path %t/textual/Foo.swiftinterface
// RUN: %target-swift-frontend -typecheck %s -I %t/binary -module-name Bar
// RUN: %target-swift-frontend -typecheck %s -I %t/binary -module-name FooBar
// RUN: %target-typecheck-verify-swift -I %t/binary -module-name Blocked
// RUN: %target-typecheck-verify-swift -I %t/textual -module-name Blocked -module-cache-path %t/module-cache
#if FOO
public func foo() {}
#else
import Foo // expected-error {{module 'Foo' doesn't allow importation from module 'Blocked'}}
func bar() { foo() }
#endif