Skip to content

Commit 3c00c1d

Browse files
committed
Allow module aliases to be raw identifiers.
This is the companion to the frontend changes for SE-0451 in swiftlang/swift#76636. The first component of a `-module-alias` flag is allowed to be a raw identifier (without the backticks), which allows for uses like the following: ``` import `//some/module:name` ``` The *target* of the alias (the actual module name) must still be a valid Swift identifier, since that corresponds to file system artifacts that need stricter naming conventions.
1 parent fef4d97 commit 3c00c1d

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
@@ -2973,16 +2973,18 @@ extension Driver {
29732973
with moduleName: String,
29742974
onError diagnosticsEngine: DiagnosticsEngine) -> [String: String]? {
29752975
var moduleAliases: [String: String]? = nil
2976-
let validate = { (_ arg: String, allowModuleName: Bool) -> Bool in
2977-
if !arg.sd_isSwiftIdentifier {
2976+
// validatingModuleName should be true when validating the alias target (an actual module
2977+
// name), or false when validating the alias name (which can be a raw identifier).
2978+
let validate = { (_ arg: String, validatingModuleName: Bool) -> Bool in
2979+
if (validatingModuleName && !arg.sd_isSwiftIdentifier) || !arg.sd_isValidAsRawIdentifier {
29782980
diagnosticsEngine.emit(.error_bad_module_name(moduleName: arg, explicitModuleName: true))
29792981
return false
29802982
}
29812983
if arg == "Swift" {
29822984
diagnosticsEngine.emit(.error_stdlib_module_name(moduleName: arg, explicitModuleName: true))
29832985
return false
29842986
}
2985-
if !allowModuleName, arg == moduleName {
2987+
if !validatingModuleName, arg == moduleName {
29862988
diagnosticsEngine.emit(.error_bad_module_alias(arg, moduleName: moduleName))
29872989
return false
29882990
}

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
@@ -3358,11 +3358,22 @@ final class SwiftDriverTests: XCTestCase {
33583358
$1.expect(.error("module alias \"Foo\" should be different from the module name \"Foo\""))
33593359
}
33603360

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

33673378
try assertDriverDiagnostics(
33683379
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)