diff --git a/Sources/SwiftParser/Declarations.swift b/Sources/SwiftParser/Declarations.swift index 310b95b379d..0d56ef0964a 100644 --- a/Sources/SwiftParser/Declarations.swift +++ b/Sources/SwiftParser/Declarations.swift @@ -1375,7 +1375,8 @@ extension Parser { var effectSpecifiers = self.parseDeclEffectSpecifiers() let output: RawReturnClauseSyntax? - if self.at(.arrow) { + + if self.at(.arrow) || self.canRecoverTo(TokenSpec(.arrow, allowAtStartOfLine: false)) != nil { output = self.parseFunctionReturnClause(effectSpecifiers: &effectSpecifiers, allowNamedOpaqueResultType: true) } else { output = nil diff --git a/Sources/SwiftParser/Recovery.swift b/Sources/SwiftParser/Recovery.swift index 21b4f7f7c9e..4e48f45de8c 100644 --- a/Sources/SwiftParser/Recovery.swift +++ b/Sources/SwiftParser/Recovery.swift @@ -70,9 +70,10 @@ extension Parser.Lookahead { let initialTokensConsumed = self.tokensConsumed let recoveryPrecedence = min(spec1.recoveryPrecedence, spec2.recoveryPrecedence, spec3.recoveryPrecedence) + let shouldSkipOverNewlines = recoveryPrecedence.shouldSkipOverNewlines && spec1.allowAtStartOfLine && spec2.allowAtStartOfLine && spec3.allowAtStartOfLine while !self.at(.eof) { - if !recoveryPrecedence.shouldSkipOverNewlines, self.currentToken.isAtStartOfLine { + if !shouldSkipOverNewlines, self.currentToken.isAtStartOfLine { break } let matchedSpec: TokenSpec? diff --git a/Sources/SwiftParser/TopLevel.swift b/Sources/SwiftParser/TopLevel.swift index 7f3899cb154..73227a0065e 100644 --- a/Sources/SwiftParser/TopLevel.swift +++ b/Sources/SwiftParser/TopLevel.swift @@ -98,7 +98,7 @@ extension Parser { /// This function is used when parsing places where function bodies are /// optional - like the function requirements in protocol declarations. mutating func parseOptionalCodeBlock(allowInitDecl: Bool = true) -> RawCodeBlockSyntax? { - guard self.at(.leftBrace) else { + guard self.at(.leftBrace) || self.canRecoverTo(TokenSpec(.leftBrace, allowAtStartOfLine: false)) != nil else { return nil } return self.parseCodeBlock(allowInitDecl: allowInitDecl) diff --git a/Sources/SwiftSyntax/generated/Keyword.swift b/Sources/SwiftSyntax/generated/Keyword.swift index ce799d53d9e..2f3cb2f980d 100644 --- a/Sources/SwiftSyntax/generated/Keyword.swift +++ b/Sources/SwiftSyntax/generated/Keyword.swift @@ -735,7 +735,7 @@ public enum Keyword: UInt8, Hashable { } } - /// Whether the token kind is switched from being an identifier to being an identifier to a keyword in the lexer. + /// Whether the token kind is switched from being an identifier to a keyword in the lexer. /// This is true for keywords that used to be considered non-contextual. public var isLexerClassified: Bool { switch self { diff --git a/Tests/SwiftParserTest/translated/EffectfulPropertiesTests.swift b/Tests/SwiftParserTest/translated/EffectfulPropertiesTests.swift index 9ef8041c0cf..14d4809583f 100644 --- a/Tests/SwiftParserTest/translated/EffectfulPropertiesTests.swift +++ b/Tests/SwiftParserTest/translated/EffectfulPropertiesTests.swift @@ -231,7 +231,7 @@ final class EffectfulPropertiesTests: XCTestCase { } """, diagnostics: [ - DiagnosticSpec(message: "unexpected code 'bogus {}' in variable") + DiagnosticSpec(message: "unexpected code 'bogus' in accessor") ] ) } @@ -257,7 +257,7 @@ final class EffectfulPropertiesTests: XCTestCase { } """, diagnostics: [ - DiagnosticSpec(message: "unexpected code '-> Int { 0 }' in variable") + DiagnosticSpec(message: "unexpected code '-> Int' in accessor") ] ) } diff --git a/Tests/SwiftParserTest/translated/InvalidTests.swift b/Tests/SwiftParserTest/translated/InvalidTests.swift index c81b8e5c172..267e1ced6f6 100644 --- a/Tests/SwiftParserTest/translated/InvalidTests.swift +++ b/Tests/SwiftParserTest/translated/InvalidTests.swift @@ -205,7 +205,8 @@ final class InvalidTests: XCTestCase { DiagnosticSpec(locationMarker: "2️⃣", message: "expected ')' in function type"), DiagnosticSpec(locationMarker: "3️⃣", message: "expected return type in function type"), DiagnosticSpec(locationMarker: "3️⃣", message: "expected ')' to end parameter clause"), - DiagnosticSpec(locationMarker: "4️⃣", message: "extraneous code ') {}' at top level"), + DiagnosticSpec(locationMarker: "3️⃣", message: "unexpected code '()' in function signature"), + DiagnosticSpec(locationMarker: "4️⃣", message: "unexpected code ')' in function"), ] ) } @@ -484,6 +485,7 @@ final class InvalidTests: XCTestCase { diagnostics: [ DiagnosticSpec(locationMarker: "1️⃣", message: "expected '(' to start parameter clause"), DiagnosticSpec(locationMarker: "2️⃣", message: "expected ')' to end parameter clause"), + DiagnosticSpec(locationMarker: "2️⃣", message: "unexpected code '()' in function"), ] ) } @@ -497,6 +499,7 @@ final class InvalidTests: XCTestCase { diagnostics: [ DiagnosticSpec(locationMarker: "1️⃣", message: "expected '(' to start parameter clause"), DiagnosticSpec(locationMarker: "2️⃣", message: "expected ')' to end parameter clause"), + DiagnosticSpec(locationMarker: "2️⃣", message: "unexpected code '(x: T)' in function"), ] ) } diff --git a/Tests/SwiftParserTest/translated/RecoveryTests.swift b/Tests/SwiftParserTest/translated/RecoveryTests.swift index dfd0343be20..6fb970f456c 100644 --- a/Tests/SwiftParserTest/translated/RecoveryTests.swift +++ b/Tests/SwiftParserTest/translated/RecoveryTests.swift @@ -1206,13 +1206,14 @@ final class RecoveryTests: XCTestCase { func bar() -> Int3️⃣] { return [0] } - } + 4️⃣} """, diagnostics: [ DiagnosticSpec(locationMarker: "1️⃣", message: "expected '}' to end struct"), DiagnosticSpec(locationMarker: "2️⃣", message: "expected ']' to end array"), // TODO: Old parser expected error on line 5: unexpected ']' in type; did you mean to write an array type?, Fix-It replacements: 17 - 17 = '[' - DiagnosticSpec(locationMarker: "3️⃣", message: "extraneous code at top level"), + DiagnosticSpec(locationMarker: "3️⃣", message: "unexpected code ']' in function"), + DiagnosticSpec(locationMarker: "4️⃣", message: "extraneous brace at top level"), ] ) } @@ -1247,7 +1248,7 @@ final class RecoveryTests: XCTestCase { """, diagnostics: [ // TODO: Old parser expected error on line 2: array types are now written with the brackets around the element type, Fix-It replacements: 17 - 17 = '[', 20 - 21 = '' - DiagnosticSpec(message: "unexpected code in struct") + DiagnosticSpec(message: "unexpected code '[0]' in function") ] ) } @@ -1263,7 +1264,7 @@ final class RecoveryTests: XCTestCase { """, diagnostics: [ // TODO: Old parser expected error on line 2: array types are now written with the brackets around the element type, Fix-It replacements: 17 - 17 = '[', 20 - 21 = '' - DiagnosticSpec(message: "unexpected code in struct") + DiagnosticSpec(message: "unexpected code '[0_1]' in function") ] ) } @@ -1279,7 +1280,7 @@ final class RecoveryTests: XCTestCase { """, diagnostics: [ // TODO: Old parser expected error on line 2: array types are now written with the brackets around the element type, Fix-It replacements: 17 - 17 = '[', 20 - 21 = '' - DiagnosticSpec(message: "unexpected code in struct") + DiagnosticSpec(message: "unexpected code '[0b1]' in function") ] ) } @@ -2205,4 +2206,20 @@ final class RecoveryTests: XCTestCase { ) } + func testRecovery182() { + AssertParse( + "func foo() 1️⃣bogus {}", + diagnostics: [ + DiagnosticSpec(message: "unexpected code 'bogus' in function") + ] + ) + + AssertParse( + "func foo() 1️⃣bogus -> Int {}", + diagnostics: [ + DiagnosticSpec(message: "unexpected code 'bogus' in function signature") + ] + ) + } + }