Skip to content

Commit facfbf4

Browse files
committed
Improve parsing of platforms and versions in availability conditions
1 parent 3f9f008 commit facfbf4

File tree

6 files changed

+58
-96
lines changed

6 files changed

+58
-96
lines changed

CodeGeneration/Sources/SyntaxSupport/gyb_generated/AvailabilityNodes.swift

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ public let AVAILABILITY_NODES: [Node] = [
1919
element: "AvailabilityArgument"),
2020

2121
Node(name: "AvailabilityArgument",
22-
nameForDiagnostics: "'@available' argument",
22+
nameForDiagnostics: "availability argument",
2323
description: "A single argument to an `@available` argument like `*`, `iOS 10.1`,or `message: \"This has been deprecated\"`.",
2424
kind: "Syntax",
2525
children: [
@@ -50,7 +50,7 @@ public let AVAILABILITY_NODES: [Node] = [
5050
]),
5151

5252
Node(name: "AvailabilityLabeledArgument",
53-
nameForDiagnostics: "'@available' argument",
53+
nameForDiagnostics: "availability argument",
5454
description: "A argument to an `@available` attribute that consists of a label anda value, e.g. `message: \"This has been deprecated\"`.",
5555
kind: "Syntax",
5656
children: [
@@ -81,7 +81,7 @@ public let AVAILABILITY_NODES: [Node] = [
8181
]),
8282

8383
Node(name: "AvailabilityVersionRestriction",
84-
nameForDiagnostics: "'@available' argument",
84+
nameForDiagnostics: "availability argument",
8585
description: "An argument to `@available` that restricts the availability on acertain platform to a version, e.g. `iOS 10` or `swift 3.4`.",
8686
kind: "Syntax",
8787
children: [

Sources/SwiftParser/Availability.swift

Lines changed: 18 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -246,9 +246,10 @@ extension Parser {
246246
/// platform-name → tvOS
247247
mutating func parsePlatformVersionConstraintSpec() -> RawAvailabilityVersionRestrictionSyntax {
248248
// Register the platform name as a keyword token.
249-
let plaform = self.consumeAnyToken()
249+
let (unexpectedBeforePlatform, plaform) = self.expect(.identifier)
250250
let version = self.parseVersionTuple()
251251
return RawAvailabilityVersionRestrictionSyntax(
252+
unexpectedBeforePlatform,
252253
platform: plaform,
253254
version: version,
254255
arena: self.arena
@@ -291,28 +292,29 @@ extension Parser {
291292
/// platform-version → decimal-digits '.' decimal-digits
292293
/// platform-version → decimal-digits '.' decimal-digits '.' decimal-digits
293294
mutating func parseVersionTuple() -> RawVersionTupleSyntax {
294-
if let major = self.consume(if: .integerLiteral) {
295-
return RawVersionTupleSyntax(
296-
majorMinor: major,
297-
patchPeriod: nil,
298-
patchVersion: nil,
299-
arena: self.arena
300-
)
301-
}
302-
303-
let majorMinor = self.consumeAnyToken()
304-
let period = self.consume(if: .period)
305-
295+
let (unexpectedBeforeMajorMinor, majorMinor) = self.expectAny([.integerLiteral, .floatingLiteral], default: .integerLiteral)
296+
let patchPeriod: RawTokenSyntax?
297+
let unexpectedBeforePatch: RawUnexpectedNodesSyntax?
306298
let patch: RawTokenSyntax?
307-
if period != nil {
308-
patch = self.consumeAnyToken()
299+
if majorMinor.tokenKind == .floatingLiteral {
300+
patchPeriod = self.consume(if: .period)
301+
if patchPeriod != nil {
302+
(unexpectedBeforePatch, patch) = self.expect(.integerLiteral)
303+
} else {
304+
unexpectedBeforePatch = nil
305+
patch = nil
306+
}
309307
} else {
308+
patchPeriod = nil
309+
unexpectedBeforePatch = nil
310310
patch = nil
311311
}
312312

313313
return RawVersionTupleSyntax(
314+
unexpectedBeforeMajorMinor,
314315
majorMinor: majorMinor,
315-
patchPeriod: period,
316+
patchPeriod: patchPeriod,
317+
unexpectedBeforePatch,
316318
patchVersion: patch,
317319
arena: self.arena
318320
)

Sources/SwiftSyntax/generated/Misc.swift

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -833,17 +833,17 @@ extension SyntaxKind {
833833
case .attributedType:
834834
return "type"
835835
case .availabilityArgument:
836-
return "'@available' argument"
836+
return "availability argument"
837837
case .availabilityCondition:
838838
return "availability condition"
839839
case .availabilityEntry:
840840
return "availability entry"
841841
case .availabilityLabeledArgument:
842-
return "'@available' argument"
842+
return "availability argument"
843843
case .availabilitySpecList:
844844
return "'@availability' arguments"
845845
case .availabilityVersionRestriction:
846-
return "'@available' argument"
846+
return "availability argument"
847847
case .awaitExpr:
848848
return "'await' expression"
849849
case .backDeployAttributeSpecList:

Tests/SwiftParserTest/translated/AvailabilityQueryTests.swift

Lines changed: 14 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -106,46 +106,35 @@ final class AvailabilityQueryTests: XCTestCase {
106106
AssertParse(
107107
"""
108108
if #available 1️⃣{
109-
}2️⃣
109+
}
110110
""",
111111
diagnostics: [
112-
// TODO: Old parser expected error on line 1: expected availability condition
113-
DiagnosticSpec(locationMarker: "1️⃣", message: "expected '(' in availability condition"),
114-
DiagnosticSpec(locationMarker: "2️⃣", message: "expected ')' to end availability condition"),
115-
DiagnosticSpec(locationMarker: "2️⃣", message: "expected code block in 'if' statement"),
112+
DiagnosticSpec(locationMarker: "1️⃣", message: "expected '(', '@availability' arguments, and ')' in availability condition")
116113
]
117114
)
118115
}
119116

120117
func testAvailabilityQuery8() {
121118
AssertParse(
122119
"""
123-
if #availableℹ️( {
124-
}1️⃣
120+
if #available( 1️⃣{
121+
}
125122
""",
126123
diagnostics: [
127-
// TODO: Old parser expected error on line 1: expected platform name
128-
DiagnosticSpec(
129-
message: "expected ')' to end availability condition",
130-
notes: [
131-
NoteSpec(message: "to match this opening '('")
132-
]
133-
),
134-
DiagnosticSpec(message: "expected code block in 'if' statement"),
124+
DiagnosticSpec(message: "expected platform and version in availability argument"),
125+
DiagnosticSpec(message: "expected ')' to end availability condition"),
135126
]
136127
)
137128
}
138129

139130
func testAvailabilityQuery9() {
140131
AssertParse(
141132
"""
142-
if #available() { 1️⃣
133+
if #available(1️⃣) {
143134
}
144135
""",
145136
diagnostics: [
146-
// TODO: Old parser expected error on line 1: expected platform name
147-
DiagnosticSpec(message: "expected ')' to end availability condition"),
148-
DiagnosticSpec(message: "expected '{' in 'if' statement"),
137+
DiagnosticSpec(message: "expected platform and version in availability argument")
149138
]
150139
)
151140
}
@@ -335,32 +324,24 @@ final class AvailabilityQueryTests: XCTestCase {
335324
func testAvailabilityQuery27() {
336325
AssertParse(
337326
"""
338-
if #availableℹ️(OSX 10.51, {
339-
}1️⃣
327+
if #available(OSX 10.51, 1️⃣{
328+
}
340329
""",
341330
diagnostics: [
342-
// TODO: Old parser expected error on line 1: expected platform name
343-
DiagnosticSpec(
344-
message: "expected ')' to end availability condition",
345-
notes: [
346-
NoteSpec(message: "to match this opening '('")
347-
]
348-
),
349-
DiagnosticSpec(message: "expected code block in 'if' statement"),
331+
DiagnosticSpec(message: "expected platform and version in availability argument"),
332+
DiagnosticSpec(message: "expected ')' to end availability condition"),
350333
]
351334
)
352335
}
353336

354337
func testAvailabilityQuery28() {
355338
AssertParse(
356339
"""
357-
if #available(OSX 10.51,) { 1️⃣
340+
if #available(OSX 10.51,1️⃣) {
358341
}
359342
""",
360343
diagnostics: [
361-
// TODO: Old parser expected error on line 1: expected platform name
362-
DiagnosticSpec(message: "expected ')' to end availability condition"),
363-
DiagnosticSpec(message: "expected '{' in 'if' statement"),
344+
DiagnosticSpec(message: "expected platform and version in availability argument")
364345
]
365346
)
366347
}

Tests/SwiftParserTest/translated/AvailabilityQueryUnavailabilityTests.swift

Lines changed: 17 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -86,47 +86,36 @@ final class AvailabilityQueryUnavailabilityTests: XCTestCase {
8686
func testAvailabilityQueryUnavailability4() {
8787
AssertParse(
8888
"""
89-
if #unavailable 1️⃣{
90-
}2️⃣
89+
if #unavailable 1️⃣{
90+
}
9191
""",
9292
diagnostics: [
93-
// TODO: Old parser expected error on line 1: expected availability condition
94-
DiagnosticSpec(locationMarker: "1️⃣", message: "expected '(' in availability condition"),
95-
DiagnosticSpec(locationMarker: "2️⃣", message: "expected ')' to end availability condition"),
96-
DiagnosticSpec(locationMarker: "2️⃣", message: "expected code block in 'if' statement"),
93+
DiagnosticSpec(message: "expected '(', '@availability' arguments, and ')' in availability condition")
9794
]
9895
)
9996
}
10097

10198
func testAvailabilityQueryUnavailability5() {
10299
AssertParse(
103100
"""
104-
if #unavailableℹ️( {
105-
}1️⃣
101+
if #unavailable( 1️⃣{
102+
}
106103
""",
107104
diagnostics: [
108-
// TODO: Old parser expected error on line 1: expected platform name
109-
DiagnosticSpec(
110-
message: "expected ')' to end availability condition",
111-
notes: [
112-
NoteSpec(message: "to match this opening '('")
113-
]
114-
),
115-
DiagnosticSpec(message: "expected code block in 'if' statement"),
105+
DiagnosticSpec(message: "expected platform and version in availability argument"),
106+
DiagnosticSpec(message: "expected ')' to end availability condition"),
116107
]
117108
)
118109
}
119110

120111
func testAvailabilityQueryUnavailability6() {
121112
AssertParse(
122113
"""
123-
if #unavailable() { 1️⃣
114+
if #unavailable(1️⃣) {
124115
}
125116
""",
126117
diagnostics: [
127-
// TODO: Old parser expected error on line 1: expected platform name
128-
DiagnosticSpec(message: "expected ')' to end availability condition"),
129-
DiagnosticSpec(message: "expected '{' in 'if' statement"),
118+
DiagnosticSpec(message: "expected platform and version in availability argument")
130119
]
131120
)
132121
}
@@ -264,13 +253,11 @@ final class AvailabilityQueryUnavailabilityTests: XCTestCase {
264253
AssertParse(
265254
"""
266255
// Should this be a valid spelling since `#unvailable(*)` cannot be written?
267-
if #unavailable() { 1️⃣
256+
if #unavailable(1️⃣) {
268257
}
269258
""",
270259
diagnostics: [
271-
// TODO: Old parser expected error on line 2: expected platform name
272-
DiagnosticSpec(message: "expected ')' to end availability condition"),
273-
DiagnosticSpec(message: "expected '{' in 'if' statement"),
260+
DiagnosticSpec(message: "expected platform and version in availability argument")
274261
]
275262
)
276263
}
@@ -305,32 +292,24 @@ final class AvailabilityQueryUnavailabilityTests: XCTestCase {
305292
func testAvailabilityQueryUnavailability22() {
306293
AssertParse(
307294
"""
308-
if #unavailableℹ️(OSX 10.51, {
309-
}1️⃣
295+
if #unavailable(OSX 10.51, 1️⃣{
296+
}
310297
""",
311298
diagnostics: [
312-
// TODO: Old parser expected error on line 1: expected platform name
313-
DiagnosticSpec(
314-
message: "expected ')' to end availability condition",
315-
notes: [
316-
NoteSpec(message: "to match this opening '('")
317-
]
318-
),
319-
DiagnosticSpec(message: "expected code block in 'if' statement"),
299+
DiagnosticSpec(message: "expected platform and version in availability argument"),
300+
DiagnosticSpec(message: "expected ')' to end availability condition"),
320301
]
321302
)
322303
}
323304

324305
func testAvailabilityQueryUnavailability23() {
325306
AssertParse(
326307
"""
327-
if #unavailable(OSX 10.51,) { 1️⃣
308+
if #unavailable(OSX 10.51,1️⃣) {
328309
}
329310
""",
330311
diagnostics: [
331-
// TODO: Old parser expected error on line 1: expected platform name
332-
DiagnosticSpec(message: "expected ')' to end availability condition"),
333-
DiagnosticSpec(message: "expected '{' in 'if' statement"),
312+
DiagnosticSpec(message: "expected platform and version in availability argument")
334313
]
335314
)
336315
}

gyb_syntax_support/AvailabilityNodes.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
# | identifier ','?
1212
# | availability-version-restriction ','?
1313
# | availability-versioned-argument ','?
14-
Node('AvailabilityArgument', name_for_diagnostics="'@available' argument",
14+
Node('AvailabilityArgument', name_for_diagnostics="availability argument",
1515
kind='Syntax',
1616
description='''
1717
A single argument to an `@available` argument like `*`, `iOS 10.1`,
@@ -38,7 +38,7 @@
3838

3939
# Representation of 'deprecated: 2.3', 'message: "Hello world"' etc.
4040
# availability-versioned-argument -> identifier ':' version-tuple
41-
Node('AvailabilityLabeledArgument', name_for_diagnostics="'@available' argument",
41+
Node('AvailabilityLabeledArgument', name_for_diagnostics="availability argument",
4242
kind='Syntax',
4343
description='''
4444
A argument to an `@available` attribute that consists of a label and
@@ -58,7 +58,7 @@
5858

5959
# Representation for 'iOS 10', 'swift 3.4' etc.
6060
# availability-version-restriction -> identifier version-tuple
61-
Node('AvailabilityVersionRestriction', name_for_diagnostics="'@available' argument",
61+
Node('AvailabilityVersionRestriction', name_for_diagnostics="availability argument",
6262
kind='Syntax',
6363
description='''
6464
An argument to `@available` that restricts the availability on a

0 commit comments

Comments
 (0)