Module Aliasing: do not allow module real names to appear in source files / only allow aliases

Resolves rdar://83592084
This commit is contained in:
elsh
2021-10-28 02:36:57 -07:00
parent 710c6b858b
commit 10a96dff57
6 changed files with 182 additions and 6 deletions

View File

@@ -351,7 +351,7 @@ private:
mutable llvm::SmallPtrSet<Identifier, 8> FailedModuleImportNames;
/// Mapping between aliases and real (physical) names of imported or referenced modules.
mutable llvm::DenseMap<Identifier, Identifier> ModuleAliasMap;
mutable llvm::DenseMap<Identifier, std::pair<Identifier, bool>> ModuleAliasMap;
/// Retrieve the allocator for the given arena.
llvm::BumpPtrAllocator &
@@ -487,6 +487,18 @@ public:
/// a module alias and X is the real (physical) name. Returns \p key if no aliasing is used.
Identifier getRealModuleName(Identifier key) const;
/// Checks if the given \p key is a module alias or a module real name.
/// If \p key is a module alias, it returns a pair of its corresponding real name and 'true',
/// if \p key is a module real name, it returns a pair of its corresponding alias, and 'false', and
/// if \p key is a non-aliased module name, it returns a pair of that given name and 'true'.
///
/// This can be used to check if the module real name appears in source files, in which case error diags
/// should be emitted (only aliases should allowed).
///
/// \param key A module name (alias, real name, or non-aliased name)
/// \returns A pair of the module real name and 'true' if the \p key is an alias
std::pair<Identifier, bool> getRealModuleNameOrAlias(Identifier key) const;
/// Decide how to interpret two precedence groups.
Associativity associateInfixOperators(PrecedenceGroupDecl *left,
PrecedenceGroupDecl *right) const;

View File

@@ -302,6 +302,8 @@ ERROR(getset_cannot_be_implied,none,
// Import
ERROR(decl_expected_module_name,none,
"expected module name in import declaration", ())
ERROR(expected_module_alias,none,
"module real name should not appear in source files; only the module alias is allowed", ())
// Extension
ERROR(expected_lbrace_extension,PointsToFirstBadToken,

View File

@@ -1638,9 +1638,14 @@ void ASTContext::addModuleInterfaceChecker(
void ASTContext::setModuleAliases(const llvm::StringMap<StringRef> &aliasMap) {
for (auto k: aliasMap.keys()) {
auto val = aliasMap.lookup(k);
if (!val.empty()) {
ModuleAliasMap[getIdentifier(k)] = getIdentifier(val);
auto v = aliasMap.lookup(k);
if (!v.empty()) {
auto key = getIdentifier(k);
auto val = getIdentifier(v);
// key is a module alias, val is its corresponding real name
ModuleAliasMap[key] = std::make_pair(val, true);
// add an entry with an alias as key for an easier lookup later
ModuleAliasMap[val] = std::make_pair(key, false);
}
}
}
@@ -1648,11 +1653,22 @@ void ASTContext::setModuleAliases(const llvm::StringMap<StringRef> &aliasMap) {
Identifier ASTContext::getRealModuleName(Identifier key) const {
auto found = ModuleAliasMap.find(key);
if (found != ModuleAliasMap.end()) {
return found->second;
auto realOrAlias = found->second;
if (realOrAlias.second) // check if it's a real name given the key (alias)
return realOrAlias.first;
}
return key;
}
std::pair<Identifier, bool> ASTContext::getRealModuleNameOrAlias(Identifier key) const {
auto found = ModuleAliasMap.find(key);
if (found != ModuleAliasMap.end()) {
return found->second;
}
// No module aliasing is used for the given key
return std::make_pair(key, true);
}
Optional<ModuleDependencies> ASTContext::getModuleDependencies(
StringRef moduleName, bool isUnderlyingClangModule,
ModuleDependenciesCache &cache, InterfaceSubContextDelegate &delegate,

View File

@@ -431,7 +431,19 @@ void UnqualifiedLookupFactory::lookForAModuleWithTheGivenName(
#endif
return;
}
ModuleDecl *desiredModule = Ctx.getLoadedModule(Name.getBaseIdentifier());
ModuleDecl *desiredModule = nullptr;
auto givenName = Name.getBaseIdentifier();
// Check if the given name appearing in the source file is a module
// real name or alias; for example, if `-module-alias Foo=Bar` was
// passed, the alias 'Foo' should appear in source files, not 'Bar'.
// If no module aliasing is used, this will simply return the given
// name and 'true' indicating the check passed.
auto checkResult = Ctx.getRealModuleNameOrAlias(givenName);
if (checkResult.second) { // Check passed
desiredModule = Ctx.getLoadedModule(givenName);
}
if (!desiredModule && Name.getFullName() == Ctx.TheBuiltinModule->getName())
desiredModule = Ctx.TheBuiltinModule;
if (desiredModule) {

View File

@@ -4819,6 +4819,19 @@ ParserResult<ImportDecl> Parser::parseDeclImport(ParseDeclOptions Flags,
return nullptr;
}
// Look up if the imported module is being aliased via -module-alias,
// and check that the module alias appeared in source files instead of
// its corresponding real name
auto parsedModuleID = importPath.get().front().Item;
auto checkResult = Context.getRealModuleNameOrAlias(parsedModuleID);
if (!checkResult.second) {
// This means the parsed module name is the real name that appeared in
// the source file; only the module alias should be allowed
diagnose(importPath.front().Loc, diag::expected_module_alias)
.fixItReplace(importPath.front().Loc, checkResult.first.str());
return nullptr;
}
auto *ID = ImportDecl::create(Context, CurDeclContext, ImportLoc, Kind,
KindLoc, importPath.get());
ID->getAttrs() = Attributes;

View File

@@ -0,0 +1,121 @@
/// Test diagnostics with module aliasing.
///
/// Module 'Lib' imports module 'XLogging', and 'XLogging' is aliased 'AppleLogging'.
// RUN: %empty-directory(%t)
// RUN: %{python} %utils/split_file.py -o %t %s
/// Create AppleLogging.swiftmodule by aliasing XLogging
// RUN: %target-swift-frontend -module-name AppleLogging -module-alias XLogging=AppleLogging %t/FileLogging.swift -emit-module -emit-module-path %t/AppleLogging.swiftmodule
// RUN: test -f %t/AppleLogging.swiftmodule
/// 1. Pass: load and reference a module with module aliasing
/// Create module Lib that imports XLogging WITH -module-alias XLogging=AppleLogging
// RUN: %target-swift-frontend -module-name LibA %t/FileLib.swift -module-alias XLogging=AppleLogging -I %t -emit-module -emit-module-path %t/LibA.swiftmodule -Rmodule-loading 2> %t/result-LibA.output
// RUN: test -f %t/LibA.swiftmodule
// RUN: %FileCheck %s -input-file %t/result-LibA.output -check-prefix CHECK-LOAD
// CHECK-LOAD: remark: loaded module at {{.*}}AppleLogging.swiftmodule
/// 2. Fail: trying to access a non-member of a module (with module aliasing) should fail with the module alias in the diags
/// Try building module Lib that imports XLogging WITH -module-alias XLogging=AppleLogging
// RUN: not %target-swift-frontend -module-name LibB %t/FileLibNoSuchMember.swift -module-alias XLogging=AppleLogging -I %t -emit-module -emit-module-path %t/LibB.swiftmodule 2> %t/result-LibB.output
// RUN: %FileCheck %s -input-file %t/result-LibB.output -check-prefix CHECK-NO-MEMBER
// CHECK-NO-MEMBER: error: module 'XLogging' has no member named 'setupErr'
/// 3. Fail: importing the real module name that's being aliased should fail
/// Create module Lib that imports XLogging WITH -module-alias XLogging=AppleLogging
// RUN: not %target-swift-frontend -module-name LibC %t/FileLibImportRealName.swift -module-alias XLogging=AppleLogging -I %t -emit-module -emit-module-path %t/LibC.swiftmodule 2> %t/result-LibC.output
// RUN: %FileCheck %s -input-file %t/result-LibC.output -check-prefix CHECK-NOT-IMPORT
// CHECK-NOT-IMPORT: error: module real name should not appear in source files; only the module alias is allowed
/// 4-1. Fail: referencing the real module name that's aliased should fail
/// Create module Lib that imports XLogging WITH -module-alias XLogging=AppleLogging
// RUN: not %target-swift-frontend -module-name LibD %t/FileLibRefRealName1.swift -module-alias XLogging=AppleLogging -I %t -emit-module -emit-module-path %t/LibD.swiftmodule 2> %t/result-LibD.output
// RUN: %FileCheck %s -input-file %t/result-LibD.output -check-prefix CHECK-NOT-REF1
// CHECK-NOT-REF1: error: cannot find 'AppleLogging' in scope
/// 4-2. Fail: referencing the real module name that's aliased should fail
/// Create module Lib that imports XLogging WITH -module-alias XLogging=AppleLogging
// RUN: not %target-swift-frontend -module-name LibE %t/FileLibRefRealName2.swift -module-alias XLogging=AppleLogging -I %t -emit-module -emit-module-path %t/LibE.swiftmodule 2> %t/result-LibE.output
// RUN: %FileCheck %s -input-file %t/result-LibE.output -check-prefix CHECK-NOT-REF2
// CHECK-NOT-REF2: error: cannot find type 'AppleLogging' in scope
/// 4-3. Fail: referencing the real module name that's aliased should fail
/// Create module Lib that imports XLogging WITH -module-alias XLogging=AppleLogging
// RUN: not %target-swift-frontend -module-name LibF %t/FileLibRefRealName3.swift -module-alias XLogging=AppleLogging -I %t -emit-module -emit-module-path %t/LibF.swiftmodule 2> %t/result-LibF.output
// RUN: %FileCheck %s -input-file %t/result-LibF.output -check-prefix CHECK-NOT-REF3
// CHECK-NOT-REF3: error: cannot find type 'AppleLogging' in scope
/// 4-4. Fail: referencing the real module name that's aliased should fail
/// Create module Lib that imports XLogging WITH -module-alias XLogging=AppleLogging
// RUN: not %target-swift-frontend -module-name LibG %t/FileLibRefRealName4.swift -module-alias XLogging=AppleLogging -I %t -emit-module -emit-module-path %t/LibG.swiftmodule 2> %t/result-LibG.output
// RUN: %FileCheck %s -input-file %t/result-LibG.output -check-prefix CHECK-NOT-REF4
// CHECK-NOT-REF4: error: cannot find type 'AppleLogging' in scope
// BEGIN FileLogging.swift
public protocol Loggable {
var verbosity: Int { get }
}
public struct Logger {
public init() {}
}
public func setup() -> XLogging.Logger? {
return Logger()
}
// BEGIN FileLib.swift
import XLogging
public func start() {
_ = XLogging.setup()
}
// BEGIN FileLibNoSuchMember.swift
import XLogging
public func start() {
_ = XLogging.setupErr()
}
// BEGIN FileLibImportRealName.swift
import XLogging
import AppleLogging
public func start() {
_ = XLogging.setup()
}
// BEGIN FileLibRefRealName1.swift
import XLogging
public func start() {
_ = AppleLogging.setup()
}
// BEGIN FileLibRefRealName2.swift
import XLogging
public struct MyStruct: AppleLogging.Loggable {
public var verbosity: Int {
return 3
}
}
// BEGIN FileLibRefRealName3.swift
import XLogging
public struct MyStruct<T> where T: AppleLogging.Loggable {
func log<T>(_ arg: T) {
}
}
// BEGIN FileLibRefRealName4.swift
import XLogging
public struct MyStruct {
func log<T: AppleLogging.Loggable>(_ arg: T) {
}
}