From 8ce0e0d047082354193d4e2148484eada14bd02d Mon Sep 17 00:00:00 2001 From: Kim de Vos Date: Wed, 12 Apr 2023 23:08:13 +0200 Subject: [PATCH] Merge pull request #1513 from kimdv/kimdv/add-diagnostic-for-testRecovery177 Add diagnostic for wrong where requirements separation --- Sources/SwiftParser/Declarations.swift | 12 +++++++ .../ParseDiagnosticsGenerator.swift | 32 +++++++++++++++++++ .../ParserDiagnosticMessages.swift | 3 ++ .../translated/RecoveryTests.swift | 10 +++--- 4 files changed, 53 insertions(+), 4 deletions(-) diff --git a/Sources/SwiftParser/Declarations.swift b/Sources/SwiftParser/Declarations.swift index d2fe86f973e..3dd455c5378 100644 --- a/Sources/SwiftParser/Declarations.swift +++ b/Sources/SwiftParser/Declarations.swift @@ -759,9 +759,21 @@ extension Parser { } keepGoing = self.consume(if: .comma) + let unexpectedBetweenBodyAndTrailingComma: RawUnexpectedNodesSyntax? + + // If there's a comma, keep parsing the list. + // If there's a "&&", diagnose replace with a comma and keep parsing + if let token = self.consumeIfContextualPunctuator("&&") { + keepGoing = self.missingToken(.comma) + unexpectedBetweenBodyAndTrailingComma = RawUnexpectedNodesSyntax([token], arena: self.arena) + } else { + unexpectedBetweenBodyAndTrailingComma = nil + } + elements.append( RawGenericRequirementSyntax( body: requirement, + unexpectedBetweenBodyAndTrailingComma, trailingComma: keepGoing, arena: self.arena ) diff --git a/Sources/SwiftParserDiagnostics/ParseDiagnosticsGenerator.swift b/Sources/SwiftParserDiagnostics/ParseDiagnosticsGenerator.swift index d0a1d68a20a..72ef56010e2 100644 --- a/Sources/SwiftParserDiagnostics/ParseDiagnosticsGenerator.swift +++ b/Sources/SwiftParserDiagnostics/ParseDiagnosticsGenerator.swift @@ -546,6 +546,38 @@ public class ParseDiagnosticsGenerator: SyntaxAnyVisitor { return handleEffectSpecifiers(node) } + public override func visit(_ node: GenericRequirementSyntax) -> SyntaxVisitorContinueKind { + if shouldSkip(node) { + return .skipChildren + } + + if let unexpected = node.unexpectedBetweenBodyAndTrailingComma, + let token = unexpected.tokens(satisfying: { $0.tokenKind == .binaryOperator("&&") }).first, + let trailingComma = node.trailingComma, + trailingComma.presence == .missing, + let previous = node.unexpectedBetweenBodyAndTrailingComma?.previousToken(viewMode: .sourceAccurate) + { + + addDiagnostic( + unexpected, + .expectedCommaInWhereClause, + fixIts: [ + FixIt( + message: ReplaceTokensFixIt(replaceTokens: [token], replacement: .commaToken()), + changes: [ + .makeMissing(token), + .makePresent(trailingComma), + FixIt.MultiNodeChange(.replaceTrailingTrivia(token: previous, newTrivia: [])), + ] + ) + ], + handledNodes: [unexpected.id, trailingComma.id] + ) + } + + return .visitChildren + } + public override func visit(_ node: DeinitializerDeclSyntax) -> SyntaxVisitorContinueKind { if shouldSkip(node) { return .skipChildren diff --git a/Sources/SwiftParserDiagnostics/ParserDiagnosticMessages.swift b/Sources/SwiftParserDiagnostics/ParserDiagnosticMessages.swift index 3cd88f30a2a..fbb781617d4 100644 --- a/Sources/SwiftParserDiagnostics/ParserDiagnosticMessages.swift +++ b/Sources/SwiftParserDiagnostics/ParserDiagnosticMessages.swift @@ -131,6 +131,9 @@ extension DiagnosticMessage where Self == StaticParserError { public static var expectedAssignmentInsteadOfComparisonOperator: Self { .init("expected '=' instead of '==' to assign default value for parameter") } + public static var expectedCommaInWhereClause: Self { + .init("expected ',' to separate the requirements of this 'where' clause") + } public static var expectedLeftBraceOrIfAfterElse: Self { .init("expected '{' or 'if' after 'else'") } diff --git a/Tests/SwiftParserTest/translated/RecoveryTests.swift b/Tests/SwiftParserTest/translated/RecoveryTests.swift index a3c04523276..8bd578f231b 100644 --- a/Tests/SwiftParserTest/translated/RecoveryTests.swift +++ b/Tests/SwiftParserTest/translated/RecoveryTests.swift @@ -2174,15 +2174,17 @@ final class RecoveryTests: XCTestCase { } func testRecovery177() { + // rdar://38225184 assertParse( """ - // rdar://38225184 extension Collection where Element == Int 1️⃣&& Index == Int {} """, diagnostics: [ - DiagnosticSpec(message: "unexpected code '&& Index == Int' in extension") - // TODO: Old parser expected error on line 1: expected ',' to separate the requirements of this 'where' clause, Fix-It replacements: 43 - 45 = ',' - ] + DiagnosticSpec(message: "expected ',' to separate the requirements of this 'where' clause", fixIts: ["replace '&&' with ','"]) + ], + fixedSource: """ + extension Collection where Element == Int, Index == Int {} + """ ) }