Skip to content

Commit b294fe9

Browse files
authored
Merge pull request swiftlang#39929 from apple/es-src
Module Aliasing: do not allow module real names to appear in source files (only allow module aliases). Resolves rdar://83592084
2 parents 1bc87ab + 66d64b6 commit b294fe9

File tree

6 files changed

+206
-13
lines changed

6 files changed

+206
-13
lines changed

include/swift/AST/ASTContext.h

Lines changed: 25 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -350,8 +350,14 @@ class ASTContext final {
350350
/// Cache of module names that fail the 'canImport' test in this context.
351351
mutable llvm::SmallPtrSet<Identifier, 8> FailedModuleImportNames;
352352

353-
/// Mapping between aliases and real (physical) names of imported or referenced modules.
354-
mutable llvm::DenseMap<Identifier, Identifier> ModuleAliasMap;
353+
/// Set if a `-module-alias` was passed. Used to store mapping between module aliases and
354+
/// their corresponding real names, and vice versa for a reverse lookup, which is needed to check
355+
/// if the module names appearing in source files are aliases or real names.
356+
/// \see ASTContext::getRealModuleName.
357+
///
358+
/// The boolean in the value indicates whether or not the entry is keyed by an alias vs real name,
359+
/// i.e. true if the entry is [key: alias_name, value: (real_name, true)].
360+
mutable llvm::DenseMap<Identifier, std::pair<Identifier, bool>> ModuleAliasMap;
355361

356362
/// Retrieve the allocator for the given arena.
357363
llvm::BumpPtrAllocator &
@@ -483,9 +489,23 @@ class ASTContext final {
483489
/// are the real (physical) module names on disk.
484490
void setModuleAliases(const llvm::StringMap<StringRef> &aliasMap);
485491

486-
/// Retrieve the actual module name if a module alias is used via '-module-alias Foo=X', where Foo is
487-
/// a module alias and X is the real (physical) name. Returns \p key if no aliasing is used.
488-
Identifier getRealModuleName(Identifier key) const;
492+
/// Look up the module alias map by the given \p key.
493+
///
494+
/// \param key A module alias or real name to look up the map by
495+
/// \param alwaysReturnRealName Indicates whether it should always retrieve the real module name
496+
/// given \p key. Defaults to true. This takes a higher precedence than
497+
/// \p lookupAliasFromReal.
498+
/// \param lookupAliasFromReal Indicates whether to look up an alias by treating \p key
499+
/// as a real name. Defaults to false.
500+
/// \return The real name or alias mapped to the key.
501+
/// If \p alwaysReturnRealName is true, return the real module name if \p key is an alias
502+
/// or the key itself since that's the real name.
503+
/// If \p lookupAliasFromReal is true, and \p alwaysReturnRealName is false, return
504+
/// only if \p key is a real name, else an empty Identifier.
505+
/// If no aliasing is used, return \p key.
506+
Identifier getRealModuleName(Identifier key,
507+
bool alwaysReturnRealName = true,
508+
bool lookupAliasFromReal = false) const;
489509

490510
/// Decide how to interpret two precedence groups.
491511
Associativity associateInfixOperators(PrecedenceGroupDecl *left,

include/swift/AST/DiagnosticsParse.def

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -302,6 +302,9 @@ ERROR(getset_cannot_be_implied,none,
302302
// Import
303303
ERROR(decl_expected_module_name,none,
304304
"expected module name in import declaration", ())
305+
ERROR(expected_module_alias,none,
306+
"cannot refer to module as %0 because it has been aliased; use %1 "
307+
"instead", (Identifier, Identifier))
305308

306309
// Extension
307310
ERROR(expected_lbrace_extension,PointsToFirstBadToken,

lib/AST/ASTContext.cpp

Lines changed: 29 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1637,20 +1637,42 @@ void ASTContext::addModuleInterfaceChecker(
16371637
}
16381638

16391639
void ASTContext::setModuleAliases(const llvm::StringMap<StringRef> &aliasMap) {
1640+
// This setter should be called only once after ASTContext has been initialized
1641+
assert(ModuleAliasMap.empty());
1642+
16401643
for (auto k: aliasMap.keys()) {
1641-
auto val = aliasMap.lookup(k);
1642-
if (!val.empty()) {
1643-
ModuleAliasMap[getIdentifier(k)] = getIdentifier(val);
1644+
auto v = aliasMap.lookup(k);
1645+
if (!v.empty()) {
1646+
auto key = getIdentifier(k);
1647+
auto val = getIdentifier(v);
1648+
// key is a module alias, val is its corresponding real name
1649+
ModuleAliasMap[key] = std::make_pair(val, true);
1650+
// add an entry with an alias as key for an easier lookup later
1651+
ModuleAliasMap[val] = std::make_pair(key, false);
16441652
}
16451653
}
16461654
}
16471655

1648-
Identifier ASTContext::getRealModuleName(Identifier key) const {
1656+
Identifier ASTContext::getRealModuleName(Identifier key, bool alwaysReturnRealName, bool lookupAliasFromReal) const {
16491657
auto found = ModuleAliasMap.find(key);
1650-
if (found != ModuleAliasMap.end()) {
1651-
return found->second;
1658+
if (found == ModuleAliasMap.end())
1659+
return key;
1660+
1661+
// Found an entry
1662+
auto realOrAlias = found->second;
1663+
1664+
// If alwaysReturnRealName, return the real name if the key is an
1665+
// alias or the key itself since that's the real name
1666+
if (alwaysReturnRealName) {
1667+
return realOrAlias.second ? realOrAlias.first : key;
16521668
}
1653-
return key;
1669+
1670+
// If lookupAliasFromReal, and the found entry should be keyed by a real
1671+
// name, return the entry value, otherwise, return an empty Identifier.
1672+
if (lookupAliasFromReal == realOrAlias.second)
1673+
return Identifier();
1674+
1675+
return realOrAlias.first;
16541676
}
16551677

16561678
Optional<ModuleDependencies> ASTContext::getModuleDependencies(

lib/AST/UnqualifiedLookup.cpp

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -431,7 +431,20 @@ void UnqualifiedLookupFactory::lookForAModuleWithTheGivenName(
431431
#endif
432432
return;
433433
}
434-
ModuleDecl *desiredModule = Ctx.getLoadedModule(Name.getBaseIdentifier());
434+
435+
ModuleDecl *desiredModule = nullptr;
436+
auto givenName = Name.getBaseIdentifier();
437+
// Check if the given name appearing in the source file is a module
438+
// real name or alias; for example, if `-module-alias Foo=Bar` was
439+
// passed, the alias 'Foo' should appear in source files, not 'Bar'.
440+
// If the real name 'Bar' was used, looking up getRealModuleName will
441+
// return an empty Identifier.
442+
if (!Ctx.getRealModuleName(givenName, /*alwaysReturnRealName=*/false).empty()) {
443+
// Only load the module if the lookup value is not empty, i.e. given
444+
// name is a module alias, not a real module name.
445+
desiredModule = Ctx.getLoadedModule(givenName);
446+
}
447+
435448
if (!desiredModule && Name.getFullName() == Ctx.TheBuiltinModule->getName())
436449
desiredModule = Ctx.TheBuiltinModule;
437450
if (desiredModule) {

lib/Parse/ParseDecl.cpp

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4822,6 +4822,20 @@ ParserResult<ImportDecl> Parser::parseDeclImport(ParseDeclOptions Flags,
48224822
return nullptr;
48234823
}
48244824

4825+
// Look up if the imported module is being aliased via -module-alias,
4826+
// and check that the module alias appeared in source files instead of
4827+
// its corresponding real name
4828+
auto parsedModuleID = importPath.get().front().Item;
4829+
if (Context.getRealModuleName(parsedModuleID, /*alwaysReturnRealName=*/false).empty()) {
4830+
// If reached here, it means the parsed module name is a real module name
4831+
// which appeared in the source file; only a module alias should be allowed
4832+
auto aliasName = Context.getRealModuleName(parsedModuleID, /*alwaysReturnRealName=*/false, /*lookupAliasFromReal=*/true);
4833+
diagnose(importPath.front().Loc, diag::expected_module_alias,
4834+
parsedModuleID, aliasName)
4835+
.fixItReplace(importPath.front().Loc, aliasName.str());
4836+
return nullptr;
4837+
}
4838+
48254839
auto *ID = ImportDecl::create(Context, CurDeclContext, ImportLoc, Kind,
48264840
KindLoc, importPath.get());
48274841
ID->getAttrs() = Attributes;
Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
/// Test diagnostics with module aliasing.
2+
///
3+
/// Module 'Lib' imports module 'XLogging', and 'XLogging' is aliased 'AppleLogging'.
4+
5+
// RUN: %empty-directory(%t)
6+
// RUN: %{python} %utils/split_file.py -o %t %s
7+
8+
/// Create AppleLogging.swiftmodule by aliasing XLogging
9+
// RUN: %target-swift-frontend -module-name AppleLogging -module-alias XLogging=AppleLogging %t/FileLogging.swift -emit-module -emit-module-path %t/AppleLogging.swiftmodule
10+
// RUN: test -f %t/AppleLogging.swiftmodule
11+
12+
/// 1. Pass: load and reference a module with module aliasing
13+
/// Create module Lib that imports XLogging WITH -module-alias XLogging=AppleLogging
14+
// 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
15+
// RUN: test -f %t/LibA.swiftmodule
16+
// RUN: %FileCheck %s -input-file %t/result-LibA.output -check-prefix CHECK-LOAD
17+
// CHECK-LOAD: remark: loaded module at {{.*}}AppleLogging.swiftmodule
18+
19+
/// 2. Fail: trying to access a non-member of a module (with module aliasing) should fail with the module alias in the diags
20+
/// Try building module Lib that imports XLogging WITH -module-alias XLogging=AppleLogging
21+
// 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
22+
// RUN: %FileCheck %s -input-file %t/result-LibB.output -check-prefix CHECK-NO-MEMBER
23+
// CHECK-NO-MEMBER: error: module 'XLogging' has no member named 'setupErr'
24+
25+
/// 3. Fail: importing the real module name that's being aliased should fail
26+
/// Create module Lib that imports XLogging WITH -module-alias XLogging=AppleLogging
27+
// 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
28+
// RUN: %FileCheck %s -input-file %t/result-LibC.output -check-prefix CHECK-NOT-IMPORT
29+
// CHECK-NOT-IMPORT: error: cannot refer to module as 'AppleLogging' because it has been aliased; use 'XLogging' instead
30+
31+
/// 4-1. Fail: referencing the real module name that's aliased should fail
32+
/// Create module Lib that imports XLogging WITH -module-alias XLogging=AppleLogging
33+
// 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
34+
// RUN: %FileCheck %s -input-file %t/result-LibD.output -check-prefix CHECK-NOT-REF1
35+
// CHECK-NOT-REF1: error: cannot find 'AppleLogging' in scope
36+
37+
/// 4-2. Fail: referencing the real module name that's aliased should fail
38+
/// Create module Lib that imports XLogging WITH -module-alias XLogging=AppleLogging
39+
// 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
40+
// RUN: %FileCheck %s -input-file %t/result-LibE.output -check-prefix CHECK-NOT-REF2
41+
// CHECK-NOT-REF2: error: cannot find type 'AppleLogging' in scope
42+
43+
/// 4-3. Fail: referencing the real module name that's aliased should fail
44+
/// Create module Lib that imports XLogging WITH -module-alias XLogging=AppleLogging
45+
// 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
46+
// RUN: %FileCheck %s -input-file %t/result-LibF.output -check-prefix CHECK-NOT-REF3
47+
// CHECK-NOT-REF3: error: cannot find type 'AppleLogging' in scope
48+
49+
/// 4-4. Fail: referencing the real module name that's aliased should fail
50+
/// Create module Lib that imports XLogging WITH -module-alias XLogging=AppleLogging
51+
// 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
52+
// RUN: %FileCheck %s -input-file %t/result-LibG.output -check-prefix CHECK-NOT-REF4
53+
// CHECK-NOT-REF4: error: cannot find type 'AppleLogging' in scope
54+
55+
56+
// BEGIN FileLogging.swift
57+
public protocol Loggable {
58+
var verbosity: Int { get }
59+
}
60+
61+
public struct Logger {
62+
public init() {}
63+
}
64+
65+
public func setup() -> XLogging.Logger? {
66+
return Logger()
67+
}
68+
69+
// BEGIN FileLib.swift
70+
import XLogging
71+
72+
public func start() {
73+
_ = XLogging.setup()
74+
}
75+
76+
// BEGIN FileLibNoSuchMember.swift
77+
import XLogging
78+
79+
public func start() {
80+
_ = XLogging.setupErr()
81+
}
82+
83+
// BEGIN FileLibImportRealName.swift
84+
import XLogging
85+
import AppleLogging
86+
87+
public func start() {
88+
_ = XLogging.setup()
89+
}
90+
91+
// BEGIN FileLibRefRealName1.swift
92+
import XLogging
93+
94+
public func start() {
95+
_ = AppleLogging.setup()
96+
}
97+
98+
// BEGIN FileLibRefRealName2.swift
99+
import XLogging
100+
101+
public struct MyStruct: AppleLogging.Loggable {
102+
public var verbosity: Int {
103+
return 3
104+
}
105+
}
106+
107+
// BEGIN FileLibRefRealName3.swift
108+
import XLogging
109+
110+
public struct MyStruct<T> where T: AppleLogging.Loggable {
111+
func log<T>(_ arg: T) {
112+
}
113+
}
114+
115+
// BEGIN FileLibRefRealName4.swift
116+
import XLogging
117+
118+
public struct MyStruct {
119+
func log<T: AppleLogging.Loggable>(_ arg: T) {
120+
}
121+
}

0 commit comments

Comments
 (0)