mirror of
https://github.com/apple/swift.git
synced 2025-12-21 12:14:44 +01:00
Module Aliasing: do not allow module real names to appear in source files / only allow aliases
Resolves rdar://83592084
This commit is contained in:
@@ -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;
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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;
|
||||
|
||||
121
test/Frontend/module-alias-diags.swift
Normal file
121
test/Frontend/module-alias-diags.swift
Normal 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) {
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user