Skip to content

Commit 450fd89

Browse files
committed
Fix parsing attributes at the start of an accessor
Previously, it would consume an attribute without adding it to the tree if an identifier is not found. Now, it looks ahead to confirm there is an identifier before consuming. Added two test cases to verify parseAccessorIntroducer Fixes: #677
1 parent 689eb41 commit 450fd89

File tree

2 files changed

+39
-14
lines changed

2 files changed

+39
-14
lines changed

Sources/SwiftParser/Declarations.swift

Lines changed: 17 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1519,33 +1519,37 @@ extension Parser {
15191519
struct AccessorIntroducer {
15201520
var attributes: RawAttributeListSyntax?
15211521
var modifier: RawDeclModifierSyntax?
1522-
var introducer: (AccessorKind, RawTokenSyntax)?
1522+
var kind: AccessorKind
1523+
var token: RawTokenSyntax
15231524
}
15241525

1525-
mutating func parseAccessorIntroducer() -> AccessorIntroducer {
1526+
mutating func parseAccessorIntroducer() -> AccessorIntroducer? {
1527+
// Check there is an identifier before consuming
1528+
var look = self.lookahead()
1529+
let _ = look.consumeAttributeList()
1530+
let hasModifier = look.consume(ifAny: [], contextualKeywords: ["mutating", "nonmutating", "__consuming"]) != nil
1531+
guard let (kind, handle) = look.at(anyIn: AccessorKind.self) else {
1532+
return nil
1533+
}
1534+
15261535
let attrs = self.parseAttributeList()
15271536

15281537
// Parse the contextual keywords for 'mutating' and 'nonmutating' before
15291538
// get and set.
15301539
let modifier: RawDeclModifierSyntax?
1531-
if let name = self.consume(ifAny: [], contextualKeywords: ["mutating", "nonmutating", "__consuming"]) {
1540+
if hasModifier {
15321541
modifier = RawDeclModifierSyntax(
1533-
name: name,
1542+
name: self.consumeAnyToken(),
15341543
detail: nil,
15351544
arena: self.arena
15361545
)
15371546
} else {
15381547
modifier = nil
15391548
}
15401549

1541-
guard let (kind, handle) = self.at(anyIn: AccessorKind.self) else {
1542-
return AccessorIntroducer(
1543-
attributes: attrs, modifier: modifier, introducer: nil)
1544-
}
1545-
15461550
let introducer = self.eat(handle)
15471551
return AccessorIntroducer(
1548-
attributes: attrs, modifier: modifier, introducer: (kind, introducer))
1552+
attributes: attrs, modifier: modifier, kind: kind, token: introducer)
15491553
}
15501554

15511555
@_spi(RawSyntax)
@@ -1612,8 +1616,7 @@ extension Parser {
16121616
do {
16131617
var loopProgress = LoopProgressCondition()
16141618
while !self.at(any: [.eof, .rightBrace]) && loopProgress.evaluate(currentToken) {
1615-
let introducer = self.parseAccessorIntroducer()
1616-
guard let (kind, kindToken) = introducer.introducer else {
1619+
guard let introducer = self.parseAccessorIntroducer() else {
16171620
// There can only be an implicit getter if no other accessors were
16181621
// seen before this one.
16191622
guard elements.isEmpty else {
@@ -1649,7 +1652,7 @@ extension Parser {
16491652
//
16501653
// set-name ::= '(' identifier ')'
16511654
let parameter: RawAccessorParameterSyntax?
1652-
if [ AccessorKind.set, .willSet, .didSet ].contains(kind), let lparen = self.consume(if: .leftParen) {
1655+
if [ AccessorKind.set, .willSet, .didSet ].contains(introducer.kind), let lparen = self.consume(if: .leftParen) {
16531656
let (unexpectedBeforeName, name) = self.expectIdentifier()
16541657
let (unexpectedBeforeRParen, rparen) = self.expect(.rightParen)
16551658
parameter = RawAccessorParameterSyntax(
@@ -1681,7 +1684,7 @@ extension Parser {
16811684
elements.append(RawAccessorDeclSyntax(
16821685
attributes: introducer.attributes,
16831686
modifier: introducer.modifier,
1684-
accessorKind: kindToken,
1687+
accessorKind: introducer.token,
16851688
parameter: parameter,
16861689
asyncKeyword: asyncKeyword,
16871690
throwsKeyword: throwsKeyword,

Tests/SwiftParserTest/Declarations.swift

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -188,6 +188,28 @@ final class DeclarationTests: XCTestCase {
188188
async let theVeryLastPhotoWeWant = fetch("4.jpg")
189189
"""
190190
)
191+
192+
AssertParse(
193+
"""
194+
var foo: Int {
195+
@available(swift 5.0)
196+
func myFun() -> Int {
197+
return 42
198+
}
199+
return myFun()
200+
}
201+
"""
202+
)
203+
204+
AssertParse(
205+
"""
206+
var foo: Int {
207+
mutating set {
208+
test += 1
209+
}
210+
}
211+
"""
212+
)
191213
}
192214

193215
func testTypealias() {

0 commit comments

Comments
 (0)