Skip to content

Commit 72ed377

Browse files
committed
Add diagnostic for canImport in ifconfig
Add keywords for conditional compiling Add `forDirective` parameter for `parsePrimaryExpression` to branch into directive expression parsing
1 parent 6bb19ef commit 72ed377

21 files changed

+721
-12
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",

CodeGeneration/Sources/SyntaxSupport/KeywordSpec.swift

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,12 +82,15 @@ public let KEYWORDS: [KeywordSpec] = [
8282
KeywordSpec("_TrivialAtMost"),
8383
KeywordSpec("_typeEraser"),
8484
KeywordSpec("_unavailableFromAsync"),
85+
KeywordSpec("_underlyingVersion"),
8586
KeywordSpec("_UnknownLayout"),
87+
KeywordSpec("_version"),
8688
KeywordSpec("actor"),
8789
KeywordSpec("addressWithNativeOwner"),
8890
KeywordSpec("addressWithOwner"),
8991
KeywordSpec("any"),
9092
KeywordSpec("Any", isLexerClassified: true, requiresTrailingSpace: true),
93+
KeywordSpec("arch"),
9194
KeywordSpec("as", isLexerClassified: true, requiresTrailingSpace: true),
9295
KeywordSpec("assignment"),
9396
KeywordSpec("associatedtype", isLexerClassified: true, requiresTrailingSpace: true),
@@ -102,9 +105,11 @@ public let KEYWORDS: [KeywordSpec] = [
102105
KeywordSpec("block"),
103106
KeywordSpec("borrowing"),
104107
KeywordSpec("break", isLexerClassified: true, requiresTrailingSpace: true),
108+
KeywordSpec("canImport"),
105109
KeywordSpec("case", isLexerClassified: true, requiresTrailingSpace: true),
106110
KeywordSpec("catch", isLexerClassified: true, requiresLeadingSpace: true),
107111
KeywordSpec("class", isLexerClassified: true, requiresTrailingSpace: true),
112+
KeywordSpec("compiler"),
108113
KeywordSpec("consume"),
109114
KeywordSpec("consuming"),
110115
KeywordSpec("continue", isLexerClassified: true, requiresTrailingSpace: true),
@@ -179,6 +184,7 @@ public let KEYWORDS: [KeywordSpec] = [
179184
KeywordSpec("open"),
180185
KeywordSpec("operator", isLexerClassified: true, requiresTrailingSpace: true),
181186
KeywordSpec("optional"),
187+
KeywordSpec("os"),
182188
KeywordSpec("override"),
183189
KeywordSpec("package"),
184190
KeywordSpec("postfix"),
@@ -212,6 +218,7 @@ public let KEYWORDS: [KeywordSpec] = [
212218
KeywordSpec("swift"),
213219
KeywordSpec("switch", isLexerClassified: true, requiresTrailingSpace: true),
214220
KeywordSpec("target"),
221+
KeywordSpec("targetEnvironment"),
215222
KeywordSpec("throw", isLexerClassified: true, requiresTrailingSpace: true),
216223
KeywordSpec("throws", isLexerClassified: true, requiresTrailingSpace: true),
217224
KeywordSpec("transpose"),

Sources/SwiftParser/Expressions.swift

Lines changed: 77 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -601,7 +601,7 @@ extension Parser {
601601
forDirective: Bool,
602602
pattern: PatternContext
603603
) -> RawExprSyntax {
604-
let head = self.parsePrimaryExpression(pattern: pattern, flavor: flavor)
604+
let head = self.parsePrimaryExpression(pattern: pattern, forDirective: forDirective, flavor: flavor)
605605
guard !head.is(RawMissingExprSyntax.self) else {
606606
return head
607607
}
@@ -1151,8 +1151,15 @@ extension Parser {
11511151
@_spi(RawSyntax)
11521152
public mutating func parsePrimaryExpression(
11531153
pattern: PatternContext,
1154+
forDirective: Bool,
11541155
flavor: ExprFlavor
11551156
) -> RawExprSyntax {
1157+
if forDirective == true,
1158+
let directiveExpr = self.parsePrimaryExprForDirective()
1159+
{
1160+
return RawExprSyntax(directiveExpr)
1161+
}
1162+
11561163
switch self.at(anyIn: PrimaryExpressionStart.self) {
11571164
case (.integerLiteral, let handle)?:
11581165
let digits = self.eat(handle)
@@ -1291,6 +1298,29 @@ extension Parser {
12911298
return RawExprSyntax(RawMissingExprSyntax(arena: self.arena))
12921299
}
12931300
}
1301+
1302+
// try to parse a primary expression for a directive
1303+
mutating func parsePrimaryExprForDirective() -> RawExprSyntax? {
1304+
switch self.at(anyIn: CompilationCondition.self) {
1305+
case (.canImportKeyword, let handle)?:
1306+
let canImportKeyword = self.eat(handle)
1307+
return RawExprSyntax(self.parseCanImportExpression(canImportKeyword))
1308+
case (.trueKeyword, let handle)?,
1309+
(.falseKeyword, let handle)?:
1310+
let tok = self.eat(handle)
1311+
return RawExprSyntax(
1312+
RawBooleanLiteralExprSyntax(
1313+
booleanLiteral: tok,
1314+
arena: self.arena
1315+
)
1316+
)
1317+
case (.identifier, _)?:
1318+
return RawExprSyntax(self.parseIdentifierExpression())
1319+
1320+
default:
1321+
return nil
1322+
}
1323+
}
12941324
}
12951325

12961326
extension Parser {
@@ -2544,6 +2574,52 @@ extension Parser {
25442574
}
25452575
}
25462576

2577+
// MARK: Platform Condition
2578+
extension Parser {
2579+
mutating func parseCanImportExpression(_ canImportKeyword: RawTokenSyntax) -> RawExprSyntax {
2580+
2581+
let (unexpectedBeforeLeftParen, leftParen) = self.expect(.leftParen)
2582+
2583+
let (unexpectedBeforeImportPath, importPath) = self.expect(.identifier)
2584+
2585+
let comma = self.consume(if: .comma)
2586+
var versionTupleExpr: RawTupleExprElementSyntax?
2587+
2588+
if comma != nil {
2589+
let (unexpectedBeforeLabel, label) = self.expect(.keyword(._version), .keyword(._underlyingVersion), default: .keyword(._version))
2590+
let (unexpectedBeforeColon, colon) = self.expect(.colon)
2591+
let version = self.parseExpression()
2592+
2593+
versionTupleExpr = RawTupleExprElementSyntax(
2594+
unexpectedBeforeLabel,
2595+
label: label,
2596+
unexpectedBeforeColon,
2597+
colon: colon,
2598+
expression: version,
2599+
trailingComma: nil,
2600+
arena: self.arena
2601+
)
2602+
}
2603+
2604+
let (unexpectedBeforeRightParen, rightParen) = self.expect(.rightParen)
2605+
2606+
return RawExprSyntax(
2607+
RawCanImportExprSyntax(
2608+
canImportKeyword: canImportKeyword,
2609+
unexpectedBeforeLeftParen,
2610+
leftParen: leftParen,
2611+
unexpectedBeforeImportPath,
2612+
importPath: importPath,
2613+
comma: comma,
2614+
version: versionTupleExpr,
2615+
unexpectedBeforeRightParen,
2616+
rightParen: rightParen,
2617+
arena: self.arena
2618+
)
2619+
)
2620+
}
2621+
}
2622+
25472623
// MARK: Lookahead
25482624

25492625
extension Parser.Lookahead {

Sources/SwiftParser/TokenSpecSet.swift

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,53 @@ enum CanBeStatementStart: TokenSpecSet {
132132
}
133133
}
134134

135+
enum CompilationCondition: TokenSpecSet {
136+
// platform condition
137+
case osKeyword
138+
case archKeyword
139+
case swiftKeyword
140+
case compilerKeyword
141+
case canImportKeyword
142+
case targetEnvironmentKeyword
143+
144+
// identifier
145+
case identifier
146+
147+
// boolean literal
148+
case trueKeyword
149+
case falseKeyword
150+
151+
init?(lexeme: Lexer.Lexeme) {
152+
switch PrepareForKeywordMatch(lexeme) {
153+
case TokenSpec(.os): self = .osKeyword
154+
case TokenSpec(.arch): self = .archKeyword
155+
case TokenSpec(.swift): self = .swiftKeyword
156+
case TokenSpec(.compiler): self = .compilerKeyword
157+
case TokenSpec(.canImport): self = .canImportKeyword
158+
case TokenSpec(.targetEnvironment): self = .targetEnvironmentKeyword
159+
case TokenSpec(.identifier): self = .identifier
160+
case TokenSpec(.true): self = .trueKeyword
161+
case TokenSpec(.false): self = .falseKeyword
162+
default: return nil
163+
}
164+
}
165+
166+
var spec: TokenSpec {
167+
switch self {
168+
case .osKeyword: return .keyword(.os)
169+
case .archKeyword: return .keyword(.arch)
170+
case .swiftKeyword: return .keyword(.swift)
171+
case .compilerKeyword: return .keyword(.compiler)
172+
case .canImportKeyword: return .keyword(.canImport)
173+
case .targetEnvironmentKeyword: return .keyword(.targetEnvironment)
174+
case .identifier: return .identifier
175+
case .trueKeyword: return .keyword(.true)
176+
case .falseKeyword: return .keyword(.false)
177+
}
178+
}
179+
180+
}
181+
135182
enum ContextualDeclKeyword: TokenSpecSet {
136183
case __consuming
137184
case _compilerInitialized

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:

0 commit comments

Comments
 (0)