Skip to content

Commit 0ed41dc

Browse files
committed
Fixed testRecovery157
1 parent ac4f8f3 commit 0ed41dc

File tree

6 files changed

+34
-5
lines changed

6 files changed

+34
-5
lines changed

Sources/SwiftParser/Expressions.swift

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,8 @@ extension TokenConsumer {
3232
} else {
3333
return true
3434
}
35+
case (.primaryExpressionStart(.atSign), let handle)?:
36+
break
3537
case (_, _)?:
3638
return true
3739
case nil:
@@ -1168,6 +1170,8 @@ extension Parser {
11681170
arena: self.arena
11691171
)
11701172
)
1173+
case (.atSign, _)?:
1174+
return RawExprSyntax(self.parseStringLiteral())
11711175
case (.rawStringDelimiter, _)?, (.stringQuote, _)?, (.multilineStringQuote, _)?, (.singleQuote, _)?:
11721176
return RawExprSyntax(self.parseStringLiteral())
11731177
case (.extendedRegexDelimiter, _)?, (.regexSlash, _)?:

Sources/SwiftParser/StringLiterals.swift

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -467,8 +467,12 @@ extension Parser {
467467
/// Parse opening raw string delimiter if exist.
468468
let openDelimiter = self.consume(if: .rawStringDelimiter)
469469

470+
/// Try to parse @ in order to recover from Objective-C style literals
471+
let unexpectedAtSign = self.consume(if: .atSign)
472+
470473
/// Parse open quote.
471474
var (unexpectedBeforeOpenQuote, openQuote) = self.expect(.stringQuote, .multilineStringQuote, default: .stringQuote)
475+
unexpectedBeforeOpenQuote = RawUnexpectedNodesSyntax(combining: unexpectedAtSign, unexpectedBeforeOpenQuote, arena: self.arena)
472476
var openQuoteKind: RawTokenKind = openQuote.tokenKind
473477
if openQuote.isMissing, let singleQuote = self.consume(if: .singleQuote) {
474478
unexpectedBeforeOpenQuote = RawUnexpectedNodesSyntax(combining: unexpectedBeforeOpenQuote, singleQuote, arena: self.arena)

Sources/SwiftParser/TokenSpecSet.swift

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -569,6 +569,7 @@ enum ParameterModifier: TokenSpecSet {
569569

570570
enum PrimaryExpressionStart: TokenSpecSet {
571571
case anyKeyword
572+
case atSign // For recovery
572573
case capitalSelfKeyword
573574
case dollarIdentifier
574575
case falseKeyword
@@ -598,6 +599,7 @@ enum PrimaryExpressionStart: TokenSpecSet {
598599
init?(lexeme: Lexer.Lexeme) {
599600
switch PrepareForKeywordMatch(lexeme) {
600601
case TokenSpec(.Any): self = .anyKeyword
602+
case TokenSpec(.atSign): self = .atSign
601603
case TokenSpec(.Self): self = .capitalSelfKeyword
602604
case TokenSpec(.dollarIdentifier): self = .dollarIdentifier
603605
case TokenSpec(.false): self = .falseKeyword
@@ -630,6 +632,7 @@ enum PrimaryExpressionStart: TokenSpecSet {
630632
var spec: TokenSpec {
631633
switch self {
632634
case .anyKeyword: return .keyword(.Any)
635+
case .atSign: return .atSign
633636
case .capitalSelfKeyword: return .keyword(.Self)
634637
case .dollarIdentifier: return .dollarIdentifier
635638
case .falseKeyword: return .keyword(.false)

Sources/SwiftParserDiagnostics/ParseDiagnosticsGenerator.swift

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1003,6 +1003,17 @@ public class ParseDiagnosticsGenerator: SyntaxAnyVisitor {
10031003
if shouldSkip(node) {
10041004
return .skipChildren
10051005
}
1006+
// recover from Objective-C style literals
1007+
if let atSign = node.unexpectedBetweenOpenDelimiterAndOpenQuote?.onlyToken(where: { $0.tokenKind == .atSign }) {
1008+
addDiagnostic(
1009+
node,
1010+
.stringLiteralAtSign,
1011+
fixIts: [
1012+
FixIt(message: RemoveNodesFixIt(atSign), changes: .makeMissing(atSign))
1013+
],
1014+
handledNodes: [atSign.id]
1015+
)
1016+
}
10061017
if let singleQuote = node.unexpectedBetweenOpenDelimiterAndOpenQuote?.onlyToken(where: { $0.tokenKind == .singleQuote }) {
10071018
let fixIt = FixIt(
10081019
message: ReplaceTokensFixIt(replaceTokens: [singleQuote], replacement: node.openQuote),

Sources/SwiftParserDiagnostics/ParserDiagnosticMessages.swift

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -194,6 +194,9 @@ extension DiagnosticMessage where Self == StaticParserError {
194194
public static var standaloneSemicolonStatement: Self {
195195
.init("standalone ';' statements are not allowed")
196196
}
197+
public static var stringLiteralAtSign: Self {
198+
.init("string literals in Swift are not preceded by an '@' sign")
199+
}
197200
public static var subscriptsCannotHaveNames: Self {
198201
.init("subscripts cannot have a name")
199202
}

Tests/SwiftParserTest/translated/RecoveryTests.swift

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1881,16 +1881,20 @@ final class RecoveryTests: XCTestCase {
18811881
}
18821882

18831883
func testRecovery157() {
1884+
// <rdar://problem/19833424> QoI: Bad error message when using Objective-C literals (@"Hello")
18841885
assertParse(
18851886
#"""
1886-
// <rdar://problem/19833424> QoI: Bad error message when using Objective-C literals (@"Hello") in Swift files
18871887
let myString = 1️⃣@"foo"
18881888
"""#,
18891889
diagnostics: [
1890-
// TODO: Old parser expected error on line 2: string literals in Swift are not preceded by an '@' sign, Fix-It replacements: 16 - 17 = ''
1891-
DiagnosticSpec(message: "expected expression in variable"),
1892-
DiagnosticSpec(message: #"extraneous code '@"foo"' at top level"#),
1893-
]
1890+
DiagnosticSpec(
1891+
message: "string literals in Swift are not preceded by an '@' sign",
1892+
fixIts: ["remove '@'"]
1893+
)
1894+
],
1895+
fixedSource: """
1896+
let myString = "foo"
1897+
"""
18941898
)
18951899
}
18961900

0 commit comments

Comments
 (0)