Skip to content

Commit e7dc9a4

Browse files
committed
Fixed testAvailabilityQueryUnavailability34a
1 parent ced4443 commit e7dc9a4

File tree

5 files changed

+83
-18
lines changed

5 files changed

+83
-18
lines changed

Sources/SwiftParser/Statements.swift

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -187,13 +187,14 @@ extension Parser {
187187
var loopProgress = LoopProgressCondition()
188188
repeat {
189189
let condition = self.parseConditionElement()
190-
let unexpectedBeforeKeepGoing: RawUnexpectedNodesSyntax?
190+
var unexpectedBeforeKeepGoing: RawUnexpectedNodesSyntax? = nil
191+
if let equalOperator = self.consumeIfContextualPunctuator("=="), let falseKeyword = self.consume(if: .keyword(.false)) {
192+
unexpectedBeforeKeepGoing = RawUnexpectedNodesSyntax([equalOperator, falseKeyword], arena: self.arena)
193+
}
191194
keepGoing = self.consume(if: .comma)
192195
if keepGoing == nil, let andOperator = self.consumeIfContextualPunctuator("&&") {
193196
unexpectedBeforeKeepGoing = RawUnexpectedNodesSyntax([andOperator], arena: self.arena)
194197
keepGoing = missingToken(.comma)
195-
} else {
196-
unexpectedBeforeKeepGoing = nil
197198
}
198199
elements.append(
199200
RawConditionElementSyntax(

Sources/SwiftParserDiagnostics/ParseDiagnosticsGenerator.swift

Lines changed: 51 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,17 @@ import SwiftDiagnostics
1414
import SwiftParser
1515
@_spi(RawSyntax) import SwiftSyntax
1616

17+
fileprivate func getTokens(between first: TokenSyntax, and second: TokenSyntax) -> [TokenSyntax] {
18+
var tokens: [TokenSyntax] = []
19+
var currentToken = first
20+
while currentToken != second {
21+
tokens.append(currentToken)
22+
currentToken = currentToken.nextToken ?? second
23+
}
24+
tokens.append(second)
25+
return tokens
26+
}
27+
1728
fileprivate extension TokenSyntax {
1829
/// Assuming this token is a `poundAvailableKeyword` or `poundUnavailableKeyword`
1930
/// returns the opposite keyword.
@@ -239,7 +250,7 @@ public class ParseDiagnosticsGenerator: SyntaxAnyVisitor {
239250
unexpectedTokenCondition: isOfSameKind,
240251
correctTokens: [specifier],
241252
message: { _ in misspelledError },
242-
moveFixIt: { ReplaceTokensFixIt(replaceTokens: $0, replacement: specifier) },
253+
moveFixIt: { ReplaceTokensFixIt(replaceTokens: $0, replacements: [specifier]) },
243254
removeRedundantFixIt: { RemoveRedundantFixIt(removeTokens: $0) }
244255
)
245256
}
@@ -434,7 +445,7 @@ public class ParseDiagnosticsGenerator: SyntaxAnyVisitor {
434445
unexpectedTokenCondition: { $0.text == "||" },
435446
correctTokens: [node.trailingComma],
436447
message: { _ in .joinPlatformsUsingComma },
437-
moveFixIt: { ReplaceTokensFixIt(replaceTokens: $0, replacement: trailingComma) }
448+
moveFixIt: { ReplaceTokensFixIt(replaceTokens: $0, replacements: [trailingComma]) }
438449
)
439450
}
440451
return .visitChildren
@@ -463,13 +474,42 @@ public class ParseDiagnosticsGenerator: SyntaxAnyVisitor {
463474
if shouldSkip(node) {
464475
return .skipChildren
465476
}
477+
if let unexpected = node.unexpectedBetweenConditionAndTrailingComma,
478+
let availability = node.condition.as(AvailabilityConditionSyntax.self),
479+
let (_, falseKeyword) = unexpected.twoTokens(
480+
firstSatisfying: { $0.tokenKind == .binaryOperator("==") },
481+
secondSatisfying: { $0.tokenKind == .keyword(.false) }
482+
)
483+
{
484+
// Diagnose #available used as an expression
485+
let negatedAvailabilityKeyword = availability.availabilityKeyword.negatedAvailabilityKeyword
486+
let negatedCoditionElement = ConditionElementSyntax(
487+
condition: .availability(availability.with(\.availabilityKeyword, negatedAvailabilityKeyword)),
488+
trailingComma: node.trailingComma
489+
)
490+
if let negatedAvailability = negatedCoditionElement.condition.as(AvailabilityConditionSyntax.self) {
491+
addDiagnostic(
492+
unexpected,
493+
AvailabilityConditionAsExpression(availabilityToken: availability.availabilityKeyword, negatedAvailabilityToken: negatedAvailabilityKeyword),
494+
fixIts: [
495+
FixIt(
496+
message: ReplaceTokensFixIt(replaceTokens: getTokens(between: availability.availabilityKeyword, and: falseKeyword), replacements: getTokens(between: negatedAvailability.availabilityKeyword, and: negatedAvailability.rightParen)),
497+
changes: [
498+
.replace(oldNode: Syntax(node), newNode: Syntax(negatedCoditionElement))
499+
]
500+
)
501+
],
502+
handledNodes: [unexpected.id]
503+
)
504+
}
505+
}
466506
if let trailingComma = node.trailingComma {
467507
exchangeTokens(
468508
unexpected: node.unexpectedBetweenConditionAndTrailingComma,
469509
unexpectedTokenCondition: { $0.text == "&&" },
470510
correctTokens: [node.trailingComma],
471511
message: { _ in .joinConditionsUsingComma },
472-
moveFixIt: { ReplaceTokensFixIt(replaceTokens: $0, replacement: trailingComma) }
512+
moveFixIt: { ReplaceTokensFixIt(replaceTokens: $0, replacements: [trailingComma]) }
473513
)
474514
}
475515
return .visitChildren
@@ -662,7 +702,7 @@ public class ParseDiagnosticsGenerator: SyntaxAnyVisitor {
662702
.typeParameterPackEllipsis,
663703
fixIts: [
664704
FixIt(
665-
message: ReplaceTokensFixIt(replaceTokens: [unexpectedEllipsis], replacement: .keyword(.each)),
705+
message: ReplaceTokensFixIt(replaceTokens: [unexpectedEllipsis], replacements: [.keyword(.each)]),
666706
changes: [
667707
.makeMissing(unexpected),
668708
.makePresent(each, trailingTrivia: .space),
@@ -678,7 +718,7 @@ public class ParseDiagnosticsGenerator: SyntaxAnyVisitor {
678718
unexpectedTokenCondition: { $0.tokenKind == .keyword(.class) },
679719
correctTokens: [inheritedTypeName],
680720
message: { _ in StaticParserError.classConstraintCanOnlyBeUsedInProtocol },
681-
moveFixIt: { ReplaceTokensFixIt(replaceTokens: $0, replacement: inheritedTypeName) }
721+
moveFixIt: { ReplaceTokensFixIt(replaceTokens: $0, replacements: [inheritedTypeName]) }
682722
)
683723
}
684724
return .visitChildren
@@ -709,7 +749,7 @@ public class ParseDiagnosticsGenerator: SyntaxAnyVisitor {
709749
NegatedAvailabilityCondition(avaialabilityCondition: availability, negatedAvailabilityKeyword: negatedAvailabilityKeyword),
710750
fixIts: [
711751
FixIt(
712-
message: ReplaceTokensFixIt(replaceTokens: [operatorToken, availability.availabilityKeyword], replacement: negatedAvailabilityKeyword),
752+
message: ReplaceTokensFixIt(replaceTokens: [operatorToken, availability.availabilityKeyword], replacements: [negatedAvailabilityKeyword]),
713753
changes: [
714754
.replace(oldNode: Syntax(conditionElement), newNode: Syntax(negatedCoditionElement))
715755
]
@@ -741,7 +781,7 @@ public class ParseDiagnosticsGenerator: SyntaxAnyVisitor {
741781
StaticParserError.unexpectedPoundElseSpaceIf,
742782
fixIts: [
743783
FixIt(
744-
message: ReplaceTokensFixIt(replaceTokens: unexpectedTokens, replacement: clause.poundKeyword),
784+
message: ReplaceTokensFixIt(replaceTokens: unexpectedTokens, replacements: [clause.poundKeyword]),
745785
changes: [
746786
.makeMissing(unexpectedBeforePoundKeyword, transferTrivia: false),
747787
.makePresent(clause.poundKeyword, leadingTrivia: unexpectedBeforePoundKeyword.leadingTrivia),
@@ -785,7 +825,7 @@ public class ParseDiagnosticsGenerator: SyntaxAnyVisitor {
785825
.expectedAssignmentInsteadOfComparisonOperator,
786826
fixIts: [
787827
FixIt(
788-
message: ReplaceTokensFixIt(replaceTokens: [.binaryOperator("==")], replacement: node.equal),
828+
message: ReplaceTokensFixIt(replaceTokens: [.binaryOperator("==")], replacements: [node.equal]),
789829
changes: [.makeMissing(unexpected), .makePresent(node.equal, leadingTrivia: [])]
790830
)
791831
],
@@ -799,7 +839,7 @@ public class ParseDiagnosticsGenerator: SyntaxAnyVisitor {
799839
unexpectedTokenCondition: { $0.tokenKind == .colon },
800840
correctTokens: [node.equal],
801841
message: { _ in StaticParserError.initializerInPattern },
802-
moveFixIt: { ReplaceTokensFixIt(replaceTokens: $0, replacement: node.equal) }
842+
moveFixIt: { ReplaceTokensFixIt(replaceTokens: $0, replacements: [node.equal]) }
803843
)
804844
}
805845
return .visitChildren
@@ -1005,7 +1045,7 @@ public class ParseDiagnosticsGenerator: SyntaxAnyVisitor {
10051045
}
10061046
if let singleQuote = node.unexpectedBetweenOpenDelimiterAndOpenQuote?.onlyToken(where: { $0.tokenKind == .singleQuote }) {
10071047
let fixIt = FixIt(
1008-
message: ReplaceTokensFixIt(replaceTokens: [singleQuote], replacement: node.openQuote),
1048+
message: ReplaceTokensFixIt(replaceTokens: [singleQuote], replacements: [node.openQuote]),
10091049
changes: [
10101050
.makeMissing(singleQuote, transferTrivia: false),
10111051
.makePresent(node.openQuote, leadingTrivia: singleQuote.leadingTrivia ?? []),
@@ -1181,7 +1221,7 @@ public class ParseDiagnosticsGenerator: SyntaxAnyVisitor {
11811221
unexpectedTokenCondition: { $0.tokenKind == .colon },
11821222
correctTokens: [node.equal],
11831223
message: { _ in MissingNodesError(missingNodes: [Syntax(node.equal)]) },
1184-
moveFixIt: { ReplaceTokensFixIt(replaceTokens: $0, replacement: node.equal) }
1224+
moveFixIt: { ReplaceTokensFixIt(replaceTokens: $0, replacements: [node.equal]) }
11851225
)
11861226
}
11871227
return .visitChildren

Sources/SwiftParserDiagnostics/ParserDiagnosticMessages.swift

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -237,6 +237,15 @@ public struct AsyncMustPrecedeThrows: ParserError {
237237
}
238238
}
239239

240+
public struct AvailabilityConditionAsExpression: ParserError {
241+
public let availabilityToken: TokenSyntax
242+
public let negatedAvailabilityToken: TokenSyntax
243+
244+
public var message: String {
245+
return "\(availabilityToken) cannot be used as an expression, did you mean to use '\(negatedAvailabilityToken)'?"
246+
}
247+
}
248+
240249
public struct AvailabilityConditionInExpression: ParserError {
241250
public let availabilityCondition: AvailabilityConditionSyntax
242251

@@ -596,9 +605,9 @@ public struct RemoveNodesFixIt: ParserFixIt {
596605
public struct ReplaceTokensFixIt: ParserFixIt {
597606
public let replaceTokens: [TokenSyntax]
598607

599-
public let replacement: TokenSyntax
608+
public let replacements: [TokenSyntax]
600609

601610
public var message: String {
602-
"replace \(nodesDescription(replaceTokens, format: false)) with '\(replacement.text)'"
611+
"replace \(nodesDescription(replaceTokens, format: false)) with \(nodesDescription(replacements, format: false))"
603612
}
604613
}

Sources/SwiftParserDiagnostics/SyntaxExtensions.swift

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,22 @@ extension UnexpectedNodesSyntax {
3939
return nil
4040
}
4141
}
42+
43+
/// If this only contains two tokens, the first satisfying `firstCondition`, and the second satisfying `secondCondition`,
44+
/// return these tokens as a tuple, otherwise return `nil`.
45+
func twoTokens(
46+
firstSatisfying firstCondition: (TokenSyntax) -> Bool,
47+
secondSatisfying secondCondition: (TokenSyntax) -> Bool
48+
) -> (first: TokenSyntax, second: TokenSyntax)? {
49+
let sourceAccurateChildren = self.children(viewMode: .sourceAccurate).compactMap({ $0.as(TokenSyntax.self) })
50+
guard sourceAccurateChildren.count == 2 else {
51+
return nil
52+
}
53+
guard firstCondition(sourceAccurateChildren[0]) && secondCondition(sourceAccurateChildren[1]) else {
54+
return nil
55+
}
56+
return (sourceAccurateChildren[0], sourceAccurateChildren[1])
57+
}
4258
}
4359

4460
extension Syntax {

Tests/SwiftParserTest/translated/AvailabilityQueryUnavailabilityTests.swift

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -441,8 +441,7 @@ final class AvailabilityQueryUnavailabilityTests: XCTestCase {
441441
}
442442
""",
443443
diagnostics: [
444-
// TODO: (good first issue) Old parser expected error on line 2: #available cannot be used as an expression, did you mean to use '#unavailable'?, Fix-It replacements: 4 - 14 = '#unavailable', 18 - 27 = ''
445-
DiagnosticSpec(message: "unexpected code '== false' in 'if' statement")
444+
DiagnosticSpec(message: "#available cannot be used as an expression, did you mean to use '#unavailable'?", fixIts: ["replace '#available(*) == false' with '#unavailable(*)'"])
446445
]
447446
)
448447
}

0 commit comments

Comments
 (0)