mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +01:00
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.
This commit is contained in:
committed by
Tony Allevato
parent
329261593e
commit
2b0f9aa765
@@ -400,6 +400,10 @@ public:
|
||||
// its name into runtime metadata.
|
||||
static bool identifierMustAlwaysBeEscaped(StringRef str);
|
||||
|
||||
/// Determines if the given string is a valid non-operator
|
||||
/// identifier if it were surrounded by backticks.
|
||||
static bool isValidAsEscapedIdentifier(StringRef identifier);
|
||||
|
||||
/// Determine the token kind of the string, given that it is a valid
|
||||
/// non-operator identifier. Return tok::identifier if the string is not a
|
||||
/// reserved word.
|
||||
|
||||
@@ -936,9 +936,12 @@ bool ModuleAliasesConverter::computeModuleAliases(std::vector<std::string> args,
|
||||
// it should be called only once
|
||||
options.ModuleAliasMap.clear();
|
||||
|
||||
auto validate = [&options, &diags](StringRef value, bool allowModuleName) -> bool
|
||||
{
|
||||
if (!allowModuleName) {
|
||||
// validatingModuleName should be true if validating the alias target (an
|
||||
// actual module name), or true if validating the alias name (which can be
|
||||
// an escaped identifier).
|
||||
auto validate = [&options, &diags](StringRef value,
|
||||
bool validatingModuleName) -> bool {
|
||||
if (!validatingModuleName) {
|
||||
if (value == options.ModuleName ||
|
||||
value == options.ModuleABIName ||
|
||||
value == options.ModuleLinkName ||
|
||||
@@ -947,7 +950,8 @@ bool ModuleAliasesConverter::computeModuleAliases(std::vector<std::string> args,
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (!Lexer::isIdentifier(value)) {
|
||||
if ((validatingModuleName && !Lexer::isIdentifier(value)) ||
|
||||
!Lexer::isValidAsEscapedIdentifier(value)) {
|
||||
diags.diagnose(SourceLoc(), diag::error_bad_module_name, value, false);
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -581,17 +581,7 @@ static bool isValidIdentifierStartCodePoint(uint32_t c) {
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool isValidIdentifierEscapedCodePoint(uint32_t c) {
|
||||
// An escaped identifier is terminated by a backtick, and the backslash is
|
||||
// reserved for possible future escaping.
|
||||
if (c == '`' || c == '\\')
|
||||
return false;
|
||||
|
||||
// This is the set of code points satisfying the `White_Space` property,
|
||||
// excluding the set satisfying the `Pattern_White_Space` property, and
|
||||
// excluding any other ASCII non-printables and Unicode separators. In
|
||||
// other words, the only whitespace code points allowed in a raw
|
||||
// identifier are U+0020, and U+200E/200F (LTR/RTL marks).
|
||||
static bool isForbiddenRawIdentifierWhitespace(uint32_t c) {
|
||||
if ((c >= 0x0009 && c <= 0x000D) ||
|
||||
c == 0x0085 ||
|
||||
c == 0x00A0 ||
|
||||
@@ -601,6 +591,30 @@ static bool isValidIdentifierEscapedCodePoint(uint32_t c) {
|
||||
c == 0x202F ||
|
||||
c == 0x205F ||
|
||||
c == 0x3000)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool isPermittedRawIdentifierWhitespace(uint32_t c) {
|
||||
return c == 0x0020 || c == 0x200E || c == 0x200F;
|
||||
}
|
||||
|
||||
static bool isValidIdentifierEscapedCodePoint(uint32_t c) {
|
||||
// An escaped identifier is terminated by a backtick, and the backslash is
|
||||
// reserved for possible future escaping.
|
||||
if (c == '`' || c == '\\')
|
||||
return false;
|
||||
|
||||
if ((c >= 0x0000 && c <= 0x001F) || c == 0x007F)
|
||||
return false;
|
||||
|
||||
// This is the set of code points satisfying the `White_Space` property,
|
||||
// excluding the set satisfying the `Pattern_White_Space` property, and
|
||||
// excluding any other ASCII non-printables and Unicode separators. In
|
||||
// other words, the only whitespace code points allowed in a raw
|
||||
// identifier are U+0020, and U+200E/200F (LTR/RTL marks).
|
||||
if (isForbiddenRawIdentifierWhitespace(c))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
@@ -644,6 +658,17 @@ static bool advanceIfValidContinuationOfOperator(char const *&ptr,
|
||||
return advanceIf(ptr, end, Identifier::isOperatorContinuationCodePoint);
|
||||
}
|
||||
|
||||
/// Returns true if the given string is entirely whitespace (considering only
|
||||
/// those whitespace code points permitted in raw identifiers).
|
||||
static bool isEntirelyWhitespace(StringRef string) {
|
||||
if (string.empty()) return false;
|
||||
char const *p = string.data(), *end = string.end();
|
||||
if (!advanceIf(p, end, isPermittedRawIdentifierWhitespace))
|
||||
return false;
|
||||
while (p < end && advanceIf(p, end, isPermittedRawIdentifierWhitespace));
|
||||
return p == end;
|
||||
}
|
||||
|
||||
bool Lexer::isIdentifier(StringRef string) {
|
||||
if (string.empty()) return false;
|
||||
char const *p = string.data(), *end = string.end();
|
||||
@@ -665,6 +690,19 @@ bool Lexer::identifierMustAlwaysBeEscaped(StringRef str) {
|
||||
return mustEscape;
|
||||
}
|
||||
|
||||
bool Lexer::isValidAsEscapedIdentifier(StringRef string) {
|
||||
if (string.empty())
|
||||
return false;
|
||||
char const *p = string.data(), *end = string.end();
|
||||
if (!advanceIfValidEscapedIdentifier(p, end))
|
||||
return false;
|
||||
while (p < end && advanceIfValidEscapedIdentifier(p, end))
|
||||
;
|
||||
if (p != end)
|
||||
return false;
|
||||
return !isEntirelyWhitespace(string);
|
||||
}
|
||||
|
||||
/// Determines if the given string is a valid operator identifier,
|
||||
/// without escaping characters.
|
||||
bool Lexer::isOperator(StringRef string) {
|
||||
@@ -2271,10 +2309,10 @@ void Lexer::lexEscapedIdentifier() {
|
||||
while (advanceIfValidEscapedIdentifier(CurPtr, BufferEnd))
|
||||
;
|
||||
|
||||
// If we have the terminating "`", it's an escaped identifier, unless it
|
||||
// contained only operator characters.
|
||||
if (*CurPtr == '`' &&
|
||||
!isOperator(StringRef(IdentifierStart, CurPtr - IdentifierStart))) {
|
||||
// If we have the terminating "`", it's an escaped/raw identifier, unless it
|
||||
// contained only operator characters or was entirely whitespace.
|
||||
StringRef IdStr(IdentifierStart, CurPtr - IdentifierStart);
|
||||
if (*CurPtr == '`' && !isOperator(IdStr) && !isEntirelyWhitespace(IdStr)) {
|
||||
++CurPtr;
|
||||
formEscapedIdentifierToken(Quote);
|
||||
return;
|
||||
|
||||
11
test/ClangImporter/module-alias-escaped-identifier.swift
Normal file
11
test/ClangImporter/module-alias-escaped-identifier.swift
Normal file
@@ -0,0 +1,11 @@
|
||||
// RUN: %empty-directory(%t)
|
||||
// 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
|
||||
|
||||
// RUN: %FileCheck %s -input-file %t/load-result.output -check-prefix CHECK-FOO
|
||||
// CHECK-FOO: import `//my/project:uncommon_name`
|
||||
// CHECK-FOO-NEXT: remark: loaded module 'CommonName'
|
||||
|
||||
import `//my/project:uncommon_name`
|
||||
|
||||
_ = MyStruct()
|
||||
_ = `//my/project:uncommon_name`.MyStruct()
|
||||
11
test/ClangImporter/module-alias.swift
Normal file
11
test/ClangImporter/module-alias.swift
Normal file
@@ -0,0 +1,11 @@
|
||||
// RUN: %empty-directory(%t)
|
||||
// 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
|
||||
|
||||
// RUN: %FileCheck %s -input-file %t/load-result.output -check-prefix CHECK-FOO
|
||||
// CHECK-FOO: import UncommonName
|
||||
// CHECK-FOO-NEXT: remark: loaded module 'CommonName'
|
||||
|
||||
import UncommonName
|
||||
|
||||
_ = MyStruct()
|
||||
_ = UncommonName.MyStruct()
|
||||
40
test/Frontend/module-alias-escaped-identifier.swift
Normal file
40
test/Frontend/module-alias-escaped-identifier.swift
Normal file
@@ -0,0 +1,40 @@
|
||||
/// Test the -module-alias flag with an escaped identifier alias.
|
||||
|
||||
// RUN: %empty-directory(%t)
|
||||
// RUN: %{python} %utils/split_file.py -o %t %s
|
||||
|
||||
/// Create a module Bar
|
||||
// RUN: %target-swift-frontend -module-name Bar %t/FileBar.swift -emit-module -emit-module-path %t/Bar.swiftmodule
|
||||
|
||||
/// Check Bar.swiftmodule is created
|
||||
// RUN: test -f %t/Bar.swiftmodule
|
||||
|
||||
/// Create a module Foo that imports `//my/project:cat` with -module-alias "//my/project:cat=Bar" with a serialized module loader
|
||||
// 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
|
||||
|
||||
/// Check Foo.swiftmodule is created and Bar.swiftmodule is loaded
|
||||
// RUN: test -f %t/Foo.swiftmodule
|
||||
// RUN: test -f %t/Bar.swiftmodule
|
||||
// RUN: not test -f %t/*cat.swiftmodule
|
||||
|
||||
// RUN: %FileCheck %s -input-file %t/load-result-foo.output -check-prefix CHECK-FOO
|
||||
// CHECK-FOO: remark: loaded module {{.*}}Bar.swiftmodule
|
||||
|
||||
/// Create a module Zoo that imports `//my/project:cat` with -module-alias "//my/project:cat=Bar" with a source loader
|
||||
// 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
|
||||
|
||||
// RUN: test -f %t/Zoo.swiftmodule
|
||||
// RUN: test -f %t/Bar.swiftmodule
|
||||
// RUN: not test -f %t/*cat.swiftmodule
|
||||
|
||||
// RUN: %FileCheck %s -input-file %t/load-result-zoo.output -check-prefix CHECK-ZOO
|
||||
// CHECK-ZOO: remark: loaded module {{.*}}Bar.swiftmodule
|
||||
|
||||
|
||||
// BEGIN FileBar.swift
|
||||
public func bar() {}
|
||||
|
||||
// BEGIN FileFoo.swift
|
||||
import `//my/project:cat`
|
||||
|
||||
`//my/project:cat`.bar()
|
||||
@@ -0,0 +1,65 @@
|
||||
/// Test the -module-alias flag with an escaped identifier and the explicit module loader.
|
||||
// UNSUPPORTED: OS=windows-msvc
|
||||
// RUN: %empty-directory(%t)
|
||||
// RUN: mkdir -p %t/inputs
|
||||
// RUN: mkdir -p %t/outputs
|
||||
|
||||
/// Create a module Bar
|
||||
// RUN: echo 'public func bar() {}' > %t/inputs/FileBar.swift
|
||||
// RUN: %target-swift-frontend -module-name Bar %t/inputs/FileBar.swift -emit-module -emit-module-path %t/inputs/Bar.swiftmodule
|
||||
// RUN: %target-swift-emit-pcm -module-name SwiftShims %swift-lib-dir/swift/shims/module.modulemap -o %t/inputs/SwiftShims.pcm
|
||||
// RUN: %target-swift-emit-pcm -module-name _SwiftConcurrencyShims %swift-lib-dir/swift/shims/module.modulemap -o %t/inputs/_SwiftConcurrencyShims.pcm
|
||||
|
||||
/// Check Bar.swiftmodule is created
|
||||
// RUN: test -f %t/inputs/Bar.swiftmodule
|
||||
|
||||
/// Next create an explicit module dependency map to build module Foo
|
||||
// RUN: echo 'import `//my/project:cat`' > %t/inputs/FileFoo.swift
|
||||
|
||||
// RUN: echo "[{" > %/t/inputs/map.json
|
||||
// RUN: echo "\"moduleName\": \"Bar\"," >> %/t/inputs/map.json
|
||||
// RUN: echo "\"modulePath\": \"%/t/inputs/Bar.swiftmodule\"," >> %/t/inputs/map.json
|
||||
// RUN: echo "\"isFramework\": false" >> %/t/inputs/map.json
|
||||
// RUN: echo "}," >> %/t/inputs/map.json
|
||||
// RUN: echo "{" >> %/t/inputs/map.json
|
||||
// RUN: echo "\"moduleName\": \"Swift\"," >> %/t/inputs/map.json
|
||||
// RUN: echo "\"modulePath\": \"%/stdlib_module\"," >> %/t/inputs/map.json
|
||||
// RUN: echo "\"isFramework\": false" >> %/t/inputs/map.json
|
||||
// RUN: echo "}," >> %/t/inputs/map.json
|
||||
// RUN: echo "{" >> %/t/inputs/map.json
|
||||
// RUN: echo "\"moduleName\": \"SwiftOnoneSupport\"," >> %/t/inputs/map.json
|
||||
// RUN: echo "\"modulePath\": \"%/ononesupport_module\"," >> %/t/inputs/map.json
|
||||
// RUN: echo "\"isFramework\": false" >> %/t/inputs/map.json
|
||||
// RUN: echo "}," >> %/t/inputs/map.json
|
||||
// RUN: echo "{" >> %/t/inputs/map.json
|
||||
// RUN: echo "\"moduleName\": \"_Concurrency\"," >> %/t/inputs/map.json
|
||||
// RUN: echo "\"modulePath\": \"%/concurrency_module\"," >> %/t/inputs/map.json
|
||||
// RUN: echo "\"isFramework\": false" >> %/t/inputs/map.json
|
||||
// RUN: echo "}," >> %/t/inputs/map.json
|
||||
// RUN: echo "{" >> %/t/inputs/map.json
|
||||
// RUN: echo "\"moduleName\": \"SwiftShims\"," >> %/t/inputs/map.json
|
||||
// RUN: echo "\"isFramework\": false," >> %/t/inputs/map.json
|
||||
// RUN: echo "\"clangModuleMapPath\": \"%swift-lib-dir/swift/shims/module.modulemap\"," >> %/t/inputs/map.json
|
||||
// RUN: echo "\"clangModulePath\": \"%t/inputs/SwiftShims.pcm\"" >> %/t/inputs/map.json
|
||||
// RUN: echo "}," >> %/t/inputs/map.json
|
||||
// RUN: echo "{" >> %/t/inputs/map.json
|
||||
// RUN: echo "\"moduleName\": \"_SwiftConcurrencyShims\"," >> %/t/inputs/map.json
|
||||
// RUN: echo "\"isFramework\": false," >> %/t/inputs/map.json
|
||||
// RUN: echo "\"clangModuleMapPath\": \"%swift-lib-dir/swift/shims/module.modulemap\"," >> %/t/inputs/map.json
|
||||
// RUN: echo "\"clangModulePath\": \"%t/inputs/_SwiftConcurrencyShims.pcm\"" >> %/t/inputs/map.json
|
||||
// RUN: echo "}," >> %/t/inputs/map.json
|
||||
// RUN: echo "{" >> %/t/inputs/map.json
|
||||
// RUN: echo "\"moduleName\": \"_StringProcessing\"," >> %/t/inputs/map.json
|
||||
// RUN: echo "\"modulePath\": \"%/string_processing_module\"," >> %/t/inputs/map.json
|
||||
// RUN: echo "\"isFramework\": false" >> %/t/inputs/map.json
|
||||
// RUN: echo "}]" >> %/t/inputs/map.json
|
||||
|
||||
/// Create a module Foo that imports `//my/project:cat` with -module-alias "//my/project:cat=Bar" with an explicit module loader
|
||||
// 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
|
||||
|
||||
// RUN: test -f %t/outputs/Foo.swiftmodule
|
||||
// RUN: test -f %t/inputs/Bar.swiftmodule
|
||||
// RUN: not test -f %t/inputs/*cat.swiftmodule
|
||||
|
||||
// RUN: %FileCheck %s -input-file %t/outputs/load-result.output -check-prefix CHECK
|
||||
// CHECK: remark: loaded module {{.*}}Bar.swiftmodule
|
||||
Reference in New Issue
Block a user