Skip to content

Commit ef06459

Browse files
committed
[WIP] Add diagnostics for missing & wrong label in
canImport expression
1 parent ec486c4 commit ef06459

17 files changed

+617
-47
lines changed

Sources/SwiftParser/Expressions.swift

Lines changed: 119 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -761,7 +761,16 @@ extension Parser {
761761
leadingExpr = self.parseDottedExpressionSuffix(leadingExpr)
762762
continue
763763
}
764-
764+
765+
766+
// canImport expression
767+
if let leadingIdentifier = leadingExpr.as(RawIdentifierExprSyntax.self),
768+
leadingIdentifier.identifier.tokenText == "canImport" {
769+
leadingExpr = self.parseCanImportExpression(leadingIdentifier)
770+
continue
771+
}
772+
773+
765774
// If there is an expr-call-suffix, parse it and form a call.
766775
if let lparen = self.consume(if: TokenSpec(.leftParen, allowAtStartOfLine: false)) {
767776
let args = self.parseArgumentListElements(pattern: pattern)
@@ -2005,50 +2014,59 @@ extension Parser {
20052014
var keepGoing: RawTokenSyntax? = nil
20062015
var loopProgress = LoopProgressCondition()
20072016
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)
2017+
let tupleExprElement = self.parseTupleExprElement(pattern: pattern)
2018+
keepGoing = tupleExprElement.trailingComma
20392019
result.append(
2040-
RawTupleExprElementSyntax(
2041-
unexpectedBeforeLabel,
2042-
label: label,
2043-
colon: colon,
2044-
expression: expr,
2045-
trailingComma: keepGoing,
2046-
arena: self.arena
2047-
)
2020+
tupleExprElement
20482021
)
20492022
} while keepGoing != nil && loopProgress.evaluate(currentToken)
20502023
return result
20512024
}
2025+
2026+
mutating func parseTupleExprElement(pattern: PatternContext) -> RawTupleExprElementSyntax {
2027+
let unexpectedBeforeLabel: RawUnexpectedNodesSyntax?
2028+
let label: RawTokenSyntax?
2029+
let colon: RawTokenSyntax?
2030+
if currentToken.canBeArgumentLabel(allowDollarIdentifier: true) && self.peek().rawTokenKind == .colon {
2031+
(unexpectedBeforeLabel, label) = parseArgumentLabel()
2032+
colon = consumeAnyToken()
2033+
} else {
2034+
unexpectedBeforeLabel = nil
2035+
label = nil
2036+
colon = nil
2037+
}
2038+
2039+
// See if we have an operator decl ref '(<op>)'. The operator token in
2040+
// this case lexes as a binary operator because it neither leads nor
2041+
// follows a proper subexpression.
2042+
let expr: RawExprSyntax
2043+
if self.at(.binaryOperator)
2044+
&& (self.peek().rawTokenKind == .comma || self.peek().rawTokenKind == .rightParen || self.peek().rawTokenKind == .rightSquareBracket)
2045+
{
2046+
let (ident, args) = self.parseDeclNameRef(.operators)
2047+
expr = RawExprSyntax(
2048+
RawIdentifierExprSyntax(
2049+
identifier: ident,
2050+
declNameArguments: args,
2051+
arena: self.arena
2052+
)
2053+
)
2054+
} else {
2055+
expr = self.parseExpression(pattern: pattern)
2056+
}
2057+
2058+
let comma = self.consume(if: .comma)
2059+
2060+
return RawTupleExprElementSyntax(
2061+
unexpectedBeforeLabel,
2062+
label: label,
2063+
colon: colon,
2064+
expression: expr,
2065+
trailingComma: comma,
2066+
arena: self.arena
2067+
)
2068+
}
2069+
20522070
}
20532071

20542072
extension Parser {
@@ -2544,6 +2562,67 @@ extension Parser {
25442562
}
25452563
}
25462564

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

25492628
extension Parser.Lookahead {

Sources/SwiftParserDiagnostics/ParseDiagnosticsGenerator.swift

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -513,6 +513,29 @@ public class ParseDiagnosticsGenerator: SyntaxAnyVisitor {
513513
}
514514
return .visitChildren
515515
}
516+
517+
public override func visit(_ node: CanImportExprSyntax) -> SyntaxVisitorContinueKind {
518+
if shouldSkip(node) {
519+
return .skipChildren
520+
}
521+
522+
if let versionTuple = node.version {
523+
524+
let allowedLabel = ["_version", "_underlyingVersion"]
525+
guard let label = versionTuple.label,
526+
allowedLabel.contains(label.text) else {
527+
addDiagnostic(
528+
versionTuple,
529+
.canImportWrongSecondParameterLabel,
530+
handledNodes: [versionTuple.id]
531+
)
532+
return .visitChildren
533+
}
534+
535+
}
536+
537+
return .visitChildren
538+
}
516539

517540
public override func visit(_ node: ConditionElementSyntax) -> SyntaxVisitorContinueKind {
518541
if shouldSkip(node) {

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:

Sources/SwiftSyntax/generated/SyntaxKind.swift

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ public enum SyntaxKind {
4949
case booleanLiteralExpr
5050
case borrowExpr
5151
case breakStmt
52+
case canImportExpr
5253
case caseItemList
5354
case caseItem
5455
case catchClauseList
@@ -473,6 +474,8 @@ public enum SyntaxKind {
473474
return BorrowExprSyntax.self
474475
case .breakStmt:
475476
return BreakStmtSyntax.self
477+
case .canImportExpr:
478+
return CanImportExprSyntax.self
476479
case .caseItemList:
477480
return CaseItemListSyntax.self
478481
case .caseItem:

0 commit comments

Comments
 (0)