Skip to content

Commit a0b4326

Browse files
committed
Add a CanImportExprSyntax node
This change makes it easy to diagnose the the arguments in `canImport` expression
1 parent 6bb19ef commit a0b4326

File tree

18 files changed

+661
-50
lines changed

18 files changed

+661
-50
lines changed

CodeGeneration/Sources/SyntaxSupport/ExprNodes.swift

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -185,6 +185,41 @@ public let EXPR_NODES: [Node] = [
185185
]
186186
),
187187

188+
// the canImport expr in if config expression
189+
Node(
190+
name: "CanImportExpr",
191+
nameForDiagnostics: "'canImport' expression in if config expression",
192+
kind: "Expr",
193+
children: [
194+
Child(
195+
name: "CanImportKeyword",
196+
kind: .token(choices: [.token(tokenKind: "IdentifierToken")])
197+
),
198+
Child(
199+
name: "LeftParen",
200+
kind: .token(choices: [.token(tokenKind: "LeftParenToken")])
201+
),
202+
Child(
203+
name: "ImportPath",
204+
kind: .token(choices: [.token(tokenKind: "IdentifierToken")])
205+
),
206+
Child(
207+
name: "Comma",
208+
kind: .token(choices: [.token(tokenKind: "CommaToken")]),
209+
isOptional: true
210+
),
211+
Child(
212+
name: "Version",
213+
kind: .node(kind: "TupleExprElement"),
214+
isOptional: true
215+
),
216+
Child(
217+
name: "RightParen",
218+
kind: .token(choices: [.token(tokenKind: "RightParenToken")])
219+
),
220+
]
221+
),
222+
188223
// case-item -> pattern where-clause? ','?
189224
Node(
190225
name: "CaseItem",

Sources/SwiftParser/Expressions.swift

Lines changed: 117 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -762,6 +762,14 @@ extension Parser {
762762
continue
763763
}
764764

765+
// canImport expression
766+
if let leadingIdentifier = leadingExpr.as(RawIdentifierExprSyntax.self),
767+
leadingIdentifier.identifier.tokenText == "canImport"
768+
{
769+
leadingExpr = self.parseCanImportExpression(leadingIdentifier)
770+
continue
771+
}
772+
765773
// If there is an expr-call-suffix, parse it and form a call.
766774
if let lparen = self.consume(if: TokenSpec(.leftParen, allowAtStartOfLine: false)) {
767775
let args = self.parseArgumentListElements(pattern: pattern)
@@ -2005,50 +2013,59 @@ extension Parser {
20052013
var keepGoing: RawTokenSyntax? = nil
20062014
var loopProgress = LoopProgressCondition()
20072015
repeat {
2008-
let unexpectedBeforeLabel: RawUnexpectedNodesSyntax?
2009-
let label: RawTokenSyntax?
2010-
let colon: RawTokenSyntax?
2011-
if currentToken.canBeArgumentLabel(allowDollarIdentifier: true) && self.peek().rawTokenKind == .colon {
2012-
(unexpectedBeforeLabel, label) = parseArgumentLabel()
2013-
colon = consumeAnyToken()
2014-
} else {
2015-
unexpectedBeforeLabel = nil
2016-
label = nil
2017-
colon = nil
2018-
}
2019-
2020-
// See if we have an operator decl ref '(<op>)'. The operator token in
2021-
// this case lexes as a binary operator because it neither leads nor
2022-
// follows a proper subexpression.
2023-
let expr: RawExprSyntax
2024-
if self.at(.binaryOperator)
2025-
&& (self.peek().rawTokenKind == .comma || self.peek().rawTokenKind == .rightParen || self.peek().rawTokenKind == .rightSquareBracket)
2026-
{
2027-
let (ident, args) = self.parseDeclNameRef(.operators)
2028-
expr = RawExprSyntax(
2029-
RawIdentifierExprSyntax(
2030-
identifier: ident,
2031-
declNameArguments: args,
2032-
arena: self.arena
2033-
)
2034-
)
2035-
} else {
2036-
expr = self.parseExpression(pattern: pattern)
2037-
}
2038-
keepGoing = self.consume(if: .comma)
2016+
let tupleExprElement = self.parseTupleExprElement(pattern: pattern)
2017+
keepGoing = tupleExprElement.trailingComma
20392018
result.append(
2040-
RawTupleExprElementSyntax(
2041-
unexpectedBeforeLabel,
2042-
label: label,
2043-
colon: colon,
2044-
expression: expr,
2045-
trailingComma: keepGoing,
2046-
arena: self.arena
2047-
)
2019+
tupleExprElement
20482020
)
20492021
} while keepGoing != nil && loopProgress.evaluate(currentToken)
20502022
return result
20512023
}
2024+
2025+
mutating func parseTupleExprElement(pattern: PatternContext) -> RawTupleExprElementSyntax {
2026+
let unexpectedBeforeLabel: RawUnexpectedNodesSyntax?
2027+
let label: RawTokenSyntax?
2028+
let colon: RawTokenSyntax?
2029+
if currentToken.canBeArgumentLabel(allowDollarIdentifier: true) && self.peek().rawTokenKind == .colon {
2030+
(unexpectedBeforeLabel, label) = parseArgumentLabel()
2031+
colon = consumeAnyToken()
2032+
} else {
2033+
unexpectedBeforeLabel = nil
2034+
label = nil
2035+
colon = nil
2036+
}
2037+
2038+
// See if we have an operator decl ref '(<op>)'. The operator token in
2039+
// this case lexes as a binary operator because it neither leads nor
2040+
// follows a proper subexpression.
2041+
let expr: RawExprSyntax
2042+
if self.at(.binaryOperator)
2043+
&& (self.peek().rawTokenKind == .comma || self.peek().rawTokenKind == .rightParen || self.peek().rawTokenKind == .rightSquareBracket)
2044+
{
2045+
let (ident, args) = self.parseDeclNameRef(.operators)
2046+
expr = RawExprSyntax(
2047+
RawIdentifierExprSyntax(
2048+
identifier: ident,
2049+
declNameArguments: args,
2050+
arena: self.arena
2051+
)
2052+
)
2053+
} else {
2054+
expr = self.parseExpression(pattern: pattern)
2055+
}
2056+
2057+
let comma = self.consume(if: .comma)
2058+
2059+
return RawTupleExprElementSyntax(
2060+
unexpectedBeforeLabel,
2061+
label: label,
2062+
colon: colon,
2063+
expression: expr,
2064+
trailingComma: comma,
2065+
arena: self.arena
2066+
)
2067+
}
2068+
20522069
}
20532070

20542071
extension Parser {
@@ -2544,6 +2561,67 @@ extension Parser {
25442561
}
25452562
}
25462563

2564+
// MARK: Platform Condition
2565+
extension Parser {
2566+
mutating func parseCanImportExpression(_ canImportIdentifierExpr: RawIdentifierExprSyntax) -> RawExprSyntax {
2567+
let (unexpectedBeforeLeftParen, leftParen) = self.expect(.leftParen)
2568+
2569+
let (unexpectedBeforeImportPath, importPath) = self.expect(.identifier)
2570+
2571+
let comma = self.consume(if: .comma)
2572+
var versionTupleExpr: RawTupleExprElementSyntax?
2573+
2574+
if comma != nil {
2575+
versionTupleExpr = self.parseTupleExprElement(pattern: .none)
2576+
2577+
if versionTupleExpr?.label == nil {
2578+
versionTupleExpr = RawTupleExprElementSyntax(
2579+
versionTupleExpr?.unexpectedBeforeLabel,
2580+
label: self.missingToken(.identifier),
2581+
versionTupleExpr?.unexpectedBetweenLabelAndColon,
2582+
colon: versionTupleExpr?.colon,
2583+
versionTupleExpr?.unexpectedBetweenColonAndExpression,
2584+
expression: versionTupleExpr!.expression,
2585+
versionTupleExpr?.unexpectedBetweenExpressionAndTrailingComma,
2586+
trailingComma: versionTupleExpr?.trailingComma,
2587+
versionTupleExpr?.unexpectedAfterTrailingComma,
2588+
arena: self.arena
2589+
)
2590+
} else if !["_version", "_underlyingVersion"].map({ SyntaxText.init($0) }).contains(versionTupleExpr?.label?.tokenText) {
2591+
versionTupleExpr = RawTupleExprElementSyntax(
2592+
RawUnexpectedNodesSyntax(combining: versionTupleExpr?.unexpectedBeforeLabel, versionTupleExpr?.label, arena: self.arena),
2593+
label: self.missingToken(.identifier),
2594+
versionTupleExpr?.unexpectedBetweenLabelAndColon,
2595+
colon: versionTupleExpr?.colon,
2596+
versionTupleExpr?.unexpectedBetweenColonAndExpression,
2597+
expression: versionTupleExpr!.expression,
2598+
versionTupleExpr?.unexpectedBetweenExpressionAndTrailingComma,
2599+
trailingComma: versionTupleExpr?.trailingComma,
2600+
versionTupleExpr?.unexpectedAfterTrailingComma,
2601+
arena: self.arena
2602+
)
2603+
}
2604+
}
2605+
2606+
let (unexpectedBeforeRightParen, rightParen) = self.expect(.rightParen)
2607+
2608+
return RawExprSyntax(
2609+
RawCanImportExprSyntax(
2610+
canImportKeyword: canImportIdentifierExpr.identifier,
2611+
unexpectedBeforeLeftParen,
2612+
leftParen: leftParen,
2613+
unexpectedBeforeImportPath,
2614+
importPath: importPath,
2615+
comma: comma,
2616+
version: versionTupleExpr,
2617+
unexpectedBeforeRightParen,
2618+
rightParen: rightParen,
2619+
arena: self.arena
2620+
)
2621+
)
2622+
}
2623+
}
2624+
25472625
// MARK: Lookahead
25482626

25492627
extension Parser.Lookahead {

Sources/SwiftParserDiagnostics/ParseDiagnosticsGenerator.swift

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -514,6 +514,35 @@ public class ParseDiagnosticsGenerator: SyntaxAnyVisitor {
514514
return .visitChildren
515515
}
516516

517+
public override func visit(_ node: CanImportExprSyntax) -> SyntaxVisitorContinueKind {
518+
if shouldSkip(node) {
519+
return .skipChildren
520+
}
521+
522+
if let versionTuple = node.version {
523+
if let label = versionTuple.label,
524+
label.presence == .missing
525+
{
526+
addDiagnostic(
527+
versionTuple,
528+
.canImportWrongSecondParameterLabel,
529+
handledNodes: [versionTuple.id]
530+
)
531+
}
532+
}
533+
534+
if let unexpectedBeforeRightParen = node.unexpectedBetweenVersionAndRightParen {
535+
addDiagnostic(
536+
unexpectedBeforeRightParen,
537+
.canImportWrongParameterNumber,
538+
handledNodes: [unexpectedBeforeRightParen.id]
539+
)
540+
541+
}
542+
543+
return .visitChildren
544+
}
545+
517546
public override func visit(_ node: ConditionElementSyntax) -> SyntaxVisitorContinueKind {
518547
if shouldSkip(node) {
519548
return .skipChildren

Sources/SwiftParserDiagnostics/ParserDiagnosticMessages.swift

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,12 @@ extension DiagnosticMessage where Self == StaticParserError {
9292
public static var associatedTypeCannotUsePack: Self {
9393
.init("associated types cannot be variadic")
9494
}
95+
public static var canImportWrongSecondParameterLabel: Self {
96+
.init("2nd parameter of canImport should be labeled as _version or _underlyingVersion")
97+
}
98+
public static var canImportWrongParameterNumber: Self {
99+
.init("canImport can take only two parameters")
100+
}
95101
public static var caseOutsideOfSwitchOrEnum: Self {
96102
.init("'case' can only appear inside a 'switch' statement or 'enum' declaration")
97103
}

Sources/SwiftParserDiagnostics/generated/SyntaxKindNameForDiagnostics.swift

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,8 @@ extension SyntaxKind {
6969
return "'_borrow' expression"
7070
case .breakStmt:
7171
return "'break' statement"
72+
case .canImportExpr:
73+
return "'canImport' expression in if config expression"
7274
case .catchClauseList:
7375
return "'catch' clause"
7476
case .catchClause:

Sources/SwiftSyntax/Documentation.docc/generated/SwiftSyntax.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,7 @@ allows Swift tools to parse, inspect, generate, and transform Swift source code.
9393
- <doc:SwiftSyntax/BinaryOperatorExprSyntax>
9494
- <doc:SwiftSyntax/BooleanLiteralExprSyntax>
9595
- <doc:SwiftSyntax/BorrowExprSyntax>
96+
- <doc:SwiftSyntax/CanImportExprSyntax>
9697
- <doc:SwiftSyntax/ClosureExprSyntax>
9798
- <doc:SwiftSyntax/DictionaryExprSyntax>
9899
- <doc:SwiftSyntax/DiscardAssignmentExprSyntax>

Sources/SwiftSyntax/generated/ChildNameForKeyPath.swift

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -398,6 +398,32 @@ internal func childName(_ keyPath: AnyKeyPath) -> String? {
398398
return "label"
399399
case \BreakStmtSyntax.unexpectedAfterLabel:
400400
return "unexpectedAfterLabel"
401+
case \CanImportExprSyntax.unexpectedBeforeCanImportKeyword:
402+
return "unexpectedBeforeCanImportKeyword"
403+
case \CanImportExprSyntax.canImportKeyword:
404+
return "canImportKeyword"
405+
case \CanImportExprSyntax.unexpectedBetweenCanImportKeywordAndLeftParen:
406+
return "unexpectedBetweenCanImportKeywordAndLeftParen"
407+
case \CanImportExprSyntax.leftParen:
408+
return "leftParen"
409+
case \CanImportExprSyntax.unexpectedBetweenLeftParenAndImportPath:
410+
return "unexpectedBetweenLeftParenAndImportPath"
411+
case \CanImportExprSyntax.importPath:
412+
return "importPath"
413+
case \CanImportExprSyntax.unexpectedBetweenImportPathAndComma:
414+
return "unexpectedBetweenImportPathAndComma"
415+
case \CanImportExprSyntax.comma:
416+
return "comma"
417+
case \CanImportExprSyntax.unexpectedBetweenCommaAndVersion:
418+
return "unexpectedBetweenCommaAndVersion"
419+
case \CanImportExprSyntax.version:
420+
return "version"
421+
case \CanImportExprSyntax.unexpectedBetweenVersionAndRightParen:
422+
return "unexpectedBetweenVersionAndRightParen"
423+
case \CanImportExprSyntax.rightParen:
424+
return "rightParen"
425+
case \CanImportExprSyntax.unexpectedAfterRightParen:
426+
return "unexpectedAfterRightParen"
401427
case \CaseItemSyntax.unexpectedBeforePattern:
402428
return "unexpectedBeforePattern"
403429
case \CaseItemSyntax.pattern:

Sources/SwiftSyntax/generated/SyntaxAnyVisitor.swift

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -317,6 +317,14 @@ open class SyntaxAnyVisitor: SyntaxVisitor {
317317
visitAnyPost(node._syntaxNode)
318318
}
319319

320+
override open func visit(_ node: CanImportExprSyntax) -> SyntaxVisitorContinueKind {
321+
return visitAny(node._syntaxNode)
322+
}
323+
324+
override open func visitPost(_ node: CanImportExprSyntax) {
325+
visitAnyPost(node._syntaxNode)
326+
}
327+
320328
override open func visit(_ node: CaseItemListSyntax) -> SyntaxVisitorContinueKind {
321329
return visitAny(node._syntaxNode)
322330
}

Sources/SwiftSyntax/generated/SyntaxBaseNodes.swift

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -206,7 +206,7 @@ public struct ExprSyntax: ExprSyntaxProtocol, SyntaxHashable {
206206

207207
public init?<S: SyntaxProtocol>(_ node: S) {
208208
switch node.raw.kind {
209-
case .arrayExpr, .arrowExpr, .asExpr, .assignmentExpr, .awaitExpr, .binaryOperatorExpr, .booleanLiteralExpr, .borrowExpr, .closureExpr, .dictionaryExpr, .discardAssignmentExpr, .editorPlaceholderExpr, .floatLiteralExpr, .forcedValueExpr, .functionCallExpr, .identifierExpr, .ifExpr, .inOutExpr, .infixOperatorExpr, .integerLiteralExpr, .isExpr, .keyPathExpr, .macroExpansionExpr, .memberAccessExpr, .missingExpr, .moveExpr, .nilLiteralExpr, .optionalChainingExpr, .packElementExpr, .packExpansionExpr, .postfixIfConfigExpr, .postfixUnaryExpr, .prefixOperatorExpr, .regexLiteralExpr, .sequenceExpr, .specializeExpr, .stringLiteralExpr, .subscriptExpr, .superRefExpr, .switchExpr, .ternaryExpr, .tryExpr, .tupleExpr, .typeExpr, .unresolvedAsExpr, .unresolvedIsExpr, .unresolvedPatternExpr, .unresolvedTernaryExpr:
209+
case .arrayExpr, .arrowExpr, .asExpr, .assignmentExpr, .awaitExpr, .binaryOperatorExpr, .booleanLiteralExpr, .borrowExpr, .canImportExpr, .closureExpr, .dictionaryExpr, .discardAssignmentExpr, .editorPlaceholderExpr, .floatLiteralExpr, .forcedValueExpr, .functionCallExpr, .identifierExpr, .ifExpr, .inOutExpr, .infixOperatorExpr, .integerLiteralExpr, .isExpr, .keyPathExpr, .macroExpansionExpr, .memberAccessExpr, .missingExpr, .moveExpr, .nilLiteralExpr, .optionalChainingExpr, .packElementExpr, .packExpansionExpr, .postfixIfConfigExpr, .postfixUnaryExpr, .prefixOperatorExpr, .regexLiteralExpr, .sequenceExpr, .specializeExpr, .stringLiteralExpr, .subscriptExpr, .superRefExpr, .switchExpr, .ternaryExpr, .tryExpr, .tupleExpr, .typeExpr, .unresolvedAsExpr, .unresolvedIsExpr, .unresolvedPatternExpr, .unresolvedTernaryExpr:
210210
self._syntaxNode = node._syntaxNode
211211
default:
212212
return nil
@@ -218,7 +218,7 @@ public struct ExprSyntax: ExprSyntaxProtocol, SyntaxHashable {
218218
/// is undefined.
219219
internal init(_ data: SyntaxData) {
220220
switch data.raw.kind {
221-
case .arrayExpr, .arrowExpr, .asExpr, .assignmentExpr, .awaitExpr, .binaryOperatorExpr, .booleanLiteralExpr, .borrowExpr, .closureExpr, .dictionaryExpr, .discardAssignmentExpr, .editorPlaceholderExpr, .floatLiteralExpr, .forcedValueExpr, .functionCallExpr, .identifierExpr, .ifExpr, .inOutExpr, .infixOperatorExpr, .integerLiteralExpr, .isExpr, .keyPathExpr, .macroExpansionExpr, .memberAccessExpr, .missingExpr, .moveExpr, .nilLiteralExpr, .optionalChainingExpr, .packElementExpr, .packExpansionExpr, .postfixIfConfigExpr, .postfixUnaryExpr, .prefixOperatorExpr, .regexLiteralExpr, .sequenceExpr, .specializeExpr, .stringLiteralExpr, .subscriptExpr, .superRefExpr, .switchExpr, .ternaryExpr, .tryExpr, .tupleExpr, .typeExpr, .unresolvedAsExpr, .unresolvedIsExpr, .unresolvedPatternExpr, .unresolvedTernaryExpr:
221+
case .arrayExpr, .arrowExpr, .asExpr, .assignmentExpr, .awaitExpr, .binaryOperatorExpr, .booleanLiteralExpr, .borrowExpr, .canImportExpr, .closureExpr, .dictionaryExpr, .discardAssignmentExpr, .editorPlaceholderExpr, .floatLiteralExpr, .forcedValueExpr, .functionCallExpr, .identifierExpr, .ifExpr, .inOutExpr, .infixOperatorExpr, .integerLiteralExpr, .isExpr, .keyPathExpr, .macroExpansionExpr, .memberAccessExpr, .missingExpr, .moveExpr, .nilLiteralExpr, .optionalChainingExpr, .packElementExpr, .packExpansionExpr, .postfixIfConfigExpr, .postfixUnaryExpr, .prefixOperatorExpr, .regexLiteralExpr, .sequenceExpr, .specializeExpr, .stringLiteralExpr, .subscriptExpr, .superRefExpr, .switchExpr, .ternaryExpr, .tryExpr, .tupleExpr, .typeExpr, .unresolvedAsExpr, .unresolvedIsExpr, .unresolvedPatternExpr, .unresolvedTernaryExpr:
222222
break
223223
default:
224224
preconditionFailure("Unable to create ExprSyntax from \(data.raw.kind)")
@@ -262,6 +262,7 @@ public struct ExprSyntax: ExprSyntaxProtocol, SyntaxHashable {
262262
.node(BinaryOperatorExprSyntax.self),
263263
.node(BooleanLiteralExprSyntax.self),
264264
.node(BorrowExprSyntax.self),
265+
.node(CanImportExprSyntax.self),
265266
.node(ClosureExprSyntax.self),
266267
.node(DictionaryExprSyntax.self),
267268
.node(DiscardAssignmentExprSyntax.self),
@@ -716,6 +717,7 @@ extension Syntax {
716717
.node(BooleanLiteralExprSyntax.self),
717718
.node(BorrowExprSyntax.self),
718719
.node(BreakStmtSyntax.self),
720+
.node(CanImportExprSyntax.self),
719721
.node(CaseItemListSyntax.self),
720722
.node(CaseItemSyntax.self),
721723
.node(CatchClauseListSyntax.self),

Sources/SwiftSyntax/generated/SyntaxEnum.swift

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ public enum SyntaxEnum {
4949
case booleanLiteralExpr(BooleanLiteralExprSyntax)
5050
case borrowExpr(BorrowExprSyntax)
5151
case breakStmt(BreakStmtSyntax)
52+
case canImportExpr(CanImportExprSyntax)
5253
case caseItemList(CaseItemListSyntax)
5354
case caseItem(CaseItemSyntax)
5455
case catchClauseList(CatchClauseListSyntax)
@@ -358,6 +359,8 @@ public extension Syntax {
358359
return .borrowExpr(BorrowExprSyntax(self)!)
359360
case .breakStmt:
360361
return .breakStmt(BreakStmtSyntax(self)!)
362+
case .canImportExpr:
363+
return .canImportExpr(CanImportExprSyntax(self)!)
361364
case .caseItemList:
362365
return .caseItemList(CaseItemListSyntax(self)!)
363366
case .caseItem:

0 commit comments

Comments
 (0)