Skip to content

Commit 10ef365

Browse files
committed
Parse versions as three integer tokens
Previously, internals from the lexer leaked through by representing the major and minor part of the version as a float literal. Also, we accepted non-decimal numbers (eg. hex) for version number, which we don’t want. Fixes #1434 rdar://107152155
1 parent 39b3336 commit 10ef365

File tree

6 files changed

+287
-68
lines changed

6 files changed

+287
-68
lines changed

CodeGeneration/Sources/SyntaxSupport/AvailabilityNodes.swift

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -128,9 +128,21 @@ public let AVAILABILITY_NODES: [Node] = [
128128
kind: "Syntax",
129129
children: [
130130
Child(
131-
name: "MajorMinor",
132-
kind: .token(choices: [.token(tokenKind: "IntegerLiteralToken"), .token(tokenKind: "FloatingLiteralToken")]),
133-
description: "In case the version consists only of the major version, an integer literal that specifies the major version. In case the version consists of major and minor version number, a floating literal in which the decimal part is interpreted as the minor version."
131+
name: "Major",
132+
kind: .token(choices: [.token(tokenKind: "IntegerLiteralToken")]),
133+
description: "The major version."
134+
),
135+
Child(
136+
name: "MinorPeriod",
137+
kind: .token(choices: [.token(tokenKind: "PeriodToken")]),
138+
description: "If the version contains a minor number, the period separating the major from the minor number.",
139+
isOptional: true
140+
),
141+
Child(
142+
name: "Minor",
143+
kind: .token(choices: [.token(tokenKind: "IntegerLiteralToken")]),
144+
description: "The minor version if specified.",
145+
isOptional: true
134146
),
135147
Child(
136148
name: "PatchPeriod",

Sources/SwiftParser/Availability.swift

Lines changed: 45 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -241,6 +241,18 @@ extension Parser {
241241
)
242242
}
243243

244+
/// If the next token is an integer literal only consisting of the digits 0-9
245+
/// consume and return it, otherwise return a missing integer token.
246+
private mutating func expectDecimalIntegerWithoutRecovery() -> RawTokenSyntax {
247+
guard self.at(.integerLiteral) else {
248+
return missingToken(.integerLiteral, text: nil)
249+
}
250+
guard self.currentToken.tokenText.allSatisfy({ Unicode.Scalar($0).isDigit }) else {
251+
return missingToken(.integerLiteral, text: nil)
252+
}
253+
return self.consumeAnyToken()
254+
}
255+
244256
/// Parse a dot-separated list of version numbers.
245257
///
246258
/// Grammar
@@ -250,31 +262,43 @@ extension Parser {
250262
/// platform-version → decimal-digits '.' decimal-digits
251263
/// platform-version → decimal-digits '.' decimal-digits '.' decimal-digits
252264
mutating func parseVersionTuple() -> RawVersionTupleSyntax {
253-
let (unexpectedBeforeMajorMinor, majorMinor) = self.expect(.integerLiteral, .floatingLiteral, default: .integerLiteral)
254-
let patchPeriod: RawTokenSyntax?
255-
let unexpectedBeforePatch: RawUnexpectedNodesSyntax?
256-
let patch: RawTokenSyntax?
257-
if majorMinor.tokenKind == .floatingLiteral {
258-
patchPeriod = self.consume(if: .period)
259-
if patchPeriod != nil {
260-
(unexpectedBeforePatch, patch) = self.expect(.integerLiteral)
265+
if self.at(.floatingLiteral),
266+
let periodIndex = self.currentToken.tokenText.firstIndex(of: UInt8(ascii: ".")),
267+
self.currentToken.tokenText[0..<periodIndex].allSatisfy({ Unicode.Scalar($0).isDigit })
268+
{
269+
// The lexer generates a float literal '1.2' for the major and minor version.
270+
// Split it into two integers if possible
271+
let major = self.consumePrefix(SyntaxText(rebasing: self.currentToken.tokenText[0..<periodIndex]), as: .integerLiteral)
272+
let (unexpectedBeforeMinorPeriod, minorPeriod) = self.expect(.period)
273+
let minor = self.expectDecimalIntegerWithoutRecovery()
274+
let patchPeriod: RawTokenSyntax?
275+
let patch: RawTokenSyntax?
276+
if let period = self.consume(if: .period) {
277+
patchPeriod = period
278+
patch = self.expectDecimalIntegerWithoutRecovery()
261279
} else {
262-
unexpectedBeforePatch = nil
280+
patchPeriod = nil
263281
patch = nil
264282
}
283+
return RawVersionTupleSyntax(
284+
major: major,
285+
unexpectedBeforeMinorPeriod,
286+
minorPeriod: minorPeriod,
287+
minor: minor,
288+
patchPeriod: patchPeriod,
289+
patchVersion: patch,
290+
arena: self.arena
291+
)
265292
} else {
266-
patchPeriod = nil
267-
unexpectedBeforePatch = nil
268-
patch = nil
293+
let major = self.expectDecimalIntegerWithoutRecovery()
294+
return RawVersionTupleSyntax(
295+
major: major,
296+
minorPeriod: nil,
297+
minor: nil,
298+
patchPeriod: nil,
299+
patchVersion: nil,
300+
arena: self.arena
301+
)
269302
}
270-
271-
return RawVersionTupleSyntax(
272-
unexpectedBeforeMajorMinor,
273-
majorMinor: majorMinor,
274-
patchPeriod: patchPeriod,
275-
unexpectedBeforePatch,
276-
patchVersion: patch,
277-
arena: self.arena
278-
)
279303
}
280304
}

Sources/SwiftSyntax/generated/raw/RawSyntaxNodes.swift

Lines changed: 42 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -21031,56 +21031,80 @@ public struct RawVersionTupleSyntax: RawSyntaxNodeProtocol {
2103121031
}
2103221032

2103321033
public init(
21034-
_ unexpectedBeforeMajorMinor: RawUnexpectedNodesSyntax? = nil,
21035-
majorMinor: RawTokenSyntax,
21036-
_ unexpectedBetweenMajorMinorAndPatchPeriod: RawUnexpectedNodesSyntax? = nil,
21034+
_ unexpectedBeforeMajor: RawUnexpectedNodesSyntax? = nil,
21035+
major: RawTokenSyntax,
21036+
_ unexpectedBetweenMajorAndMinorPeriod: RawUnexpectedNodesSyntax? = nil,
21037+
minorPeriod: RawTokenSyntax?,
21038+
_ unexpectedBetweenMinorPeriodAndMinor: RawUnexpectedNodesSyntax? = nil,
21039+
minor: RawTokenSyntax?,
21040+
_ unexpectedBetweenMinorAndPatchPeriod: RawUnexpectedNodesSyntax? = nil,
2103721041
patchPeriod: RawTokenSyntax?,
2103821042
_ unexpectedBetweenPatchPeriodAndPatchVersion: RawUnexpectedNodesSyntax? = nil,
2103921043
patchVersion: RawTokenSyntax?,
2104021044
_ unexpectedAfterPatchVersion: RawUnexpectedNodesSyntax? = nil,
2104121045
arena: __shared SyntaxArena
2104221046
) {
2104321047
let raw = RawSyntax.makeLayout(
21044-
kind: .versionTuple, uninitializedCount: 7, arena: arena) { layout in
21048+
kind: .versionTuple, uninitializedCount: 11, arena: arena) { layout in
2104521049
layout.initialize(repeating: nil)
21046-
layout[0] = unexpectedBeforeMajorMinor?.raw
21047-
layout[1] = majorMinor.raw
21048-
layout[2] = unexpectedBetweenMajorMinorAndPatchPeriod?.raw
21049-
layout[3] = patchPeriod?.raw
21050-
layout[4] = unexpectedBetweenPatchPeriodAndPatchVersion?.raw
21051-
layout[5] = patchVersion?.raw
21052-
layout[6] = unexpectedAfterPatchVersion?.raw
21050+
layout[0] = unexpectedBeforeMajor?.raw
21051+
layout[1] = major.raw
21052+
layout[2] = unexpectedBetweenMajorAndMinorPeriod?.raw
21053+
layout[3] = minorPeriod?.raw
21054+
layout[4] = unexpectedBetweenMinorPeriodAndMinor?.raw
21055+
layout[5] = minor?.raw
21056+
layout[6] = unexpectedBetweenMinorAndPatchPeriod?.raw
21057+
layout[7] = patchPeriod?.raw
21058+
layout[8] = unexpectedBetweenPatchPeriodAndPatchVersion?.raw
21059+
layout[9] = patchVersion?.raw
21060+
layout[10] = unexpectedAfterPatchVersion?.raw
2105321061
}
2105421062
self.init(unchecked: raw)
2105521063
}
2105621064

21057-
public var unexpectedBeforeMajorMinor: RawUnexpectedNodesSyntax? {
21065+
public var unexpectedBeforeMajor: RawUnexpectedNodesSyntax? {
2105821066
layoutView.children[0].map(RawUnexpectedNodesSyntax.init(raw:))
2105921067
}
2106021068

21061-
public var majorMinor: RawTokenSyntax {
21069+
public var major: RawTokenSyntax {
2106221070
layoutView.children[1].map(RawTokenSyntax.init(raw:))!
2106321071
}
2106421072

21065-
public var unexpectedBetweenMajorMinorAndPatchPeriod: RawUnexpectedNodesSyntax? {
21073+
public var unexpectedBetweenMajorAndMinorPeriod: RawUnexpectedNodesSyntax? {
2106621074
layoutView.children[2].map(RawUnexpectedNodesSyntax.init(raw:))
2106721075
}
2106821076

21069-
public var patchPeriod: RawTokenSyntax? {
21077+
public var minorPeriod: RawTokenSyntax? {
2107021078
layoutView.children[3].map(RawTokenSyntax.init(raw:))
2107121079
}
2107221080

21073-
public var unexpectedBetweenPatchPeriodAndPatchVersion: RawUnexpectedNodesSyntax? {
21081+
public var unexpectedBetweenMinorPeriodAndMinor: RawUnexpectedNodesSyntax? {
2107421082
layoutView.children[4].map(RawUnexpectedNodesSyntax.init(raw:))
2107521083
}
2107621084

21077-
public var patchVersion: RawTokenSyntax? {
21085+
public var minor: RawTokenSyntax? {
2107821086
layoutView.children[5].map(RawTokenSyntax.init(raw:))
2107921087
}
2108021088

21081-
public var unexpectedAfterPatchVersion: RawUnexpectedNodesSyntax? {
21089+
public var unexpectedBetweenMinorAndPatchPeriod: RawUnexpectedNodesSyntax? {
2108221090
layoutView.children[6].map(RawUnexpectedNodesSyntax.init(raw:))
2108321091
}
21092+
21093+
public var patchPeriod: RawTokenSyntax? {
21094+
layoutView.children[7].map(RawTokenSyntax.init(raw:))
21095+
}
21096+
21097+
public var unexpectedBetweenPatchPeriodAndPatchVersion: RawUnexpectedNodesSyntax? {
21098+
layoutView.children[8].map(RawUnexpectedNodesSyntax.init(raw:))
21099+
}
21100+
21101+
public var patchVersion: RawTokenSyntax? {
21102+
layoutView.children[9].map(RawTokenSyntax.init(raw:))
21103+
}
21104+
21105+
public var unexpectedAfterPatchVersion: RawUnexpectedNodesSyntax? {
21106+
layoutView.children[10].map(RawUnexpectedNodesSyntax.init(raw:))
21107+
}
2108421108
}
2108521109

2108621110
@_spi(RawSyntax)

Sources/SwiftSyntax/generated/raw/RawSyntaxValidation.swift

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2524,14 +2524,18 @@ func validateLayout(layout: RawSyntaxBuffer, as kind: SyntaxKind) {
25242524
assertNoError(kind, 7, verify(layout[7], as: RawPatternBindingListSyntax.self))
25252525
assertNoError(kind, 8, verify(layout[8], as: RawUnexpectedNodesSyntax?.self))
25262526
case .versionTuple:
2527-
assert(layout.count == 7)
2527+
assert(layout.count == 11)
25282528
assertNoError(kind, 0, verify(layout[0], as: RawUnexpectedNodesSyntax?.self))
25292529
assertNoError(kind, 1, verify(layout[1], as: RawTokenSyntax.self, tokenChoices: [.tokenKind(.integerLiteral), .tokenKind(.floatingLiteral)]))
25302530
assertNoError(kind, 2, verify(layout[2], as: RawUnexpectedNodesSyntax?.self))
25312531
assertNoError(kind, 3, verify(layout[3], as: RawTokenSyntax?.self, tokenChoices: [.tokenKind(.period)]))
25322532
assertNoError(kind, 4, verify(layout[4], as: RawUnexpectedNodesSyntax?.self))
25332533
assertNoError(kind, 5, verify(layout[5], as: RawTokenSyntax?.self, tokenChoices: [.tokenKind(.integerLiteral)]))
25342534
assertNoError(kind, 6, verify(layout[6], as: RawUnexpectedNodesSyntax?.self))
2535+
assertNoError(kind, 7, verify(layout[7], as: RawTokenSyntax?.self))
2536+
assertNoError(kind, 8, verify(layout[8], as: RawUnexpectedNodesSyntax?.self))
2537+
assertNoError(kind, 9, verify(layout[9], as: RawTokenSyntax?.self))
2538+
assertNoError(kind, 10, verify(layout[10], as: RawUnexpectedNodesSyntax?.self))
25352539
case .whereClause:
25362540
assert(layout.count == 5)
25372541
assertNoError(kind, 0, verify(layout[0], as: RawUnexpectedNodesSyntax?.self))

0 commit comments

Comments
 (0)