From 0eb8fd7f90d4937cab19ee88353e9e8f52747733 Mon Sep 17 00:00:00 2001 From: Pavel Yaskevich Date: Thu, 15 Jun 2023 09:59:18 -0700 Subject: [PATCH] InitAccessors: Parse initializer exprs associated with computed properties that have `init` accessor Initialization expressions are not allowed on computed properties but if a property has `init` accessor it should be allowed because it could be used by a memberwise initializer. (cherry picked from commit b05cd4e62a6fc7ae6c9fe2ae7773f5dcd478d493) --- Sources/SwiftParser/Lookahead.swift | 6 +- Tests/SwiftParserTest/DeclarationTests.swift | 78 ++++++++++++++++++++ 2 files changed, 81 insertions(+), 3 deletions(-) diff --git a/Sources/SwiftParser/Lookahead.swift b/Sources/SwiftParser/Lookahead.swift index aabb189d0e8..333e1967251 100644 --- a/Sources/SwiftParser/Lookahead.swift +++ b/Sources/SwiftParser/Lookahead.swift @@ -254,7 +254,7 @@ extension Parser.Lookahead { // If we have a 'didSet' or a 'willSet' label, disambiguate immediately as // an accessor block. let nextToken = self.peek() - if TokenSpec(.didSet) ~= nextToken || TokenSpec(.willSet) ~= nextToken { + if TokenSpec(.didSet) ~= nextToken || TokenSpec(.willSet) ~= nextToken || TokenSpec(.`init`) ~= nextToken { return true } @@ -278,8 +278,8 @@ extension Parser.Lookahead { } } - // Check if we have 'didSet'/'willSet' after attributes. - return lookahead.at(.keyword(.didSet), .keyword(.willSet)) + // Check if we have 'didSet'/'willSet' or 'init' after attributes. + return lookahead.at(.keyword(.didSet), .keyword(.willSet), .keyword(.`init`)) } } diff --git a/Tests/SwiftParserTest/DeclarationTests.swift b/Tests/SwiftParserTest/DeclarationTests.swift index e9e36573e75..7d313bab79d 100644 --- a/Tests/SwiftParserTest/DeclarationTests.swift +++ b/Tests/SwiftParserTest/DeclarationTests.swift @@ -1960,4 +1960,82 @@ final class DeclarationTests: XCTestCase { fixedSource: "protocol X<<#identifier#>> {}" ) } + + func testInitAccessorsWithDefaultValues() { + assertParse( + """ + struct Test { + var pair: (Int, Int) = (42, 0) { + init(initialValue) {} + + get { (0, 42) } + set { } + } + } + """ + ) + + assertParse( + """ + struct Test { + var pair: (Int, Int) = (42, 0) { + init initializes(a) {} + + get { (0, 42) } + set { } + } + } + """ + ) + + assertParse( + """ + struct Test { + var pair: (Int, Int) = (42, 0) { + get { (0, 42) } + set { } + + init(initialValue1️⃣) {} + } + } + """, + substructure: Syntax( + InitializerDeclSyntax( + initKeyword: .keyword(.`init`), + signature: FunctionSignatureSyntax( + input: ParameterClauseSyntax( + leftParen: .leftParenToken(), + parameterList: FunctionParameterListSyntax([ + FunctionParameterSyntax( + firstName: .identifier("initialValue"), + colon: .colonToken(presence: .missing), + type: TypeSyntax(MissingTypeSyntax(placeholder: .identifier("<#type#>", presence: .missing))) + ) + ]), + rightParen: .rightParenToken(trailingTrivia: .space) + ) + ), + body: CodeBlockSyntax( + leftBrace: .leftBraceToken(), + statements: CodeBlockItemListSyntax([]), + rightBrace: .rightBraceToken() + ) + ) + ), + diagnostics: [ + DiagnosticSpec(message: "expected ':' and type in parameter", fixIts: ["insert ':' and type"]) + ], + fixedSource: + """ + struct Test { + var pair: (Int, Int) = (42, 0) { + get { (0, 42) } + set { } + + init(initialValue: <#type#>) {} + } + } + """ + ) + } }