Skip to content

Commit 3668c9f

Browse files
authored
Merge pull request #1836 from allevato/module-alias-raw-identifiers
Allow module aliases to be raw identifiers.
2 parents 3d5347b + 3c00c1d commit 3668c9f

File tree

4 files changed

+75
-4
lines changed

4 files changed

+75
-4
lines changed

Sources/SwiftDriver/Driver/Driver.swift

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2977,16 +2977,18 @@ extension Driver {
29772977
with moduleName: String,
29782978
onError diagnosticsEngine: DiagnosticsEngine) -> [String: String]? {
29792979
var moduleAliases: [String: String]? = nil
2980-
let validate = { (_ arg: String, allowModuleName: Bool) -> Bool in
2981-
if !arg.sd_isSwiftIdentifier {
2980+
// validatingModuleName should be true when validating the alias target (an actual module
2981+
// name), or false when validating the alias name (which can be a raw identifier).
2982+
let validate = { (_ arg: String, validatingModuleName: Bool) -> Bool in
2983+
if (validatingModuleName && !arg.sd_isSwiftIdentifier) || !arg.sd_isValidAsRawIdentifier {
29822984
diagnosticsEngine.emit(.error_bad_module_name(moduleName: arg, explicitModuleName: true))
29832985
return false
29842986
}
29852987
if arg == "Swift" {
29862988
diagnosticsEngine.emit(.error_stdlib_module_name(moduleName: arg, explicitModuleName: true))
29872989
return false
29882990
}
2989-
if !allowModuleName, arg == moduleName {
2991+
if !validatingModuleName, arg == moduleName {
29902992
diagnosticsEngine.emit(.error_bad_module_alias(arg, moduleName: moduleName))
29912993
return false
29922994
}

Sources/SwiftDriver/Utilities/StringAdditions.swift

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,15 @@ extension String {
2121
return start.isValidSwiftIdentifierStart &&
2222
continuation.allSatisfy { $0.isValidSwiftIdentifierContinuation }
2323
}
24+
25+
/// Whether this string is a valid Swift raw identifier.
26+
public var sd_isValidAsRawIdentifier: Bool {
27+
guard !isEmpty else {
28+
return false
29+
}
30+
return unicodeScalars.allSatisfy { $0.isValidRawIdentifierScalar }
31+
&& !unicodeScalars.allSatisfy { $0.isPermittedRawIdentifierWhitespace }
32+
}
2433
}
2534

2635
extension DefaultStringInterpolation {
@@ -120,6 +129,43 @@ extension Unicode.Scalar {
120129
/// `true` if this character is an ASCII digit: [0-9]
121130
var isASCIIDigit: Bool { (0x30...0x39).contains(value) }
122131

132+
/// `true` if this scalar is valid in a Swift raw identifier.
133+
var isValidRawIdentifierScalar: Bool {
134+
// A raw identifier is terminated by a backtick, and the backslash is reserved for possible
135+
// future escaping.
136+
if self == "`" || self == "\\" {
137+
return false
138+
}
139+
// Unprintable ASCII control characters are forbidden.
140+
if (0x0000...0x001F).contains(value) || value == 0x007F {
141+
return false;
142+
}
143+
return !isForbiddenRawIdentifierWhitespace
144+
}
145+
146+
var isForbiddenRawIdentifierWhitespace: Bool {
147+
// This is the set of code points satisfying the `White_Space` property, excluding the set
148+
// satisfying the `Pattern_White_Space` property, and excluding any other ASCII non-printables
149+
// and Unicode separators. In other words, the only whitespace code points allowed in a raw
150+
// identifier are U+0020, and U+200E/200F (LTR/RTL marks) (see
151+
// `isPermittedRawIdentifierWhitespace` below).
152+
return (0x0009...0x000D).contains(value) ||
153+
value == 0x0085 ||
154+
value == 0x00A0 ||
155+
value == 0x1680 ||
156+
(0x2000...0x200A).contains(value) ||
157+
(0x2028...0x2029).contains(value) ||
158+
value == 0x202F ||
159+
value == 0x205F ||
160+
value == 0x3000
161+
}
162+
163+
var isPermittedRawIdentifierWhitespace: Bool {
164+
return value == 0x0020 ||
165+
value == 0x200E ||
166+
value == 0x200F
167+
}
168+
123169
/// `true` if this is a body character of a C identifier,
124170
/// which is [a-zA-Z0-9_].
125171
func isCIdentifierBody(allowDollar: Bool = false) -> Bool {

Tests/SwiftDriverTests/StringAdditionsTests.swift

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,4 +76,16 @@ final class StringAdditionsTests: XCTestCase {
7676
XCTAssertFalse("".sd_isSwiftIdentifier,
7777
"Private-use characters aren't valid Swift identifiers")
7878
}
79+
80+
func testRawIdentifiers() {
81+
XCTAssertTrue("plain".sd_isValidAsRawIdentifier)
82+
XCTAssertTrue("has spaces".sd_isValidAsRawIdentifier)
83+
XCTAssertTrue("$^has/other!characters@#".sd_isValidAsRawIdentifier)
84+
85+
XCTAssertFalse("has`backtick".sd_isValidAsRawIdentifier)
86+
XCTAssertFalse("has\\backslash".sd_isValidAsRawIdentifier)
87+
XCTAssertFalse("has\u{0000}control\u{007F}characters".sd_isValidAsRawIdentifier)
88+
XCTAssertFalse("has\u{00A0}forbidden\u{2028}whitespace".sd_isValidAsRawIdentifier)
89+
XCTAssertFalse(" ".sd_isValidAsRawIdentifier)
90+
}
7991
}

Tests/SwiftDriverTests/SwiftDriverTests.swift

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3360,11 +3360,22 @@ final class SwiftDriverTests: XCTestCase {
33603360
$1.expect(.error("module alias \"Foo\" should be different from the module name \"Foo\""))
33613361
}
33623362

3363+
// A module alias is allowed to be a valid raw identifier, not just a regular Swift identifier.
3364+
try assertNoDriverDiagnostics(
3365+
args: "swiftc", "foo.swift", "-module-name", "Foo", "-module-alias", "//car/far:par=Bar", "-emit-module", "-emit-module-path", "/tmp/dir/Foo.swiftmodule"
3366+
)
3367+
// The alias target (an actual module name), however, may not be a raw identifier.
33633368
try assertDriverDiagnostics(
3364-
args: ["swiftc", "foo.swift", "-module-name", "Foo", "-module-alias", "C-ar=Bar", "-emit-module", "-emit-module-path", "/tmp/dir/Foo.swiftmodule"]
3369+
args: ["swiftc", "foo.swift", "-module-name", "Foo", "-module-alias", "Bar=C-ar", "-emit-module", "-emit-module-path", "/tmp/dir/Foo.swiftmodule"]
33653370
) {
33663371
$1.expect(.error("module name \"C-ar\" is not a valid identifier"))
33673372
}
3373+
// We should still diagnose names that are not valid raw identifiers.
3374+
try assertDriverDiagnostics(
3375+
args: ["swiftc", "foo.swift", "-module-name", "Foo", "-module-alias", "C`ar=Bar", "-emit-module", "-emit-module-path", "/tmp/dir/Foo.swiftmodule"]
3376+
) {
3377+
$1.expect(.error("module name \"C`ar\" is not a valid identifier"))
3378+
}
33683379

33693380
try assertDriverDiagnostics(
33703381
args: ["swiftc", "foo.swift", "-module-name", "Foo", "-module-alias", "Car=Bar", "-module-alias", "Train=Car", "-emit-module", "-emit-module-path", "/tmp/dir/Foo.swiftmodule"]

0 commit comments

Comments
 (0)