Skip to content

Remove TokenList as option for AvailabilityArgument #1226

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
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
Original file line number Diff line number Diff line change
Expand Up @@ -36,9 +36,7 @@ public let AVAILABILITY_NODES: [Node] = [
Child(name: "AvailabilityVersionRestriction",
kind: "AvailabilityVersionRestriction"),
Child(name: "AvailabilityLabeledArgument",
kind: "AvailabilityLabeledArgument"),
Child(name: "TokenList",
kind: "TokenList")
kind: "AvailabilityLabeledArgument")
]),
Child(name: "TrailingComma",
kind: "CommaToken",
Expand Down Expand Up @@ -81,7 +79,7 @@ public let AVAILABILITY_NODES: [Node] = [
]),

Node(name: "AvailabilityVersionRestriction",
nameForDiagnostics: "availability argument",
nameForDiagnostics: "version restriction",
description: "An argument to `@available` that restricts the availability on acertain platform to a version, e.g. `iOS 10` or `swift 3.4`.",
kind: "Syntax",
children: [
Expand Down
8 changes: 2 additions & 6 deletions Sources/SwiftParser/Attributes.swift
Original file line number Diff line number Diff line change
Expand Up @@ -220,11 +220,7 @@ extension Parser {
switch DeclarationAttributeWithSpecialSyntax(lexeme: self.peek()) {
case .available, ._spi_available:
return parseAttribute(argumentMode: .required) { parser in
if parser.peek().rawTokenKind == .integerLiteral || parser.peek().rawTokenKind == .floatingLiteral {
return .availability(parser.parseAvailabilitySpecList())
} else {
return .availability(parser.parseExtendedAvailabilitySpecList())
}
return .availability(parser.parseAvailabilityArgumentSpecList())
}
case .differentiable:
return parseAttribute(argumentMode: .required) { parser in
Expand Down Expand Up @@ -997,7 +993,7 @@ extension Parser {
var platforms: [RawAvailabilityVersionRestrictionListEntrySyntax] = []
var keepGoing: RawTokenSyntax?
repeat {
let restriction = self.parseAvailabilityMacro()
let restriction = self.parseAvailabilityMacro(allowStarAsVersionNumber: true)
keepGoing = self.consume(if: .comma)
platforms.append(
RawAvailabilityVersionRestrictionListEntrySyntax(
Expand Down
232 changes: 87 additions & 145 deletions Sources/SwiftParser/Availability.swift
Original file line number Diff line number Diff line change
Expand Up @@ -48,28 +48,6 @@ extension Parser {
arena: self.arena
)
)

// Before continuing to parse the next specification, we check that it's
// also in the shorthand syntax and recover from it.
if keepGoing != nil,
let (_, handle) = self.at(anyIn: AvailabilityArgumentKind.self)
{
var tokens = [RawTokenSyntax]()
tokens.append(self.eat(handle))
var recoveryProgress = LoopProgressCondition()
while !self.at(any: [.eof, .comma, .rightParen]) && recoveryProgress.evaluate(currentToken) {
tokens.append(self.consumeAnyToken())
}
let syntax = RawTokenListSyntax(elements: tokens, arena: self.arena)
keepGoing = self.consume(if: .comma)
elements.append(
RawAvailabilityArgumentSyntax(
entry: .tokenList(syntax),
trailingComma: keepGoing,
arena: self.arena
)
)
}
} while keepGoing != nil && availablityArgumentProgress.evaluate(currentToken)
}

Expand All @@ -84,6 +62,8 @@ extension Parser {
case obsoleted
case unavailable
case noasync
case star
case identifier

init?(lexeme: Lexer.Lexeme) {
switch lexeme {
Expand All @@ -94,6 +74,8 @@ extension Parser {
case RawTokenKindMatch(.obsoleted): self = .obsoleted
case RawTokenKindMatch(.unavailable): self = .unavailable
case RawTokenKindMatch(.noasync): self = .noasync
case RawTokenKindMatch(.binaryOperator) where lexeme.tokenText == "*": self = .star
case RawTokenKindMatch(.identifier): self = .identifier
default: return nil
}
}
Expand All @@ -107,100 +89,94 @@ extension Parser {
case .obsoleted: return .keyword(.obsoleted)
case .unavailable: return .keyword(.unavailable)
case .noasync: return .keyword(.noasync)
case .star: return .binaryOperator
case .identifier: return .identifier
}
}
}

mutating func parseExtendedAvailabilitySpecList() -> RawAvailabilitySpecListSyntax {
mutating func parseAvailabilityArgumentSpecList() -> RawAvailabilitySpecListSyntax {
var elements = [RawAvailabilityArgumentSyntax]()
var keepGoing: RawTokenSyntax? = nil

// Parse the platform from the first element.
let platform = self.consumeAnyToken()
var keepGoing: RawTokenSyntax? = self.consume(if: .comma)
elements.append(
RawAvailabilityArgumentSyntax(
entry: .token(platform),
trailingComma: keepGoing,
arena: self.arena
)
)
var loopProgressCondition = LoopProgressCondition()
LOOP: repeat {
let entry: RawAvailabilityArgumentSyntax.Entry
switch self.at(anyIn: AvailabilityArgumentKind.self) {
case (.message, let handle)?,
(.renamed, let handle)?:
let argumentLabel = self.eat(handle)
let (unexpectedBeforeColon, colon) = self.expect(.colon)
// FIXME: Make sure this is a string literal with no interpolation.
let stringValue = self.consumeAnyToken()

do {
var loopProgressCondition = LoopProgressCondition()
while keepGoing != nil && loopProgressCondition.evaluate(currentToken) {
let entry: RawAvailabilityArgumentSyntax.Entry
switch self.at(anyIn: AvailabilityArgumentKind.self) {
case (.message, let handle)?,
(.renamed, let handle)?:
let argumentLabel = self.eat(handle)
let (unexpectedBeforeColon, colon) = self.expect(.colon)
// FIXME: Make sure this is a string literal with no interpolation.
let stringValue = self.consumeAnyToken()

entry = .availabilityLabeledArgument(
RawAvailabilityLabeledArgumentSyntax(
label: argumentLabel,
unexpectedBeforeColon,
colon: colon,
value: .string(stringValue),
arena: self.arena
)
entry = .availabilityLabeledArgument(
RawAvailabilityLabeledArgumentSyntax(
label: argumentLabel,
unexpectedBeforeColon,
colon: colon,
value: .string(stringValue),
arena: self.arena
)
case (.introduced, let handle)?,
(.obsoleted, let handle)?:
let argumentLabel = self.eat(handle)
let (unexpectedBeforeColon, colon) = self.expect(.colon)
)
case (.introduced, let handle)?,
(.obsoleted, let handle)?:
let argumentLabel = self.eat(handle)
let (unexpectedBeforeColon, colon) = self.expect(.colon)
let version = self.parseVersionTuple()
entry = .availabilityLabeledArgument(
RawAvailabilityLabeledArgumentSyntax(
label: argumentLabel,
unexpectedBeforeColon,
colon: colon,
value: .version(version),
arena: self.arena
)
)
case (.deprecated, let handle)?:
let argumentLabel = self.eat(handle)
if let colon = self.consume(if: .colon) {
let version = self.parseVersionTuple()
entry = .availabilityLabeledArgument(
RawAvailabilityLabeledArgumentSyntax(
label: argumentLabel,
unexpectedBeforeColon,
colon: colon,
value: .version(version),
arena: self.arena
)
)
case (.deprecated, let handle)?:
let argumentLabel = self.eat(handle)
if let colon = self.consume(if: .colon) {
let version = self.parseVersionTuple()
entry = .availabilityLabeledArgument(
RawAvailabilityLabeledArgumentSyntax(
label: argumentLabel,
colon: colon,
value: .version(version),
arena: self.arena
)
)
} else {
entry = .token(argumentLabel)
}
case (.unavailable, let handle)?,
(.noasync, let handle)?:
let argument = self.eat(handle)
// FIXME: Can we model this in SwiftSyntax by making the
// 'labeled' argument part optional?
entry = .token(argument)
case nil:
// Not sure what this label is but, let's just eat it and
// keep going.
var tokens = [RawTokenSyntax]()
while !self.at(any: [.eof, .comma, .rightParen]) {
tokens.append(self.consumeAnyToken())
}
entry = .tokenList(RawTokenListSyntax(elements: tokens, arena: self.arena))
} else {
entry = .token(argumentLabel)
}
case (.unavailable, let handle)?,
(.noasync, let handle)?:
let argument = self.eat(handle)
// FIXME: Can we model this in SwiftSyntax by making the
// 'labeled' argument part optional?
entry = .token(argument)
case (.star, _)?:
entry = self.parseAvailabilitySpec()
case (.identifier, let handle)?:
if self.peek().rawTokenKind == .comma {
// An argument like `_iOS13Aligned` that isn't followed by a version.
let version = self.eat(handle)
entry = .token(version)
} else {
entry = self.parseAvailabilitySpec()
}
case nil:
break LOOP
}

keepGoing = self.consume(if: .comma)
elements.append(
RawAvailabilityArgumentSyntax(
entry: entry,
trailingComma: keepGoing,
arena: self.arena
)
keepGoing = self.consume(if: .comma)
elements.append(
RawAvailabilityArgumentSyntax(
entry: entry,
trailingComma: keepGoing,
arena: self.arena
)
}
}
)
} while keepGoing != nil && loopProgressCondition.evaluate(currentToken)
return RawAvailabilitySpecListSyntax(elements: elements, arena: self.arena)
}

Expand All @@ -218,50 +194,7 @@ extension Parser {
return .token(star)
}

if self.at(any: [.identifier, .wildcard]) {
if self.at(.keyword(.swift)) || self.at(.keyword(._PackageDescription)) {
return .availabilityVersionRestriction(self.parsePlatformAgnosticVersionConstraintSpec())
}
}

return .availabilityVersionRestriction(self.parsePlatformVersionConstraintSpec())
}

mutating func parsePlatformAgnosticVersionConstraintSpec() -> RawAvailabilityVersionRestrictionSyntax {
let (unexpectedBeforePlatform, platform) = self.expectAny([.identifier, .wildcard], default: .identifier)
let version = self.parseVersionTuple()
return RawAvailabilityVersionRestrictionSyntax(
unexpectedBeforePlatform,
platform: platform,
version: version,
arena: self.arena
)
}

/// Parse a platform-specific version constraint.
///
/// The grammar calls out Apple-specific names, even though the Swift compiler
/// will accept any identifier here. The compiler will diagnose usages of platforms it
/// doesn't know about later.
///
/// Grammar
/// =======
///
/// platform-name → iOS | iOSApplicationExtension
/// platform-name → macOS | macOSApplicationExtension
/// platform-name → macCatalyst | macCatalystApplicationExtension
/// platform-name → watchOS
/// platform-name → tvOS
mutating func parsePlatformVersionConstraintSpec() -> RawAvailabilityVersionRestrictionSyntax {
// Register the platform name as a keyword token.
let (unexpectedBeforePlatform, plaform) = self.expect(.identifier)
let version = self.parseVersionTuple()
return RawAvailabilityVersionRestrictionSyntax(
unexpectedBeforePlatform,
platform: plaform,
version: version,
arena: self.arena
)
return .availabilityVersionRestriction(self.parseAvailabilityMacro())
}

/// Parse an availability macro.
Expand All @@ -272,19 +205,28 @@ extension Parser {
/// =======
///
/// availability-argument → macro-name platform-version
mutating func parseAvailabilityMacro() -> RawAvailabilityVersionRestrictionSyntax {
let platform = self.consumeAnyToken()
///
/// If `allowStarAsVersionNumber` is `true`, versions like `* 13.0` are accepted.
/// This is to match the behavior of `@_originallyDefinedIn` in the old parser that accepted such versions
mutating func parseAvailabilityMacro(allowStarAsVersionNumber: Bool = false) -> RawAvailabilityVersionRestrictionSyntax {
let unexpectedBeforePlatform: RawUnexpectedNodesSyntax?
let platform: RawTokenSyntax
if allowStarAsVersionNumber, self.atContextualPunctuator("*") {
unexpectedBeforePlatform = nil
platform = self.consumeAnyToken(remapping: .identifier)
} else {
(unexpectedBeforePlatform, platform) = self.expect(.identifier)
}

let version: RawVersionTupleSyntax?
if self.at(.integerLiteral) {
version = self.parseVersionTuple()
} else if self.at(.floatingLiteral) {
if self.at(any: [.integerLiteral, .floatingLiteral]) {
version = self.parseVersionTuple()
} else {
version = nil
}

return RawAvailabilityVersionRestrictionSyntax(
unexpectedBeforePlatform,
platform: platform,
version: version,
arena: self.arena
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18086,18 +18086,16 @@ public struct RawAvailabilityArgumentSyntax: RawSyntaxNodeProtocol {
case `token`(RawTokenSyntax)
case `availabilityVersionRestriction`(RawAvailabilityVersionRestrictionSyntax)
case `availabilityLabeledArgument`(RawAvailabilityLabeledArgumentSyntax)
case `tokenList`(RawTokenListSyntax)

public static func isKindOf(_ raw: RawSyntax) -> Bool {
return RawTokenSyntax.isKindOf(raw) || RawAvailabilityVersionRestrictionSyntax.isKindOf(raw) || RawAvailabilityLabeledArgumentSyntax.isKindOf(raw) || RawTokenListSyntax.isKindOf(raw)
return RawTokenSyntax.isKindOf(raw) || RawAvailabilityVersionRestrictionSyntax.isKindOf(raw) || RawAvailabilityLabeledArgumentSyntax.isKindOf(raw)
}

public var raw: RawSyntax {
switch self {
case .token(let node): return node.raw
case .availabilityVersionRestriction(let node): return node.raw
case .availabilityLabeledArgument(let node): return node.raw
case .tokenList(let node): return node.raw
}
}

Expand All @@ -18114,10 +18112,6 @@ public struct RawAvailabilityArgumentSyntax: RawSyntaxNodeProtocol {
self = .availabilityLabeledArgument(node)
return
}
if let node = RawTokenListSyntax(other) {
self = .tokenList(node)
return
}
return nil
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2637,7 +2637,6 @@ func validateLayout(layout: RawSyntaxBuffer, as kind: SyntaxKind) {
verify(layout[1], as: RawSyntax.self),
verify(layout[1], as: RawSyntax.self),
verify(layout[1], as: RawSyntax.self),
verify(layout[1], as: RawSyntax.self),
])
assertNoError(kind, 2, verify(layout[2], as: RawUnexpectedNodesSyntax?.self))
assertNoError(kind, 3, verify(layout[3], as: RawTokenSyntax?.self))
Expand Down
2 changes: 1 addition & 1 deletion Sources/SwiftSyntax/generated/Misc.swift
Original file line number Diff line number Diff line change
Expand Up @@ -865,7 +865,7 @@ extension SyntaxKind {
case .availabilityVersionRestrictionList:
return "version list"
case .availabilityVersionRestriction:
return "availability argument"
return "version restriction"
case .awaitExpr:
return "'await' expression"
case .backDeployAttributeSpecList:
Expand Down
Loading