Skip to content

Add diagnostic for comparison in default value #1445

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
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 7 additions & 7 deletions Sources/SwiftParser/Declarations.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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
)
Expand Down Expand Up @@ -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
)
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -1575,7 +1575,7 @@ extension Parser {
typeAnnotationUnwrapped.unexpectedBetweenColonAndType,
arena: self.arena
),
equal: missingToken(.equal, text: nil),
equal: missingToken(.equal),
value: initExpr,
arena: self.arena
)
Expand Down Expand Up @@ -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)
}
Expand Down Expand Up @@ -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)
}
Expand Down Expand Up @@ -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.
Expand Down
17 changes: 14 additions & 3 deletions Sources/SwiftParser/Expressions.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -2314,7 +2325,7 @@ extension Parser {
RawCaseItemSyntax(
pattern: RawPatternSyntax(
RawIdentifierPatternSyntax(
identifier: missingToken(.identifier, text: nil),
identifier: missingToken(.identifier),
arena: self.arena
)
),
Expand All @@ -2325,7 +2336,7 @@ extension Parser {
],
arena: self.arena
),
colon: missingToken(.colon, text: nil),
colon: missingToken(.colon),
arena: self.arena
)
),
Expand Down
2 changes: 1 addition & 1 deletion Sources/SwiftParser/Names.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down
14 changes: 7 additions & 7 deletions Sources/SwiftParser/Parser.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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),
Expand All @@ -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 (
Expand Down Expand Up @@ -505,21 +505,21 @@ 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.
let rightBraceTrivia = self.arena.parseTrivia(source: lookahead.currentToken.leadingTriviaText, position: .leading)
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)
}
Expand Down
4 changes: 2 additions & 2 deletions Sources/SwiftParser/Patterns.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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
)
)
Expand Down Expand Up @@ -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
)
Expand Down
4 changes: 2 additions & 2 deletions Sources/SwiftParser/TokenConsumer.swift
Original file line number Diff line number Diff line change
Expand Up @@ -224,15 +224,15 @@ extension TokenConsumer {
if let (_, handle) = self.at(anyIn: IdentifierTokens.self) {
return self.eat(handle)
}
return missingToken(.identifier, text: nil)
return missingToken(.identifier)
}

@inline(__always)
mutating func expectIdentifierOrRethrowsWithoutRecovery() -> Token {
if let (_, handle) = self.at(anyIn: IdentifierOrRethrowsTokens.self) {
return self.eat(handle)
}
return missingToken(.identifier, text: nil)
return missingToken(.identifier)
}

var canHaveParameterSpecifier: Bool {
Expand Down
4 changes: 2 additions & 2 deletions Sources/SwiftParser/TopLevel.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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
)
Expand Down Expand Up @@ -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
)
Expand Down
17 changes: 17 additions & 0 deletions Sources/SwiftParserDiagnostics/ParseDiagnosticsGenerator.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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'")
}
Expand Down
12 changes: 7 additions & 5 deletions Tests/SwiftParserTest/translated/RecoveryTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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) {}
"""
)
}

Expand Down