Skip to content

Commit 10a96df

Browse files
committed
Module Aliasing: do not allow module real names to appear in source files / only allow aliases
Resolves rdar://83592084
1 parent 710c6b8 commit 10a96df

File tree

6 files changed

+182
-6
lines changed

6 files changed

+182
-6
lines changed

include/swift/AST/ASTContext.h

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -351,7 +351,7 @@ class ASTContext final {
351351
mutable llvm::SmallPtrSet<Identifier, 8> FailedModuleImportNames;
352352

353353
/// Mapping between aliases and real (physical) names of imported or referenced modules.
354-
mutable llvm::DenseMap<Identifier, Identifier> ModuleAliasMap;
354+
mutable llvm::DenseMap<Identifier, std::pair<Identifier, bool>> ModuleAliasMap;
355355

356356
/// Retrieve the allocator for the given arena.
357357
llvm::BumpPtrAllocator &
@@ -487,6 +487,18 @@ class ASTContext final {
487487
/// a module alias and X is the real (physical) name. Returns \p key if no aliasing is used.
488488
Identifier getRealModuleName(Identifier key) const;
489489

490+
/// Checks if the given \p key is a module alias or a module real name.
491+
/// If \p key is a module alias, it returns a pair of its corresponding real name and 'true',
492+
/// if \p key is a module real name, it returns a pair of its corresponding alias, and 'false', and
493+
/// if \p key is a non-aliased module name, it returns a pair of that given name and 'true'.
494+
///
495+
/// This can be used to check if the module real name appears in source files, in which case error diags
496+
/// should be emitted (only aliases should allowed).
497+
///
498+
/// \param key A module name (alias, real name, or non-aliased name)
499+
/// \returns A pair of the module real name and 'true' if the \p key is an alias
500+
std::pair<Identifier, bool> getRealModuleNameOrAlias(Identifier key) const;
501+
490502
/// Decide how to interpret two precedence groups.
491503
Associativity associateInfixOperators(PrecedenceGroupDecl *left,
492504
PrecedenceGroupDecl *right) const;

include/swift/AST/DiagnosticsParse.def

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -302,6 +302,8 @@ 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+
"module real name should not appear in source files; only the module alias is allowed", ())
305307

306308
// Extension
307309
ERROR(expected_lbrace_extension,PointsToFirstBadToken,

lib/AST/ASTContext.cpp

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1638,21 +1638,37 @@ void ASTContext::addModuleInterfaceChecker(
16381638

16391639
void ASTContext::setModuleAliases(const llvm::StringMap<StringRef> &aliasMap) {
16401640
for (auto k: aliasMap.keys()) {
1641-
auto val = aliasMap.lookup(k);
1642-
if (!val.empty()) {
1643-
ModuleAliasMap[getIdentifier(k)] = getIdentifier(val);
1641+
auto v = aliasMap.lookup(k);
1642+
if (!v.empty()) {
1643+
auto key = getIdentifier(k);
1644+
auto val = getIdentifier(v);
1645+
// key is a module alias, val is its corresponding real name
1646+
ModuleAliasMap[key] = std::make_pair(val, true);
1647+
// add an entry with an alias as key for an easier lookup later
1648+
ModuleAliasMap[val] = std::make_pair(key, false);
16441649
}
16451650
}
16461651
}
16471652

16481653
Identifier ASTContext::getRealModuleName(Identifier key) const {
16491654
auto found = ModuleAliasMap.find(key);
16501655
if (found != ModuleAliasMap.end()) {
1651-
return found->second;
1656+
auto realOrAlias = found->second;
1657+
if (realOrAlias.second) // check if it's a real name given the key (alias)
1658+
return realOrAlias.first;
16521659
}
16531660
return key;
16541661
}
16551662

1663+
std::pair<Identifier, bool> ASTContext::getRealModuleNameOrAlias(Identifier key) const {
1664+
auto found = ModuleAliasMap.find(key);
1665+
if (found != ModuleAliasMap.end()) {
1666+
return found->second;
1667+
}
1668+
// No module aliasing is used for the given key
1669+
return std::make_pair(key, true);
1670+
}
1671+
16561672
Optional<ModuleDependencies> ASTContext::getModuleDependencies(
16571673
StringRef moduleName, bool isUnderlyingClangModule,
16581674
ModuleDependenciesCache &cache, InterfaceSubContextDelegate &delegate,

lib/AST/UnqualifiedLookup.cpp

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -431,7 +431,19 @@ 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 no module aliasing is used, this will simply return the given
441+
// name and 'true' indicating the check passed.
442+
auto checkResult = Ctx.getRealModuleNameOrAlias(givenName);
443+
if (checkResult.second) { // Check passed
444+
desiredModule = Ctx.getLoadedModule(givenName);
445+
}
446+
435447
if (!desiredModule && Name.getFullName() == Ctx.TheBuiltinModule->getName())
436448
desiredModule = Ctx.TheBuiltinModule;
437449
if (desiredModule) {

lib/Parse/ParseDecl.cpp

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4819,6 +4819,19 @@ ParserResult<ImportDecl> Parser::parseDeclImport(ParseDeclOptions Flags,
48194819
return nullptr;
48204820
}
48214821

4822+
// Look up if the imported module is being aliased via -module-alias,
4823+
// and check that the module alias appeared in source files instead of
4824+
// its corresponding real name
4825+
auto parsedModuleID = importPath.get().front().Item;
4826+
auto checkResult = Context.getRealModuleNameOrAlias(parsedModuleID);
4827+
if (!checkResult.second) {
4828+
// This means the parsed module name is the real name that appeared in
4829+
// the source file; only the module alias should be allowed
4830+
diagnose(importPath.front().Loc, diag::expected_module_alias)
4831+
.fixItReplace(importPath.front().Loc, checkResult.first.str());
4832+
return nullptr;
4833+
}
4834+
48224835
auto *ID = ImportDecl::create(Context, CurDeclContext, ImportLoc, Kind,
48234836
KindLoc, importPath.get());
48244837
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: module real name should not appear in source files; only the module alias is allowed
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)