Skip to content

Commit

Permalink
Merge pull request #1345 from ahoppen/ahoppen/5.8-fixes-found-by-sour…
Browse files Browse the repository at this point in the history
…ce-alteration

[5.8] Fix bugs found by mutating source in SwiftParserTest using alternative token choices
  • Loading branch information
ahoppen authored Feb 20, 2023
2 parents e1ee3cf + 42c113d commit cd793ad
Show file tree
Hide file tree
Showing 10 changed files with 67 additions and 17 deletions.
8 changes: 5 additions & 3 deletions Sources/SwiftParser/Attributes.swift
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ extension Parser {
mutating func parseAttribute() -> RawAttributeListSyntax.Element {
if self.at(.poundIfKeyword) {
return .ifConfigDecl(
self.parsePoundIfDirective { parser -> RawAttributeListSyntax.Element in
self.parsePoundIfDirective { (parser, _) -> RawAttributeListSyntax.Element in
return parser.parseAttribute()
} syntax: { parser, attributes in
return .attributes(RawAttributeListSyntax(elements: attributes, arena: parser.arena))
Expand Down Expand Up @@ -494,7 +494,7 @@ extension Parser {
mutating func parseObjectiveCSelector() -> RawObjCSelectorSyntax {
var elements = [RawObjCSelectorPieceSyntax]()
var loopProgress = LoopProgressCondition()
while !self.at(any: [.eof, .rightParen]) && loopProgress.evaluate(currentToken) {
while loopProgress.evaluate(currentToken) {
// Empty selector piece.
if let colon = self.consume(if: .colon) {
elements.append(
Expand All @@ -507,7 +507,7 @@ extension Parser {
continue
}

if self.at(.identifier) || self.currentToken.isKeyword {
if self.at(any: [.identifier, .wildcardKeyword]) || self.currentToken.isKeyword {
let name = self.consumeAnyToken()

// If we hit a ')' we may have a zero-argument selector.
Expand All @@ -531,6 +531,8 @@ extension Parser {
arena: self.arena
)
)
} else {
break
}
}
return RawObjCSelectorSyntax(elements: elements, arena: self.arena)
Expand Down
2 changes: 1 addition & 1 deletion Sources/SwiftParser/Declarations.swift
Original file line number Diff line number Diff line change
Expand Up @@ -165,7 +165,7 @@ extension Parser {
public mutating func parseDeclaration(inMemberDeclList: Bool = false) -> RawDeclSyntax {
switch self.at(anyIn: PoundDeclarationStart.self) {
case (.poundIfKeyword, _)?:
let directive = self.parsePoundIfDirective { parser in
let directive = self.parsePoundIfDirective { (parser, _) in
let parsedDecl = parser.parseDeclaration()
let semicolon = parser.consume(if: .semicolon)
return RawMemberDeclListItemSyntax(
Expand Down
6 changes: 3 additions & 3 deletions Sources/SwiftParser/Directives.swift
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ extension Parser {
/// into a syntax collection.
@_spi(RawSyntax)
public mutating func parsePoundIfDirective<Element: RawSyntaxNodeProtocol>(
_ parseElement: (inout Parser) -> Element?,
_ parseElement: (_ parser: inout Parser, _ isFirstElement: Bool) -> Element?,
addSemicolonIfNeeded: (_ lastElement: Element, _ newItemAtStartOfLine: Bool, _ parser: inout Parser) -> Element? = { _, _, _ in nil },
syntax: (inout Parser, [Element]) -> RawIfConfigClauseSyntax.Elements?
) -> RawIfConfigDeclSyntax {
Expand All @@ -83,7 +83,7 @@ extension Parser {
do {
var firstIteration = true
var loopProgress = LoopProgressCondition()
while let poundIfHandle = self.canRecoverTo(any: firstIteration ? [.poundIfKeyword] : [.poundIfKeyword, .poundElseifKeyword, .poundElseKeyword]),
while let poundIfHandle = self.canRecoverTo(any: firstIteration ? [.poundIfKeyword] : [.poundElseifKeyword, .poundElseKeyword]),
loopProgress.evaluate(self.currentToken)
{
let (unexpectedBeforePoundIf, poundIf) = self.eat(poundIfHandle)
Expand All @@ -104,7 +104,7 @@ extension Parser {
var elementsProgress = LoopProgressCondition()
while !self.at(any: [.eof, .poundElseKeyword, .poundElseifKeyword, .poundEndifKeyword]) && elementsProgress.evaluate(currentToken) {
let newItemAtStartOfLine = self.currentToken.isAtStartOfLine
guard let element = parseElement(&self), !element.isEmpty else {
guard let element = parseElement(&self, elements.isEmpty), !element.isEmpty else {
break
}
if let lastElement = elements.last, let fixedUpLastItem = addSemicolonIfNeeded(lastElement, newItemAtStartOfLine, &self) {
Expand Down
5 changes: 4 additions & 1 deletion Sources/SwiftParser/Expressions.swift
Original file line number Diff line number Diff line change
Expand Up @@ -624,7 +624,10 @@ extension Parser {
) -> RawExprSyntax {
assert(self.at(.poundIfKeyword))

let config = self.parsePoundIfDirective { parser -> RawExprSyntax? in
let config = self.parsePoundIfDirective { (parser, isFirstElement) -> RawExprSyntax? in
if !isFirstElement {
return nil
}
let head: RawExprSyntax
if parser.at(any: [.period, .prefixPeriod]) {
head = parser.parseDottedExpressionSuffix(nil)
Expand Down
6 changes: 0 additions & 6 deletions Sources/SwiftParser/RawTokenKindSubset.swift
Original file line number Diff line number Diff line change
Expand Up @@ -773,16 +773,13 @@ enum PrimaryExpressionStart: RawTokenKindSubset {
enum ExpressionStart: RawTokenKindSubset {
case awaitTryMove(AwaitTryMove)
case expressionPrefixOperator(ExpressionPrefixOperator)
case matchingPatternStart(MatchingPatternStart)
case primaryExpressionStart(PrimaryExpressionStart)

init?(lexeme: Lexer.Lexeme) {
if let subset = AwaitTryMove(lexeme: lexeme) {
self = .awaitTryMove(subset)
} else if let subset = ExpressionPrefixOperator(lexeme: lexeme) {
self = .expressionPrefixOperator(subset)
} else if let subset = MatchingPatternStart(lexeme: lexeme) {
self = .matchingPatternStart(subset)
} else if let subset = PrimaryExpressionStart(lexeme: lexeme) {
self = .primaryExpressionStart(subset)
} else {
Expand All @@ -793,15 +790,13 @@ enum ExpressionStart: RawTokenKindSubset {
static var allCases: [ExpressionStart] {
return AwaitTryMove.allCases.map(Self.awaitTryMove)
+ ExpressionPrefixOperator.allCases.map(Self.expressionPrefixOperator)
+ MatchingPatternStart.allCases.map(Self.matchingPatternStart)
+ PrimaryExpressionStart.allCases.map(Self.primaryExpressionStart)
}

var rawTokenKind: RawTokenKind {
switch self {
case .awaitTryMove(let underlyingKind): return underlyingKind.rawTokenKind
case .expressionPrefixOperator(let underlyingKind): return underlyingKind.rawTokenKind
case .matchingPatternStart(let underlyingKind): return underlyingKind.rawTokenKind
case .primaryExpressionStart(let underlyingKind): return underlyingKind.rawTokenKind
}
}
Expand All @@ -810,7 +805,6 @@ enum ExpressionStart: RawTokenKindSubset {
switch self {
case .awaitTryMove(let underlyingKind): return underlyingKind.contextualKeyword
case .expressionPrefixOperator(let underlyingKind): return underlyingKind.contextualKeyword
case .matchingPatternStart(let underlyingKind): return underlyingKind.contextualKeyword
case .primaryExpressionStart(let underlyingKind): return underlyingKind.contextualKeyword
}
}
Expand Down
5 changes: 4 additions & 1 deletion Sources/SwiftParser/Statements.swift
Original file line number Diff line number Diff line change
Expand Up @@ -752,7 +752,7 @@ extension Parser {
elements.append(
.ifConfigDecl(
self.parsePoundIfDirective(
{ $0.parseSwitchCases(allowStandaloneStmtRecovery: allowStandaloneStmtRecovery) },
{ (parser, _) in parser.parseSwitchCases(allowStandaloneStmtRecovery: allowStandaloneStmtRecovery) },
syntax: { parser, cases in
guard cases.count == 1, let firstCase = cases.first else {
assert(cases.isEmpty)
Expand All @@ -766,6 +766,9 @@ extension Parser {
} else if allowStandaloneStmtRecovery && (self.atStartOfExpression() || self.atStartOfStatement() || self.atStartOfDeclaration()) {
// Synthesize a label for the stamenent or declaration that isn't coverd by a case right now.
let statements = parseSwitchCaseBody()
if statements.isEmpty {
break
}
elements.append(
.switchCase(
RawSwitchCaseSyntax(
Expand Down
4 changes: 2 additions & 2 deletions Sources/SwiftParser/TopLevel.swift
Original file line number Diff line number Diff line change
Expand Up @@ -201,8 +201,8 @@ extension Parser {
/// wrapping declaration instead of being consumed by lookeahead.
private mutating func parseItem(isAtTopLevel: Bool = false, allowInitDecl: Bool = true) -> RawCodeBlockItemSyntax.Item {
if self.at(.poundIfKeyword) {
let directive = self.parsePoundIfDirective {
$0.parseCodeBlockItem()
let directive = self.parsePoundIfDirective { (parser, _) in
parser.parseCodeBlockItem()
} addSemicolonIfNeeded: { lastElement, newItemAtStartOfLine, parser in
if lastElement.semicolon == nil && !newItemAtStartOfLine {
return RawCodeBlockItemSyntax(
Expand Down
7 changes: 7 additions & 0 deletions Tests/SwiftParserTest/AttributeTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,13 @@ final class AttributeTests: XCTestCase {
func f(_: Int, _: Int, _: Int, _: Int, _: Int) { }
"""
)

AssertParse(
"""
@objc(_:)
func f(_: Int)
"""
)
}

func testRethrowsAttribute() {
Expand Down
16 changes: 16 additions & 0 deletions Tests/SwiftParserTest/DirectiveTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,22 @@ final class DirectiveTests: XCTestCase {
)
}

func testPostfixIfConfigExpressionContainsPoundIf() {
AssertParse(
"""
b
#if true
.a
1️⃣#if true
#endif
#endif
""",
diagnostics: [
DiagnosticSpec(message: "unexpected code in conditional compilation block")
]
)
}

func testSourceLocation() {
AssertParse(
"""
Expand Down
25 changes: 25 additions & 0 deletions Tests/SwiftParserTest/StatementTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -603,4 +603,29 @@ final class StatementTests: XCTestCase {
)
)
}

func testPatternExprInSwitchCaseItem() {
AssertParse(
"""
switch x {
case a:
1️⃣is
}
""",
diagnostics: [
DiagnosticSpec(message: "unexpected 'is' keyword in 'switch' statement")
]
)
}

func testStandaloneAtCaseInSwitch() {
AssertParse(
"""
switch x {
1️⃣@case
}
""",
diagnostics: [DiagnosticSpec(message: "unexpected code '@case' in 'switch' statement")]
)
}
}

0 comments on commit cd793ad

Please sign in to comment.