Skip to content

[5.8] Fix bugs found by mutating source in SwiftParserTest using alternative token choices #1345

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
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")]
)
}
}