Skip to content

Commit c4f2b03

Browse files
committed
Handle missing func keyword
1 parent ad237ff commit c4f2b03

File tree

3 files changed

+48
-24
lines changed

3 files changed

+48
-24
lines changed

Sources/SwiftParser/Declarations.swift

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -208,6 +208,10 @@ extension Parser {
208208
modifiers: self.parseDeclModifierList()
209209
)
210210

211+
if atFunctionDeclarationWithoutFuncKeyword() {
212+
return RawDeclSyntax(self.parseFuncDeclaration(attrs, .missing(.keyword(.func))))
213+
}
214+
211215
// If we are inside a memberDecl list, we don't want to eat closing braces (which most likely close the outer context)
212216
// while recovering to the declaration start.
213217
let recoveryPrecedence = inMemberDeclList ? TokenPrecedence.closingBrace : nil
@@ -283,10 +287,7 @@ extension Parser {
283287
)
284288
}
285289

286-
let isPossibleFuncIdentifier = self.at(.identifier, .wildcard)
287-
let isPossibleFuncParen = self.peek(isAt: .leftParen, .binaryOperator)
288-
// Treat operators specially because they're likely to be functions.
289-
if (isPossibleFuncIdentifier && isPossibleFuncParen) || self.at(anyIn: Operator.self) != nil {
290+
if atFunctionDeclarationWithoutFuncKeyword() {
290291
return RawDeclSyntax(self.parseFuncDeclaration(attrs, .missing(.keyword(.func))))
291292
}
292293
}
@@ -298,6 +299,17 @@ extension Parser {
298299
)
299300
)
300301
}
302+
303+
/// Returns `true` if it looks like the parser is positioned at a function declaration that’s missing the `func` keyword.
304+
fileprivate mutating func atFunctionDeclarationWithoutFuncKeyword() -> Bool {
305+
if self.at(.identifier) && (self.peek(isAt: .leftParen) || self.peek().tokenText.hasPrefix("<")) {
306+
return true
307+
}
308+
if self.at(anyIn: Operator.self) != nil {
309+
return true
310+
}
311+
return false
312+
}
301313
}
302314

303315
extension Parser {

Tests/SwiftParserTest/DeclarationTests.swift

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -452,18 +452,20 @@ final class DeclarationTests: ParserTestCase {
452452

453453
assertParse(
454454
"""
455-
private(1️⃣+
456-
set
455+
private(1️⃣+2️⃣
456+
set3️⃣
457457
) var a = 0
458458
""",
459459
diagnostics: [
460-
DiagnosticSpec(message: "expected 'set)' to end modifier", fixIts: ["insert 'set)'"]),
460+
DiagnosticSpec(locationMarker: "1️⃣", message: "expected 'set)' to end modifier", fixIts: ["insert 'set)'"]),
461+
DiagnosticSpec(locationMarker: "1️⃣", message: "expected 'func' in function", fixIts: ["insert 'func'"]),
462+
DiagnosticSpec(locationMarker: "2️⃣", message: "expected '(' to start parameter clause", fixIts: ["insert '('"]),
461463
// FIXME: It should print `+` as detail of text.
462-
DiagnosticSpec(message: "unexpected code in variable"),
464+
DiagnosticSpec(locationMarker: "3️⃣", message: "expected ':' and type in parameter", fixIts: ["insert ':' and type"]),
463465
],
464466
fixedSource: """
465-
private(set) +
466-
set
467+
private(set) func + (
468+
set: <#type#>
467469
) var a = 0
468470
"""
469471
)

Tests/SwiftParserTest/translated/RecoveryTests.swift

Lines changed: 24 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -2107,13 +2107,7 @@ final class RecoveryTests: ParserTestCase {
21072107
)
21082108
}
21092109

2110-
func testRecovery120() {
2111-
assertParse(
2112-
"""
2113-
//===--- Recovery for wrong decl introducer keyword.
2114-
"""
2115-
)
2116-
}
2110+
// MARK: - Recovery for wrong decl introducer keyword.
21172111

21182112
func testRecovery121() {
21192113
assertParse(
@@ -2125,20 +2119,36 @@ final class RecoveryTests: ParserTestCase {
21252119
}
21262120
""",
21272121
diagnostics: [
2128-
// TODO: Old parser expected error on line 2: expected 'func' keyword in instance method declaration
2129-
DiagnosticSpec(message: "unexpected code 'notAKeyword() {}' before function")
2130-
]
2122+
DiagnosticSpec(message: "expected 'func' in function", fixIts: ["insert 'func'"])
2123+
],
2124+
fixedSource: """
2125+
class WrongDeclIntroducerKeyword1 {
2126+
func notAKeyword() {}
2127+
func foo() {}
2128+
class func bar() {}
2129+
}
2130+
"""
21312131
)
2132-
}
21332132

2134-
func testRecovery122() {
21352133
assertParse(
21362134
"""
2137-
//===--- Recovery for wrong inheritance clause.
2138-
"""
2135+
class WrongDeclIntroducerKeyword1 {
2136+
1️⃣notAKeyword() {}
2137+
var x: Int
2138+
}
2139+
""",
2140+
diagnostics: [DiagnosticSpec(message: "expected 'func' in function", fixIts: ["insert 'func'"])],
2141+
fixedSource: """
2142+
class WrongDeclIntroducerKeyword1 {
2143+
func notAKeyword() {}
2144+
var x: Int
2145+
}
2146+
"""
21392147
)
21402148
}
21412149

2150+
// MARK: - Recovery for wrong inheritance clause.
2151+
21422152
func testRecovery123() {
21432153
assertParse(
21442154
"""

0 commit comments

Comments
 (0)