Skip to content

Commit 23e5a32

Browse files
committed
Diagnose ObjC-style string literals at top level
Both Swift parsers have always recognized `@"foo"` as an attempt to write a string literal by a programmer with Objective-C muscle memory, but some of SwiftParser’s lookahead helpers didn’t recognize that token sequence; instead they interpreted the `@` as the beginning of an attribute and therefore a missing declaration. Correct them.
1 parent 8f4b13f commit 23e5a32

File tree

4 files changed

+35
-2
lines changed

4 files changed

+35
-2
lines changed

Sources/SwiftParser/Declarations.swift

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,10 @@ extension TokenConsumer {
9999
if subparser.at(.rightBrace) || subparser.at(.endOfFile) || subparser.at(.poundEndif) {
100100
return true
101101
}
102+
if subparser.at(.stringQuote) {
103+
// `@"abc"` is an invalid Objective-C-style string literal, not a declaration.
104+
return false
105+
}
102106
}
103107

104108
let declStartKeyword: DeclarationKeyword?

Sources/SwiftParser/Expressions.swift

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,10 @@ extension TokenConsumer {
5757
return true
5858
}
5959
}
60+
if self.at(.atSign) && self.peek(isAt: .stringQuote) {
61+
// Invalid Objective-C-style string literal
62+
return true
63+
}
6064

6165
// 'repeat' is the start of a pack expansion expression.
6266
if self.at(.keyword(.repeat)) {

Tests/SwiftParserTest/ExpressionTests.swift

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1214,6 +1214,31 @@ final class ExpressionTests: ParserTestCase {
12141214
DiagnosticSpec(message: "invalid escape sequence in literal")
12151215
]
12161216
)
1217+
1218+
// <rdar://problem/19833424> QoI: Bad error message when using Objective-C literals (@"Hello")
1219+
assertParse(
1220+
"""
1221+
_ = 1️⃣@"a"
1222+
""",
1223+
diagnostics: [
1224+
DiagnosticSpec(message: "string literals in Swift are not preceded by an '@' sign", fixIts: ["remove '@'"])
1225+
],
1226+
fixedSource: """
1227+
_ = "a"
1228+
"""
1229+
)
1230+
1231+
assertParse(
1232+
"""
1233+
1️⃣@"a"
1234+
""",
1235+
diagnostics: [
1236+
DiagnosticSpec(message: "string literals in Swift are not preceded by an '@' sign", fixIts: ["remove '@'"])
1237+
],
1238+
fixedSource: """
1239+
"a"
1240+
"""
1241+
)
12171242
}
12181243

12191244
func testAdjacentRawStringLiterals() {

Tests/SwiftParserTest/translated/ModuleSelectorTests.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1957,9 +1957,9 @@ final class ModuleSelectorTests: ParserTestCase {
19571957
#"_ = Swift::1️⃣@"fnord""#,
19581958
diagnostics: [
19591959
DiagnosticSpec(message: "expected identifier", fixIts: ["insert identifier"]),
1960-
DiagnosticSpec(message: #"extraneous code '@"fnord"' at top level"#),
1960+
DiagnosticSpec(message: "string literals in Swift are not preceded by an '@' sign", fixIts: ["remove '@'"]),
19611961
],
1962-
fixedSource: #"_ = Swift::<#identifier#>@"fnord""#
1962+
fixedSource: #"_ = Swift::<#identifier#>"fnord""#
19631963
)
19641964
assertParse(
19651965
#"_ = Swift::1️⃣"fnord""#,

0 commit comments

Comments
 (0)