Skip to content

Produce more useful diagnostics for bogus specifier before arrow and leftbrace #1381

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
3 changes: 2 additions & 1 deletion Sources/SwiftParser/Declarations.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
3 changes: 2 additions & 1 deletion Sources/SwiftParser/Recovery.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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?
Expand Down
2 changes: 1 addition & 1 deletion Sources/SwiftParser/TopLevel.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
2 changes: 1 addition & 1 deletion Sources/SwiftSyntax/generated/Keyword.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -231,7 +231,7 @@ final class EffectfulPropertiesTests: XCTestCase {
}
""",
diagnostics: [
DiagnosticSpec(message: "unexpected code 'bogus {}' in variable")
DiagnosticSpec(message: "unexpected code 'bogus' in accessor")
]
)
}
Expand All @@ -257,7 +257,7 @@ final class EffectfulPropertiesTests: XCTestCase {
}
""",
diagnostics: [
DiagnosticSpec(message: "unexpected code '-> Int { 0 }' in variable")
DiagnosticSpec(message: "unexpected code '-> Int' in accessor")
]
)
}
Expand Down
5 changes: 4 additions & 1 deletion Tests/SwiftParserTest/translated/InvalidTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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 '<T>()' in function signature"),
DiagnosticSpec(locationMarker: "4️⃣", message: "unexpected code ')' in function"),
]
)
}
Expand Down Expand Up @@ -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"),
]
)
}
Expand All @@ -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"),
]
)
}
Expand Down
27 changes: 22 additions & 5 deletions Tests/SwiftParserTest/translated/RecoveryTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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"),
]
)
}
Expand Down Expand Up @@ -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")
]
)
}
Expand All @@ -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")
]
)
}
Expand All @@ -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")
]
)
}
Expand Down Expand Up @@ -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")
]
)
}

}