From cf38c183ef1cf4725399e1f8df8f6d12a6b7ffda Mon Sep 17 00:00:00 2001 From: Kim de Vos Date: Sat, 25 Mar 2023 19:29:41 +0100 Subject: [PATCH 1/2] Add diagnostic for comparison in default value --- Sources/SwiftParser/Declarations.swift | 2 +- Sources/SwiftParser/Expressions.swift | 13 ++++++++++++- .../ParseDiagnosticsGenerator.swift | 17 +++++++++++++++++ .../ParserDiagnosticMessages.swift | 3 +++ .../translated/RecoveryTests.swift | 12 +++++++----- 5 files changed, 40 insertions(+), 7 deletions(-) diff --git a/Sources/SwiftParser/Declarations.swift b/Sources/SwiftParser/Declarations.swift index 458cb14363f..2ba3c8e612a 100644 --- a/Sources/SwiftParser/Declarations.swift +++ b/Sources/SwiftParser/Declarations.swift @@ -1229,7 +1229,7 @@ extension Parser { } let defaultArgument: RawInitializerClauseSyntax? - if self.at(.equal) { + if self.at(.equal) || self.atContextualPunctuator("==") { defaultArgument = self.parseDefaultArgument() } else { defaultArgument = nil diff --git a/Sources/SwiftParser/Expressions.swift b/Sources/SwiftParser/Expressions.swift index b2774df179f..01c922e3202 100644 --- a/Sources/SwiftParser/Expressions.swift +++ b/Sources/SwiftParser/Expressions.swift @@ -1671,7 +1671,18 @@ extension Parser { extension Parser { @_spi(RawSyntax) public mutating func parseDefaultArgument() -> RawInitializerClauseSyntax { - let (unexpectedBeforeEq, eq) = self.expect(.equal) + let unexpectedBeforeEq: RawUnexpectedNodesSyntax? + let eq: RawTokenSyntax + if let comparison = self.consumeIfContextualPunctuator("==") { + unexpectedBeforeEq = RawUnexpectedNodesSyntax( + elements: [RawSyntax(comparison)], + arena: self.arena + ) + eq = missingToken(.equal) + } else { + (unexpectedBeforeEq, eq) = self.expect(.equal) + } + let expr = self.parseExpression() return RawInitializerClauseSyntax( unexpectedBeforeEq, diff --git a/Sources/SwiftParserDiagnostics/ParseDiagnosticsGenerator.swift b/Sources/SwiftParserDiagnostics/ParseDiagnosticsGenerator.swift index f79f0a526ca..599164b8543 100644 --- a/Sources/SwiftParserDiagnostics/ParseDiagnosticsGenerator.swift +++ b/Sources/SwiftParserDiagnostics/ParseDiagnosticsGenerator.swift @@ -776,6 +776,23 @@ public class ParseDiagnosticsGenerator: SyntaxAnyVisitor { if shouldSkip(node) { return .skipChildren } + + if let unexpected = node.unexpectedBeforeEqual, + unexpected.first?.as(TokenSyntax.self)?.tokenKind == .binaryOperator("==") + { + addDiagnostic( + unexpected, + .expectedAssignmentInsteadOfComparisonOperator, + fixIts: [ + FixIt( + message: ReplaceTokensFixIt(replaceTokens: [.binaryOperator("==")], replacement: node.equal), + changes: [.makeMissing(unexpected), .makePresent(node.equal, leadingTrivia: [])] + ) + ], + handledNodes: [unexpected.id, node.equal.id] + ) + } + if node.equal.presence == .missing { exchangeTokens( unexpected: node.unexpectedBeforeEqual, diff --git a/Sources/SwiftParserDiagnostics/ParserDiagnosticMessages.swift b/Sources/SwiftParserDiagnostics/ParserDiagnosticMessages.swift index 44ae34c66d5..47064aea51f 100644 --- a/Sources/SwiftParserDiagnostics/ParserDiagnosticMessages.swift +++ b/Sources/SwiftParserDiagnostics/ParserDiagnosticMessages.swift @@ -128,6 +128,9 @@ extension DiagnosticMessage where Self == StaticParserError { public static var expectedExpressionAfterTry: Self { .init("expected expression after 'try'") } + public static var expectedAssignmentInsteadOfComparisonOperator: Self { + .init("expected '=' instead of '==' to assign default value for parameter") + } 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 5002ffdb26e..40f4a03eb6a 100644 --- a/Tests/SwiftParserTest/translated/RecoveryTests.swift +++ b/Tests/SwiftParserTest/translated/RecoveryTests.swift @@ -2139,16 +2139,18 @@ final class RecoveryTests: XCTestCase { } func testRecovery176() { + // rdar://problem/22478168 + // https://github.com/apple/swift/issues/53396 assertParse( """ - // rdar://problem/22478168 - // https://github.com/apple/swift/issues/53396 func f_53396(a: Int 1️⃣== 0) {} """, diagnostics: [ - // TODO: Old parser expected error on line 3: expected '=' instead of '==' to assign default value for parameter, Fix-It replacements: 21 - 23 = '=' - DiagnosticSpec(message: "unexpected code '== 0' in parameter clause") - ] + DiagnosticSpec(message: "expected '=' instead of '==' to assign default value for parameter", fixIts: ["replace '==' with '='"]) + ], + fixedSource: """ + func f_53396(a: Int = 0) {} + """ ) } From 0643ff3de15a8764a83710eaec915d777920f65d Mon Sep 17 00:00:00 2001 From: Kim de Vos Date: Sat, 25 Mar 2023 20:54:53 +0100 Subject: [PATCH 2/2] Remove value that is optional --- Sources/SwiftParser/Declarations.swift | 12 ++++++------ Sources/SwiftParser/Expressions.swift | 4 ++-- Sources/SwiftParser/Names.swift | 2 +- Sources/SwiftParser/Parser.swift | 14 +++++++------- Sources/SwiftParser/Patterns.swift | 4 ++-- Sources/SwiftParser/TokenConsumer.swift | 4 ++-- Sources/SwiftParser/TopLevel.swift | 4 ++-- 7 files changed, 22 insertions(+), 22 deletions(-) diff --git a/Sources/SwiftParser/Declarations.swift b/Sources/SwiftParser/Declarations.swift index 2ba3c8e612a..e6dde47ee44 100644 --- a/Sources/SwiftParser/Declarations.swift +++ b/Sources/SwiftParser/Declarations.swift @@ -192,7 +192,7 @@ extension Parser { lastElement.unexpectedBeforeDecl, decl: lastElement.decl, lastElement.unexpectedBetweenDeclAndSemicolon, - semicolon: parser.missingToken(.semicolon, text: nil), + semicolon: parser.missingToken(.semicolon), lastElement.unexpectedAfterSemicolon, arena: parser.arena ) @@ -835,7 +835,7 @@ extension Parser { lastItem.unexpectedBeforeDecl, decl: lastItem.decl, lastItem.unexpectedBetweenDeclAndSemicolon, - semicolon: self.missingToken(.semicolon, text: nil), + semicolon: self.missingToken(.semicolon), lastItem.unexpectedAfterSemicolon, arena: self.arena ) @@ -1575,7 +1575,7 @@ extension Parser { typeAnnotationUnwrapped.unexpectedBetweenColonAndType, arena: self.arena ), - equal: missingToken(.equal, text: nil), + equal: missingToken(.equal), value: initExpr, arena: self.arena ) @@ -1742,7 +1742,7 @@ extension Parser { let lbrace: RawTokenSyntax if self.at(anyIn: AccessorKind.self) != nil { unexpectedBeforeLBrace = nil - lbrace = missingToken(.leftBrace, text: nil) + lbrace = missingToken(.leftBrace) } else { (unexpectedBeforeLBrace, lbrace) = self.expect(.leftBrace) } @@ -1831,7 +1831,7 @@ extension Parser { let equal: RawTokenSyntax if let colon = self.consume(if: .colon) { unexpectedBeforeEqual = RawUnexpectedNodesSyntax(elements: [RawSyntax(colon)], arena: self.arena) - equal = missingToken(.equal, text: nil) + equal = missingToken(.equal) } else { (unexpectedBeforeEqual, equal) = self.expect(.equal) } @@ -1895,7 +1895,7 @@ extension Parser { } else { unexpectedBeforeName = nil } - name = missingToken(.binaryOperator, text: nil) + name = missingToken(.binaryOperator) } // Eat any subsequent tokens that are not separated to the operator by trivia. diff --git a/Sources/SwiftParser/Expressions.swift b/Sources/SwiftParser/Expressions.swift index 01c922e3202..afe563e7bb0 100644 --- a/Sources/SwiftParser/Expressions.swift +++ b/Sources/SwiftParser/Expressions.swift @@ -2325,7 +2325,7 @@ extension Parser { RawCaseItemSyntax( pattern: RawPatternSyntax( RawIdentifierPatternSyntax( - identifier: missingToken(.identifier, text: nil), + identifier: missingToken(.identifier), arena: self.arena ) ), @@ -2336,7 +2336,7 @@ extension Parser { ], arena: self.arena ), - colon: missingToken(.colon, text: nil), + colon: missingToken(.colon), arena: self.arena ) ), diff --git a/Sources/SwiftParser/Names.swift b/Sources/SwiftParser/Names.swift index cff52f0f125..5b848e8d0c9 100644 --- a/Sources/SwiftParser/Names.swift +++ b/Sources/SwiftParser/Names.swift @@ -28,7 +28,7 @@ extension Parser { if let dollarIdent = self.consume(if: .dollarIdentifier) { return ( RawUnexpectedNodesSyntax(elements: [RawSyntax(dollarIdent)], arena: self.arena), - self.missingToken(.identifier, text: nil) + self.missingToken(.identifier) ) } else { if let wildcardToken = self.consume(if: .wildcard) { diff --git a/Sources/SwiftParser/Parser.swift b/Sources/SwiftParser/Parser.swift index c42f6d563b5..707a9aa54b7 100644 --- a/Sources/SwiftParser/Parser.swift +++ b/Sources/SwiftParser/Parser.swift @@ -439,7 +439,7 @@ extension Parser { if let number = self.consume(if: .integerLiteral, .floatingLiteral, .dollarIdentifier) { return ( RawUnexpectedNodesSyntax(elements: [RawSyntax(number)], arena: self.arena), - self.missingToken(.identifier, text: nil) + self.missingToken(.identifier) ) } else if keywordRecovery, (self.currentToken.isLexerClassifiedKeyword || self.currentToken.rawTokenKind == .wildcard), @@ -448,7 +448,7 @@ extension Parser { let keyword = self.consumeAnyToken() return ( RawUnexpectedNodesSyntax(elements: [RawSyntax(keyword)], arena: self.arena), - self.missingToken(.identifier, text: nil) + self.missingToken(.identifier) ) } return ( @@ -505,7 +505,7 @@ extension Parser { var lookahead = self.lookahead() guard let recoveryHandle = lookahead.canRecoverTo(.rightBrace) else { // We can't recover to '}'. Synthesize it. - return (nil, self.missingToken(.rightBrace, text: nil)) + return (nil, self.missingToken(.rightBrace)) } // We can recover to a '}'. Decide whether we want to eat it based on its indentation. @@ -513,13 +513,13 @@ extension Parser { switch (indentation(introducer.leadingTriviaPieces), indentation(rightBraceTrivia)) { // Catch cases where the brace has known indentation that is less than that of `introducer`, in which case we don't want to consume it. case (.spaces(let introducerSpaces), .spaces(let rightBraceSpaces)) where rightBraceSpaces < introducerSpaces: - return (nil, self.missingToken(.rightBrace, text: nil)) + return (nil, self.missingToken(.rightBrace)) case (.tabs(let introducerTabs), .tabs(let rightBraceTabs)) where rightBraceTabs < introducerTabs: - return (nil, self.missingToken(.rightBrace, text: nil)) + return (nil, self.missingToken(.rightBrace)) case (.spaces, .tabs(0)): - return (nil, self.missingToken(.rightBrace, text: nil)) + return (nil, self.missingToken(.rightBrace)) case (.tabs, .spaces(0)): - return (nil, self.missingToken(.rightBrace, text: nil)) + return (nil, self.missingToken(.rightBrace)) default: return self.eat(recoveryHandle) } diff --git a/Sources/SwiftParser/Patterns.swift b/Sources/SwiftParser/Patterns.swift index dff7b6c3aa0..d4fce79e88f 100644 --- a/Sources/SwiftParser/Patterns.swift +++ b/Sources/SwiftParser/Patterns.swift @@ -141,7 +141,7 @@ extension Parser { return RawPatternSyntax( RawIdentifierPatternSyntax( RawUnexpectedNodesSyntax([keyword], arena: self.arena), - identifier: missingToken(.identifier, text: nil), + identifier: missingToken(.identifier), arena: self.arena ) ) @@ -178,7 +178,7 @@ extension Parser { // Recovery if the user forgot to add ':' let result = self.parseResultType() type = RawTypeAnnotationSyntax( - colon: self.missingToken(.colon, text: nil), + colon: self.missingToken(.colon), type: result, arena: self.arena ) diff --git a/Sources/SwiftParser/TokenConsumer.swift b/Sources/SwiftParser/TokenConsumer.swift index d4b50596b2f..40ef3572009 100644 --- a/Sources/SwiftParser/TokenConsumer.swift +++ b/Sources/SwiftParser/TokenConsumer.swift @@ -224,7 +224,7 @@ extension TokenConsumer { if let (_, handle) = self.at(anyIn: IdentifierTokens.self) { return self.eat(handle) } - return missingToken(.identifier, text: nil) + return missingToken(.identifier) } @inline(__always) @@ -232,7 +232,7 @@ extension TokenConsumer { if let (_, handle) = self.at(anyIn: IdentifierOrRethrowsTokens.self) { return self.eat(handle) } - return missingToken(.identifier, text: nil) + return missingToken(.identifier) } var canHaveParameterSpecifier: Bool { diff --git a/Sources/SwiftParser/TopLevel.swift b/Sources/SwiftParser/TopLevel.swift index 4152c8bf77a..3523d78ee99 100644 --- a/Sources/SwiftParser/TopLevel.swift +++ b/Sources/SwiftParser/TopLevel.swift @@ -72,7 +72,7 @@ extension Parser { lastItem.unexpectedBeforeItem, item: .init(lastItem.item)!, lastItem.unexpectedBetweenItemAndSemicolon, - semicolon: self.missingToken(.semicolon, text: nil), + semicolon: self.missingToken(.semicolon), lastItem.unexpectedAfterSemicolon, arena: self.arena ) @@ -236,7 +236,7 @@ extension Parser { lastElement.unexpectedBeforeItem, item: .init(lastElement.item)!, lastElement.unexpectedBetweenItemAndSemicolon, - semicolon: parser.missingToken(.semicolon, text: nil), + semicolon: parser.missingToken(.semicolon), lastElement.unexpectedAfterSemicolon, arena: parser.arena )