Skip to content

Commit b0e01fd

Browse files
committed
Remove CanImportSyntax
Provide api to interpret `ExprSyntax` as `VersionTupleSyntax`
1 parent 43b7a9f commit b0e01fd

28 files changed

+187
-1100
lines changed

CodeGeneration/Sources/SyntaxSupport/AvailabilityNodes.swift

+1
Original file line numberDiff line numberDiff line change
@@ -171,6 +171,7 @@ public let AVAILABILITY_NODES: [Node] = [
171171
base: .syntax,
172172
nameForDiagnostics: "version tuple",
173173
documentation: "A version number like `1.2.0`. Only the first version component is required. There might be an arbitrary number of following components.",
174+
parserFunction: "parseVersionTuple",
174175
children: [
175176
Child(
176177
name: "major",

CodeGeneration/Sources/SyntaxSupport/ExprNodes.swift

-55
Original file line numberDiff line numberDiff line change
@@ -192,61 +192,6 @@ public let EXPR_NODES: [Node] = [
192192
]
193193
),
194194

195-
// the canImport expr in if config expression
196-
Node(
197-
kind: .canImportExpr,
198-
base: .expr,
199-
nameForDiagnostics: "'canImport' expression",
200-
children: [
201-
Child(
202-
name: "canImportKeyword",
203-
kind: .token(choices: [.keyword(.canImport)])
204-
),
205-
Child(
206-
name: "leftParen",
207-
kind: .token(choices: [.token(.leftParen)])
208-
),
209-
Child(
210-
name: "importPath",
211-
kind: .token(choices: [.token(.identifier)])
212-
),
213-
Child(
214-
name: "versionInfo",
215-
kind: .node(kind: .canImportVersionInfo),
216-
isOptional: true
217-
),
218-
Child(
219-
name: "rightParen",
220-
kind: .token(choices: [.token(.rightParen)])
221-
),
222-
]
223-
),
224-
225-
Node(
226-
kind: .canImportVersionInfo,
227-
base: .expr,
228-
nameForDiagnostics: nil,
229-
children: [
230-
Child(
231-
name: "comma",
232-
kind: .token(choices: [.token(.comma)])
233-
),
234-
Child(
235-
name: "label",
236-
kind: .token(choices: [.keyword(._version), .keyword(._underlyingVersion)])
237-
),
238-
Child(
239-
name: "colon",
240-
kind: .token(choices: [.token(.colon)])
241-
),
242-
Child(
243-
name: "version",
244-
deprecatedName: "versionTuple",
245-
kind: .node(kind: .versionTuple)
246-
),
247-
]
248-
),
249-
250195
// case-item -> pattern where-clause? ','?
251196
Node(
252197
kind: .switchCaseItem,

Sources/SwiftParser/Availability.swift

+8-13
Original file line numberDiff line numberDiff line change
@@ -117,7 +117,7 @@ extension Parser {
117117
(.obsoleted, let handle)?:
118118
let argumentLabel = self.eat(handle)
119119
let (unexpectedBeforeColon, colon) = self.expect(.colon)
120-
let version = self.parseVersionTuple(maxComponentCount: 3)
120+
let version = self.parseVersionTuple()
121121
entry = .availabilityLabeledArgument(
122122
RawAvailabilityLabeledArgumentSyntax(
123123
label: argumentLabel,
@@ -130,7 +130,7 @@ extension Parser {
130130
case (.deprecated, let handle)?:
131131
let argumentLabel = self.eat(handle)
132132
if let colon = self.consume(if: .colon) {
133-
let version = self.parseVersionTuple(maxComponentCount: 3)
133+
let version = self.parseVersionTuple()
134134
entry = .availabilityLabeledArgument(
135135
RawAvailabilityLabeledArgumentSyntax(
136136
label: argumentLabel,
@@ -210,7 +210,7 @@ extension Parser {
210210

211211
let version: RawVersionTupleSyntax?
212212
if self.at(.integerLiteral, .floatLiteral) {
213-
version = self.parseVersionTuple(maxComponentCount: 3)
213+
version = self.parseVersionTuple()
214214
} else {
215215
version = nil
216216
}
@@ -254,7 +254,7 @@ extension Parser {
254254
}
255255

256256
/// Parse a dot-separated list of version numbers.
257-
mutating func parseVersionTuple(maxComponentCount: Int) -> RawVersionTupleSyntax {
257+
mutating func parseVersionTuple() -> RawVersionTupleSyntax {
258258
if self.at(.floatLiteral),
259259
let periodIndex = self.currentToken.tokenText.firstIndex(of: UInt8(ascii: ".")),
260260
self.currentToken.tokenText[0..<periodIndex].allSatisfy({ Unicode.Scalar($0).isDigit })
@@ -264,33 +264,28 @@ extension Parser {
264264
let major = self.consumePrefix(SyntaxText(rebasing: self.currentToken.tokenText[0..<periodIndex]), as: .integerLiteral)
265265

266266
var components: [RawVersionComponentSyntax] = []
267-
var trailingComponents: [RawVersionComponentSyntax] = []
268267

269-
for i in 1... {
268+
var loopProgress = LoopProgressCondition()
269+
while self.hasProgressed(&loopProgress) {
270270
guard let period = self.consume(if: .period) else {
271271
break
272272
}
273273
let version = self.expectDecimalIntegerWithoutRecovery()
274274

275275
let versionComponent = RawVersionComponentSyntax(period: period, number: version, arena: self.arena)
276276

277-
if i < maxComponentCount {
278-
components.append(versionComponent)
279-
} else {
280-
trailingComponents.append(versionComponent)
281-
}
277+
components.append(versionComponent)
282278

283279
if version.isMissing {
284280
break
285281
}
286282
}
287283

288-
let unexpectedTrailingComponents = RawUnexpectedNodesSyntax(trailingComponents, arena: self.arena)
289284
let unexpectedAfterComponents = self.parseUnexpectedVersionTokens()
290285
return RawVersionTupleSyntax(
291286
major: major,
292287
components: RawVersionComponentListSyntax(elements: components, arena: self.arena),
293-
RawUnexpectedNodesSyntax(combining: unexpectedTrailingComponents, unexpectedAfterComponents, arena: self.arena),
288+
unexpectedAfterComponents,
294289
arena: self.arena
295290
)
296291
} else {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
//===----------------------------------------------------------------------===//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 2014 - 2023 Apple Inc. and the Swift project authors
6+
// Licensed under Apache License v2.0 with Runtime Library Exception
7+
//
8+
// See https://swift.org/LICENSE.txt for license information
9+
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
10+
//
11+
//===----------------------------------------------------------------------===//
12+
13+
@_spi(RawSyntax) import SwiftSyntax
14+
15+
extension ExprSyntax {
16+
/// Parse the source code of this node as a `VersionTupleSyntax`.
17+
///
18+
/// In some situations, like the `_version` argument of the `canImport` condition in `#if`, the version gets parsed as a normal expression, which results in
19+
/// - an integer literal for single-component versions
20+
/// - a floating point number for two-component versions
21+
/// - a floating point number with a member accesses for versions with more than three components
22+
///
23+
/// This is done so the parser can parse `canImport` like any other function call, reducing the need for special handling.
24+
///
25+
/// This property re-interprets such an expression as a version tuple in cases where the client know that it should semantically represent a version.
26+
public var interpretedAsVersionTuple: VersionTupleSyntax {
27+
var parser = Parser(self.description)
28+
return VersionTupleSyntax.parse(from: &parser)
29+
}
30+
}

Sources/SwiftParser/Expressions.swift

-62
Original file line numberDiff line numberDiff line change
@@ -1077,10 +1077,6 @@ extension Parser {
10771077
pattern: PatternContext,
10781078
flavor: ExprFlavor
10791079
) -> RawExprSyntax {
1080-
if flavor == .poundIfDirective, let directiveExpr = self.parsePrimaryExprForDirective() {
1081-
return RawExprSyntax(directiveExpr)
1082-
}
1083-
10841080
switch self.at(anyIn: PrimaryExpressionStart.self) {
10851081
case (.integerLiteral, let handle)?:
10861082
let literal = self.eat(handle)
@@ -1222,18 +1218,6 @@ extension Parser {
12221218
return RawExprSyntax(RawMissingExprSyntax(arena: self.arena))
12231219
}
12241220
}
1225-
1226-
// try to parse a primary expression for a directive
1227-
mutating func parsePrimaryExprForDirective() -> RawExprSyntax? {
1228-
switch self.at(anyIn: CompilationCondition.self) {
1229-
case (.canImport, let handle)?:
1230-
return RawExprSyntax(self.parseCanImportExpression(handle))
1231-
1232-
// TODO: add case `swift` and `compiler` here
1233-
default:
1234-
return nil
1235-
}
1236-
}
12371221
}
12381222

12391223
extension Parser {
@@ -2346,52 +2330,6 @@ extension Parser {
23462330
}
23472331
}
23482332

2349-
// MARK: Platform Condition
2350-
extension Parser {
2351-
mutating func parseCanImportExpression(_ handle: TokenConsumptionHandle) -> RawExprSyntax {
2352-
let canImportKeyword = self.eat(handle)
2353-
2354-
let (unexpectedBeforeLeftParen, leftParen) = self.expect(.leftParen)
2355-
2356-
let (unexpectedBeforeImportPath, importPath) = self.expect(.identifier)
2357-
2358-
var versionInfo: RawCanImportVersionInfoSyntax?
2359-
2360-
if let comma = self.consume(if: .comma) {
2361-
let (unexpectedBeforeLabel, label) = self.expect(anyIn: CanImportVersionInfoSyntax.LabelOptions.self, default: ._version)
2362-
let (unexpectedBeforeColon, colon) = self.expect(.colon)
2363-
2364-
let version = self.parseVersionTuple(maxComponentCount: 4)
2365-
2366-
versionInfo = RawCanImportVersionInfoSyntax(
2367-
comma: comma,
2368-
unexpectedBeforeLabel,
2369-
label: label,
2370-
unexpectedBeforeColon,
2371-
colon: colon,
2372-
version: version,
2373-
arena: self.arena
2374-
)
2375-
}
2376-
2377-
let (unexpectedBeforeRightParen, rightParen) = self.expect(.rightParen)
2378-
2379-
return RawExprSyntax(
2380-
RawCanImportExprSyntax(
2381-
canImportKeyword: canImportKeyword,
2382-
unexpectedBeforeLeftParen,
2383-
leftParen: leftParen,
2384-
unexpectedBeforeImportPath,
2385-
importPath: importPath,
2386-
versionInfo: versionInfo,
2387-
unexpectedBeforeRightParen,
2388-
rightParen: rightParen,
2389-
arena: self.arena
2390-
)
2391-
)
2392-
}
2393-
}
2394-
23952333
// MARK: Lookahead
23962334

23972335
extension Parser.Lookahead {

Sources/SwiftParser/generated/LayoutNodes+Parsable.swift

+18
Original file line numberDiff line numberDiff line change
@@ -324,6 +324,24 @@ extension TypeSyntax: SyntaxParseable {
324324
}
325325
}
326326

327+
extension VersionTupleSyntax: SyntaxParseable {
328+
public static func parse(from parser: inout Parser) -> Self {
329+
// Keep the parser alive so that the arena in which `raw` is allocated
330+
// doesn’t get deallocated before we have a chance to create a syntax node
331+
// from it. We can’t use `parser.arena` as the parameter to
332+
// `Syntax(raw:arena:)` because the node might have been re-used during an
333+
// incremental parse and would then live in a different arena than
334+
// `parser.arena`.
335+
defer {
336+
withExtendedLifetime(parser) {
337+
}
338+
}
339+
let node = parser.parseVersionTuple()
340+
let raw = RawSyntax(parser.parseRemainder(into: node))
341+
return Syntax(raw: raw, rawNodeArena: raw.arena).cast(Self.self)
342+
}
343+
}
344+
327345
fileprivate extension Parser {
328346
mutating func parseNonOptionalCodeBlockItem() -> RawCodeBlockItemSyntax {
329347
guard let node = self.parseCodeBlockItem(isAtTopLevel: false, allowInitDecl: true) else {

Sources/SwiftParser/generated/Parser+TokenSpecSet.swift

-41
Original file line numberDiff line numberDiff line change
@@ -393,47 +393,6 @@ extension BooleanLiteralExprSyntax {
393393
}
394394
}
395395

396-
extension CanImportVersionInfoSyntax {
397-
@_spi(Diagnostics)
398-
public enum LabelOptions: TokenSpecSet {
399-
case _version
400-
case _underlyingVersion
401-
402-
init?(lexeme: Lexer.Lexeme) {
403-
switch PrepareForKeywordMatch(lexeme) {
404-
case TokenSpec(._version):
405-
self = ._version
406-
case TokenSpec(._underlyingVersion):
407-
self = ._underlyingVersion
408-
default:
409-
return nil
410-
}
411-
}
412-
413-
var spec: TokenSpec {
414-
switch self {
415-
case ._version:
416-
return .keyword(._version)
417-
case ._underlyingVersion:
418-
return .keyword(._underlyingVersion)
419-
}
420-
}
421-
422-
/// Returns a token that satisfies the `TokenSpec` of this case.
423-
///
424-
/// If the token kind of this spec has variable text, e.g. for an identifier, this returns a token with empty text.
425-
@_spi(Diagnostics)
426-
public var tokenSyntax: TokenSyntax {
427-
switch self {
428-
case ._version:
429-
return .keyword(._version)
430-
case ._underlyingVersion:
431-
return .keyword(._underlyingVersion)
432-
}
433-
}
434-
}
435-
}
436-
437396
extension ClosureCaptureSpecifierSyntax {
438397
@_spi(Diagnostics)
439398
public enum SpecifierOptions: TokenSpecSet {

Sources/SwiftParserDiagnostics/ParseDiagnosticsGenerator.swift

-44
Original file line numberDiff line numberDiff line change
@@ -610,50 +610,6 @@ public class ParseDiagnosticsGenerator: SyntaxAnyVisitor {
610610
return .visitChildren
611611
}
612612

613-
public override func visit(_ node: CanImportExprSyntax) -> SyntaxVisitorContinueKind {
614-
if shouldSkip(node) {
615-
return .skipChildren
616-
}
617-
618-
if let versionTuple = node.versionInfo?.version,
619-
let unexpectedVersionTuple = node.unexpectedBetweenVersionInfoAndRightParen
620-
{
621-
if versionTuple.major.isMissing {
622-
addDiagnostic(
623-
versionTuple,
624-
CannotParseVersionTuple(versionTuple: unexpectedVersionTuple),
625-
handledNodes: [versionTuple.id, unexpectedVersionTuple.id]
626-
)
627-
} else {
628-
addDiagnostic(
629-
unexpectedVersionTuple,
630-
.canImportWrongNumberOfParameter,
631-
handledNodes: [unexpectedVersionTuple.id]
632-
)
633-
}
634-
}
635-
636-
return .visitChildren
637-
}
638-
639-
public override func visit(_ node: CanImportVersionInfoSyntax) -> SyntaxVisitorContinueKind {
640-
if shouldSkip(node) {
641-
return .skipChildren
642-
}
643-
644-
if node.label.isMissing {
645-
addDiagnostic(
646-
node.label,
647-
.canImportWrongSecondParameterLabel,
648-
handledNodes: [node.label.id]
649-
)
650-
651-
handledNodes.append(contentsOf: [node.unexpectedBetweenLabelAndColon?.id, node.colon.id, node.version.id].compactMap { $0 })
652-
}
653-
654-
return .visitChildren
655-
}
656-
657613
public override func visit(_ node: ConditionElementSyntax) -> SyntaxVisitorContinueKind {
658614
if shouldSkip(node) {
659615
return .skipChildren

Sources/SwiftParserDiagnostics/ParserDiagnosticMessages.swift

-6
Original file line numberDiff line numberDiff line change
@@ -93,12 +93,6 @@ extension DiagnosticMessage where Self == StaticParserError {
9393
public static var associatedTypeCannotUsePack: Self {
9494
.init("associated types cannot be variadic")
9595
}
96-
public static var canImportWrongSecondParameterLabel: Self {
97-
.init("2nd parameter of canImport should be labeled as _version or _underlyingVersion")
98-
}
99-
public static var canImportWrongNumberOfParameter: Self {
100-
.init("canImport can take only two parameters")
101-
}
10296
public static var caseOutsideOfSwitchOrEnum: Self {
10397
.init("'case' can only appear inside a 'switch' statement or 'enum' declaration")
10498
}

0 commit comments

Comments
 (0)