Skip to content

Commit 8cf352b

Browse files
committed
Allow module aliases to use escaped identifiers as the alias name.
The original module names themselves must still be valid unescaped identifiers; most of the serialization logic in the compiler depends on the name of a module matching its name on the file system, and it would be very complex to turn escaped identifiers into file-safe names.
1 parent f288454 commit 8cf352b

File tree

7 files changed

+152
-6
lines changed

7 files changed

+152
-6
lines changed

include/swift/Parse/Lexer.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -400,6 +400,10 @@ class Lexer {
400400
// its name into runtime metadata.
401401
static bool identifierMustAlwaysBeEscaped(StringRef str);
402402

403+
/// Determines if the given string is a valid non-operator
404+
/// identifier if it were surrounded by backticks.
405+
static bool isValidAsEscapedIdentifier(StringRef identifier);
406+
403407
/// Determine the token kind of the string, given that it is a valid
404408
/// non-operator identifier. Return tok::identifier if the string is not a
405409
/// reserved word.

lib/Frontend/ArgsToFrontendOptionsConverter.cpp

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -853,10 +853,13 @@ bool ModuleAliasesConverter::computeModuleAliases(std::vector<std::string> args,
853853
// ModuleAliasMap should initially be empty as setting
854854
// it should be called only once
855855
options.ModuleAliasMap.clear();
856-
857-
auto validate = [&options, &diags](StringRef value, bool allowModuleName) -> bool
858-
{
859-
if (!allowModuleName) {
856+
857+
// validatingModuleName should be true if validating the alias target (an
858+
// actual module name), or true if validating the alias name (which can be
859+
// an escaped identifier).
860+
auto validate = [&options, &diags](StringRef value,
861+
bool validatingModuleName) -> bool {
862+
if (!validatingModuleName) {
860863
if (value == options.ModuleName ||
861864
value == options.ModuleABIName ||
862865
value == options.ModuleLinkName ||
@@ -865,13 +868,14 @@ bool ModuleAliasesConverter::computeModuleAliases(std::vector<std::string> args,
865868
return false;
866869
}
867870
}
868-
if (!Lexer::isIdentifier(value)) {
871+
if ((validatingModuleName && !Lexer::isIdentifier(value)) ||
872+
!Lexer::isValidAsEscapedIdentifier(value)) {
869873
diags.diagnose(SourceLoc(), diag::error_bad_module_name, value, false);
870874
return false;
871875
}
872876
return true;
873877
};
874-
878+
875879
for (auto item: args) {
876880
auto str = StringRef(item);
877881
// splits to an alias and its real name

lib/Parse/Lexer.cpp

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -665,6 +665,17 @@ bool Lexer::identifierMustAlwaysBeEscaped(StringRef str) {
665665
return mustEscape;
666666
}
667667

668+
bool Lexer::isValidAsEscapedIdentifier(StringRef string) {
669+
if (string.empty())
670+
return false;
671+
char const *p = string.data(), *end = string.end();
672+
if (!advanceIfValidEscapedIdentifier(p, end))
673+
return false;
674+
while (p < end && advanceIfValidEscapedIdentifier(p, end))
675+
;
676+
return p == end;
677+
}
678+
668679
/// Determines if the given string is a valid operator identifier,
669680
/// without escaping characters.
670681
bool Lexer::isOperator(StringRef string) {
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
// RUN: %empty-directory(%t)
2+
// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) -module-alias "//my/project:uncommon_name=CommonName" -typecheck -I %S/Inputs/custom-modules %s -Rmodule-loading 2> %t/load-result.output
3+
4+
// RUN: %FileCheck %s -input-file %t/load-result.output -check-prefix CHECK-FOO
5+
// CHECK-FOO: import `//my/project:uncommon_name`
6+
// CHECK-FOO-NEXT: remark: loaded module 'CommonName'
7+
8+
import `//my/project:uncommon_name`
9+
10+
_ = MyStruct()
11+
_ = `//my/project:uncommon_name`.MyStruct()

test/ClangImporter/module-alias.swift

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
// RUN: %empty-directory(%t)
2+
// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) -module-alias UncommonName=CommonName -typecheck -I %S/Inputs/custom-modules %s -Rmodule-loading 2> %t/load-result.output
3+
4+
// RUN: %FileCheck %s -input-file %t/load-result.output -check-prefix CHECK-FOO
5+
// CHECK-FOO: import UncommonName
6+
// CHECK-FOO-NEXT: remark: loaded module 'CommonName'
7+
8+
import UncommonName
9+
10+
_ = MyStruct()
11+
_ = UncommonName.MyStruct()
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
/// Test the -module-alias flag with an escaped identifier alias.
2+
3+
// RUN: %empty-directory(%t)
4+
// RUN: %{python} %utils/split_file.py -o %t %s
5+
6+
/// Create a module Bar
7+
// RUN: %target-swift-frontend -module-name Bar %t/FileBar.swift -emit-module -emit-module-path %t/Bar.swiftmodule
8+
9+
/// Check Bar.swiftmodule is created
10+
// RUN: test -f %t/Bar.swiftmodule
11+
12+
/// Create a module Foo that imports `//my/project:cat` with -module-alias "//my/project:cat=Bar" with a serialized module loader
13+
// RUN: %target-swift-frontend -module-name Foo %t/FileFoo.swift -module-alias "//my/project:cat=Bar" -I %t -emit-module -emit-module-path %t/Foo.swiftmodule -Rmodule-loading 2> %t/load-result-foo.output
14+
15+
/// Check Foo.swiftmodule is created and Bar.swiftmodule is loaded
16+
// RUN: test -f %t/Foo.swiftmodule
17+
// RUN: test -f %t/Bar.swiftmodule
18+
// RUN: not test -f %t/*cat.swiftmodule
19+
20+
// RUN: %FileCheck %s -input-file %t/load-result-foo.output -check-prefix CHECK-FOO
21+
// CHECK-FOO: remark: loaded module {{.*}}Bar.swiftmodule
22+
23+
/// Create a module Zoo that imports `//my/project:cat` with -module-alias "//my/project:cat=Bar" with a source loader
24+
// RUN: %target-swift-frontend -module-name Zoo %t/FileFoo.swift -module-alias "//my/project:cat=Bar" -I %t -emit-module -emit-module-path %t/Zoo.swiftmodule -enable-source-import -Rmodule-loading 2> %t/load-result-zoo.output
25+
26+
// RUN: test -f %t/Zoo.swiftmodule
27+
// RUN: test -f %t/Bar.swiftmodule
28+
// RUN: not test -f %t/*cat.swiftmodule
29+
30+
// RUN: %FileCheck %s -input-file %t/load-result-zoo.output -check-prefix CHECK-ZOO
31+
// CHECK-ZOO: remark: loaded module {{.*}}Bar.swiftmodule
32+
33+
34+
// BEGIN FileBar.swift
35+
public func bar() {}
36+
37+
// BEGIN FileFoo.swift
38+
import `//my/project:cat`
39+
40+
`//my/project:cat`.bar()
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
/// Test the -module-alias flag with an escaped identifier and the explicit module loader.
2+
// UNSUPPORTED: OS=windows-msvc
3+
// RUN: %empty-directory(%t)
4+
// RUN: mkdir -p %t/inputs
5+
// RUN: mkdir -p %t/outputs
6+
7+
/// Create a module Bar
8+
// RUN: echo 'public func bar() {}' > %t/inputs/FileBar.swift
9+
// RUN: %target-swift-frontend -module-name Bar %t/inputs/FileBar.swift -emit-module -emit-module-path %t/inputs/Bar.swiftmodule
10+
// RUN: %target-swift-emit-pcm -module-name SwiftShims %swift-lib-dir/swift/shims/module.modulemap -o %t/inputs/SwiftShims.pcm
11+
// RUN: %target-swift-emit-pcm -module-name _SwiftConcurrencyShims %swift-lib-dir/swift/shims/module.modulemap -o %t/inputs/_SwiftConcurrencyShims.pcm
12+
13+
/// Check Bar.swiftmodule is created
14+
// RUN: test -f %t/inputs/Bar.swiftmodule
15+
16+
/// Next create an explicit module dependency map to build module Foo
17+
// RUN: echo 'import `//my/project:cat`' > %t/inputs/FileFoo.swift
18+
19+
// RUN: echo "[{" > %/t/inputs/map.json
20+
// RUN: echo "\"moduleName\": \"Bar\"," >> %/t/inputs/map.json
21+
// RUN: echo "\"modulePath\": \"%/t/inputs/Bar.swiftmodule\"," >> %/t/inputs/map.json
22+
// RUN: echo "\"isFramework\": false" >> %/t/inputs/map.json
23+
// RUN: echo "}," >> %/t/inputs/map.json
24+
// RUN: echo "{" >> %/t/inputs/map.json
25+
// RUN: echo "\"moduleName\": \"Swift\"," >> %/t/inputs/map.json
26+
// RUN: echo "\"modulePath\": \"%/stdlib_module\"," >> %/t/inputs/map.json
27+
// RUN: echo "\"isFramework\": false" >> %/t/inputs/map.json
28+
// RUN: echo "}," >> %/t/inputs/map.json
29+
// RUN: echo "{" >> %/t/inputs/map.json
30+
// RUN: echo "\"moduleName\": \"SwiftOnoneSupport\"," >> %/t/inputs/map.json
31+
// RUN: echo "\"modulePath\": \"%/ononesupport_module\"," >> %/t/inputs/map.json
32+
// RUN: echo "\"isFramework\": false" >> %/t/inputs/map.json
33+
// RUN: echo "}," >> %/t/inputs/map.json
34+
// RUN: echo "{" >> %/t/inputs/map.json
35+
// RUN: echo "\"moduleName\": \"_Concurrency\"," >> %/t/inputs/map.json
36+
// RUN: echo "\"modulePath\": \"%/concurrency_module\"," >> %/t/inputs/map.json
37+
// RUN: echo "\"isFramework\": false" >> %/t/inputs/map.json
38+
// RUN: echo "}," >> %/t/inputs/map.json
39+
// RUN: echo "{" >> %/t/inputs/map.json
40+
// RUN: echo "\"moduleName\": \"SwiftShims\"," >> %/t/inputs/map.json
41+
// RUN: echo "\"isFramework\": false," >> %/t/inputs/map.json
42+
// RUN: echo "\"clangModuleMapPath\": \"%swift-lib-dir/swift/shims/module.modulemap\"," >> %/t/inputs/map.json
43+
// RUN: echo "\"clangModulePath\": \"%t/inputs/SwiftShims.pcm\"" >> %/t/inputs/map.json
44+
// RUN: echo "}," >> %/t/inputs/map.json
45+
// RUN: echo "{" >> %/t/inputs/map.json
46+
// RUN: echo "\"moduleName\": \"_SwiftConcurrencyShims\"," >> %/t/inputs/map.json
47+
// RUN: echo "\"isFramework\": false," >> %/t/inputs/map.json
48+
// RUN: echo "\"clangModuleMapPath\": \"%swift-lib-dir/swift/shims/module.modulemap\"," >> %/t/inputs/map.json
49+
// RUN: echo "\"clangModulePath\": \"%t/inputs/_SwiftConcurrencyShims.pcm\"" >> %/t/inputs/map.json
50+
// RUN: echo "}," >> %/t/inputs/map.json
51+
// RUN: echo "{" >> %/t/inputs/map.json
52+
// RUN: echo "\"moduleName\": \"_StringProcessing\"," >> %/t/inputs/map.json
53+
// RUN: echo "\"modulePath\": \"%/string_processing_module\"," >> %/t/inputs/map.json
54+
// RUN: echo "\"isFramework\": false" >> %/t/inputs/map.json
55+
// RUN: echo "}]" >> %/t/inputs/map.json
56+
57+
/// Create a module Foo that imports `//my/project:cat` with -module-alias "//my/project:cat=Bar" with an explicit module loader
58+
// RUN: %target-swift-frontend -module-name Foo %t/inputs/FileFoo.swift -module-alias "//my/project:cat=Bar" -I %t/inputs -emit-module -emit-module-path %t/outputs/Foo.swiftmodule -disable-implicit-swift-modules -explicit-swift-module-map-file %t/inputs/map.json -Rmodule-loading 2> %t/outputs/load-result.output
59+
60+
// RUN: test -f %t/outputs/Foo.swiftmodule
61+
// RUN: test -f %t/inputs/Bar.swiftmodule
62+
// RUN: not test -f %t/inputs/*cat.swiftmodule
63+
64+
// RUN: %FileCheck %s -input-file %t/outputs/load-result.output -check-prefix CHECK
65+
// CHECK: remark: loaded module {{.*}}Bar.swiftmodule

0 commit comments

Comments
 (0)