Skip to content

Commit f025614

Browse files
authored
Merge pull request #2076 from Matejkob/improve-keywordSpec-modeling
Improve modeling of `KeywordSpec` in CodeGeneration
2 parents 6f23c24 + 83a9132 commit f025614

File tree

11 files changed

+446
-440
lines changed

11 files changed

+446
-440
lines changed

CodeGeneration/Sources/SyntaxSupport/KeywordSpec.swift

Lines changed: 73 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -13,39 +13,49 @@
1313
import SwiftSyntax
1414

1515
public struct KeywordSpec {
16-
public var name: String
16+
/// The name of the keyword.
17+
public let name: String
1718

18-
/// If `true`, this is for an experimental language feature, and any public
19-
/// API generated should be SPI.
20-
public var isExperimental: Bool
19+
/// Indicates if the keyword is part of an experimental language feature.
20+
///
21+
/// If `true`, this keyword is for an experimental language feature, and any public
22+
/// API generated should be marked as SPI
23+
public let isExperimental: Bool
2124

22-
public var isLexerClassified: Bool
23-
public var requiresLeadingSpace: Bool
24-
public var requiresTrailingSpace: Bool
25+
/// Indicates if the token kind is switched from being an identifier to a keyword in the lexer.
26+
public let isLexerClassified: Bool
2527

26-
public var escapedName: String {
27-
if isLexerClassified || name == "Type" || name == "Protocol" {
28-
return "`\(name)`"
28+
/// The name of this keyword that's suitable to be used for variable or enum case names.
29+
public var varOrCaseName: TokenSyntax {
30+
if name == "init" {
31+
return "`init`"
2932
} else {
30-
return name
33+
return TokenSyntax.identifier(name)
3134
}
3235
}
3336

34-
/// Retrieve the attributes that should be printed on any API for the
35-
/// generated keyword.
37+
/// The attributes that should be printed on any API for the generated keyword.
38+
///
39+
/// This is typically used to mark APIs as SPI when the keyword is part of an experimental language feature.
3640
public var apiAttributes: AttributeListSyntax {
3741
guard isExperimental else { return "" }
3842
return AttributeListSyntax("@_spi(ExperimentalLanguageFeatures)").with(\.trailingTrivia, .newline)
3943
}
4044

41-
/// `isLexerClassified` determines whether the token kind is switched from being an identifier to a keyword in the lexer.
42-
/// This is true for keywords that used to be considered non-contextual.
43-
init(_ name: String, isExperimental: Bool = false, isLexerClassified: Bool = false, requiresLeadingSpace: Bool = false, requiresTrailingSpace: Bool = false) {
45+
/// Initializes a new `KeywordSpec` instance.
46+
///
47+
/// - Parameters:
48+
/// - name: A name of the keyword.
49+
/// - isExperimental: Indicates if the keyword is part of an experimental language feature.
50+
/// - isLexerClassified: Indicates if the token kind is switched from being an identifier to a keyword in the lexer.
51+
init(
52+
_ name: String,
53+
isExperimental: Bool = false,
54+
isLexerClassified: Bool = false
55+
) {
4456
self.name = name
4557
self.isExperimental = isExperimental
4658
self.isLexerClassified = isLexerClassified
47-
self.requiresLeadingSpace = requiresLeadingSpace
48-
self.requiresTrailingSpace = requiresTrailingSpace
4959
}
5060
}
5161

@@ -375,17 +385,17 @@ public enum Keyword: CaseIterable {
375385
case .any:
376386
return KeywordSpec("any")
377387
case .Any:
378-
return KeywordSpec("Any", isLexerClassified: true, requiresTrailingSpace: true)
388+
return KeywordSpec("Any", isLexerClassified: true)
379389
case .as:
380-
return KeywordSpec("as", isLexerClassified: true, requiresTrailingSpace: true)
390+
return KeywordSpec("as", isLexerClassified: true)
381391
case .assignment:
382392
return KeywordSpec("assignment")
383393
case .associatedtype:
384-
return KeywordSpec("associatedtype", isLexerClassified: true, requiresTrailingSpace: true)
394+
return KeywordSpec("associatedtype", isLexerClassified: true)
385395
case .associativity:
386396
return KeywordSpec("associativity")
387397
case .async:
388-
return KeywordSpec("async", requiresTrailingSpace: true)
398+
return KeywordSpec("async")
389399
case .attached:
390400
return KeywordSpec("attached")
391401
case .autoclosure:
@@ -395,7 +405,7 @@ public enum Keyword: CaseIterable {
395405
case .available:
396406
return KeywordSpec("available")
397407
case .await:
398-
return KeywordSpec("await", requiresTrailingSpace: true)
408+
return KeywordSpec("await")
399409
case .backDeployed:
400410
return KeywordSpec("backDeployed")
401411
case .before:
@@ -405,15 +415,15 @@ public enum Keyword: CaseIterable {
405415
case .borrowing:
406416
return KeywordSpec("borrowing")
407417
case .break:
408-
return KeywordSpec("break", isLexerClassified: true, requiresTrailingSpace: true)
418+
return KeywordSpec("break", isLexerClassified: true)
409419
case .canImport:
410420
return KeywordSpec("canImport")
411421
case .case:
412-
return KeywordSpec("case", isLexerClassified: true, requiresTrailingSpace: true)
422+
return KeywordSpec("case", isLexerClassified: true)
413423
case .catch:
414-
return KeywordSpec("catch", isLexerClassified: true, requiresLeadingSpace: true)
424+
return KeywordSpec("catch", isLexerClassified: true)
415425
case .class:
416-
return KeywordSpec("class", isLexerClassified: true, requiresTrailingSpace: true)
426+
return KeywordSpec("class", isLexerClassified: true)
417427
case .compiler:
418428
return KeywordSpec("compiler")
419429
case .consume:
@@ -423,7 +433,7 @@ public enum Keyword: CaseIterable {
423433
case .consuming:
424434
return KeywordSpec("consuming")
425435
case .continue:
426-
return KeywordSpec("continue", isLexerClassified: true, requiresTrailingSpace: true)
436+
return KeywordSpec("continue", isLexerClassified: true)
427437
case .convenience:
428438
return KeywordSpec("convenience")
429439
case .convention:
@@ -433,7 +443,7 @@ public enum Keyword: CaseIterable {
433443
case .default:
434444
return KeywordSpec("default", isLexerClassified: true)
435445
case .defer:
436-
return KeywordSpec("defer", isLexerClassified: true, requiresTrailingSpace: true)
446+
return KeywordSpec("defer", isLexerClassified: true)
437447
case .deinit:
438448
return KeywordSpec("deinit", isLexerClassified: true)
439449
case .deprecated:
@@ -453,47 +463,47 @@ public enum Keyword: CaseIterable {
453463
case .each:
454464
return KeywordSpec("each")
455465
case .else:
456-
return KeywordSpec("else", isLexerClassified: true, requiresLeadingSpace: true, requiresTrailingSpace: true)
466+
return KeywordSpec("else", isLexerClassified: true)
457467
case .enum:
458-
return KeywordSpec("enum", isLexerClassified: true, requiresTrailingSpace: true)
468+
return KeywordSpec("enum", isLexerClassified: true)
459469
case .escaping:
460470
return KeywordSpec("escaping")
461471
case .exclusivity:
462472
return KeywordSpec("exclusivity")
463473
case .exported:
464474
return KeywordSpec("exported")
465475
case .extension:
466-
return KeywordSpec("extension", isLexerClassified: true, requiresTrailingSpace: true)
476+
return KeywordSpec("extension", isLexerClassified: true)
467477
case .fallthrough:
468-
return KeywordSpec("fallthrough", isLexerClassified: true, requiresTrailingSpace: true)
478+
return KeywordSpec("fallthrough", isLexerClassified: true)
469479
case .false:
470480
return KeywordSpec("false", isLexerClassified: true)
471481
case .file:
472482
return KeywordSpec("file")
473483
case .fileprivate:
474-
return KeywordSpec("fileprivate", isLexerClassified: true, requiresTrailingSpace: true)
484+
return KeywordSpec("fileprivate", isLexerClassified: true)
475485
case .final:
476486
return KeywordSpec("final")
477487
case .for:
478-
return KeywordSpec("for", isLexerClassified: true, requiresTrailingSpace: true)
488+
return KeywordSpec("for", isLexerClassified: true)
479489
case .discard:
480490
return KeywordSpec("discard")
481491
case .forward:
482492
return KeywordSpec("forward")
483493
case .func:
484-
return KeywordSpec("func", isLexerClassified: true, requiresTrailingSpace: true)
494+
return KeywordSpec("func", isLexerClassified: true)
485495
case .get:
486496
return KeywordSpec("get")
487497
case .guard:
488-
return KeywordSpec("guard", isLexerClassified: true, requiresTrailingSpace: true)
498+
return KeywordSpec("guard", isLexerClassified: true)
489499
case .higherThan:
490500
return KeywordSpec("higherThan")
491501
case .if:
492-
return KeywordSpec("if", isLexerClassified: true, requiresTrailingSpace: true)
502+
return KeywordSpec("if", isLexerClassified: true)
493503
case .import:
494-
return KeywordSpec("import", isLexerClassified: true, requiresTrailingSpace: true)
504+
return KeywordSpec("import", isLexerClassified: true)
495505
case .in:
496-
return KeywordSpec("in", isLexerClassified: true, requiresLeadingSpace: true, requiresTrailingSpace: true)
506+
return KeywordSpec("in", isLexerClassified: true)
497507
case .indirect:
498508
return KeywordSpec("indirect")
499509
case .infix:
@@ -505,13 +515,13 @@ public enum Keyword: CaseIterable {
505515
case .inline:
506516
return KeywordSpec("inline")
507517
case .inout:
508-
return KeywordSpec("inout", isLexerClassified: true, requiresTrailingSpace: true)
518+
return KeywordSpec("inout", isLexerClassified: true)
509519
case .internal:
510-
return KeywordSpec("internal", isLexerClassified: true, requiresTrailingSpace: true)
520+
return KeywordSpec("internal", isLexerClassified: true)
511521
case .introduced:
512522
return KeywordSpec("introduced")
513523
case .is:
514-
return KeywordSpec("is", isLexerClassified: true, requiresTrailingSpace: true)
524+
return KeywordSpec("is", isLexerClassified: true)
515525
case .isolated:
516526
return KeywordSpec("isolated")
517527
case .kind:
@@ -521,7 +531,7 @@ public enum Keyword: CaseIterable {
521531
case .left:
522532
return KeywordSpec("left")
523533
case .let:
524-
return KeywordSpec("let", isLexerClassified: true, requiresTrailingSpace: true)
534+
return KeywordSpec("let", isLexerClassified: true)
525535
case .line:
526536
return KeywordSpec("line")
527537
case .linear:
@@ -565,7 +575,7 @@ public enum Keyword: CaseIterable {
565575
case .open:
566576
return KeywordSpec("open")
567577
case .operator:
568-
return KeywordSpec("operator", isLexerClassified: true, requiresTrailingSpace: true)
578+
return KeywordSpec("operator", isLexerClassified: true)
569579
case .optional:
570580
return KeywordSpec("optional")
571581
case .override:
@@ -575,29 +585,29 @@ public enum Keyword: CaseIterable {
575585
case .postfix:
576586
return KeywordSpec("postfix")
577587
case .precedencegroup:
578-
return KeywordSpec("precedencegroup", isLexerClassified: true, requiresTrailingSpace: true)
588+
return KeywordSpec("precedencegroup", isLexerClassified: true)
579589
case .prefix:
580590
return KeywordSpec("prefix")
581591
case .private:
582-
return KeywordSpec("private", isLexerClassified: true, requiresTrailingSpace: true)
592+
return KeywordSpec("private", isLexerClassified: true)
583593
case .Protocol:
584594
return KeywordSpec("Protocol")
585595
case .protocol:
586-
return KeywordSpec("protocol", isLexerClassified: true, requiresTrailingSpace: true)
596+
return KeywordSpec("protocol", isLexerClassified: true)
587597
case .public:
588-
return KeywordSpec("public", isLexerClassified: true, requiresTrailingSpace: true)
598+
return KeywordSpec("public", isLexerClassified: true)
589599
case .reasync:
590600
return KeywordSpec("reasync")
591601
case .renamed:
592602
return KeywordSpec("renamed")
593603
case .repeat:
594-
return KeywordSpec("repeat", isLexerClassified: true, requiresTrailingSpace: true)
604+
return KeywordSpec("repeat", isLexerClassified: true)
595605
case .required:
596606
return KeywordSpec("required")
597607
case .rethrows:
598-
return KeywordSpec("rethrows", isLexerClassified: true, requiresTrailingSpace: true)
608+
return KeywordSpec("rethrows", isLexerClassified: true)
599609
case .return:
600-
return KeywordSpec("return", isLexerClassified: true, requiresTrailingSpace: true)
610+
return KeywordSpec("return", isLexerClassified: true)
601611
case .reverse:
602612
return KeywordSpec("reverse")
603613
case .right:
@@ -621,33 +631,33 @@ public enum Keyword: CaseIterable {
621631
case .spiModule:
622632
return KeywordSpec("spiModule")
623633
case .static:
624-
return KeywordSpec("static", isLexerClassified: true, requiresTrailingSpace: true)
634+
return KeywordSpec("static", isLexerClassified: true)
625635
case .struct:
626-
return KeywordSpec("struct", isLexerClassified: true, requiresTrailingSpace: true)
636+
return KeywordSpec("struct", isLexerClassified: true)
627637
case .subscript:
628-
return KeywordSpec("subscript", isLexerClassified: true, requiresTrailingSpace: true)
638+
return KeywordSpec("subscript", isLexerClassified: true)
629639
case .super:
630640
return KeywordSpec("super", isLexerClassified: true)
631641
case .swift:
632642
return KeywordSpec("swift")
633643
case .switch:
634-
return KeywordSpec("switch", isLexerClassified: true, requiresTrailingSpace: true)
644+
return KeywordSpec("switch", isLexerClassified: true)
635645
case .target:
636646
return KeywordSpec("target")
637647
case .throw:
638-
return KeywordSpec("throw", isLexerClassified: true, requiresTrailingSpace: true)
648+
return KeywordSpec("throw", isLexerClassified: true)
639649
case .throws:
640-
return KeywordSpec("throws", isLexerClassified: true, requiresTrailingSpace: true)
650+
return KeywordSpec("throws", isLexerClassified: true)
641651
case .transpose:
642652
return KeywordSpec("transpose")
643653
case .true:
644654
return KeywordSpec("true", isLexerClassified: true)
645655
case .try:
646-
return KeywordSpec("try", isLexerClassified: true, requiresTrailingSpace: true)
656+
return KeywordSpec("try", isLexerClassified: true)
647657
case .Type:
648658
return KeywordSpec("Type")
649659
case .typealias:
650-
return KeywordSpec("typealias", isLexerClassified: true, requiresTrailingSpace: true)
660+
return KeywordSpec("typealias", isLexerClassified: true)
651661
case .unavailable:
652662
return KeywordSpec("unavailable")
653663
case .unchecked:
@@ -661,15 +671,15 @@ public enum Keyword: CaseIterable {
661671
case .unsafeMutableAddress:
662672
return KeywordSpec("unsafeMutableAddress")
663673
case .var:
664-
return KeywordSpec("var", isLexerClassified: true, requiresTrailingSpace: true)
674+
return KeywordSpec("var", isLexerClassified: true)
665675
case .visibility:
666676
return KeywordSpec("visibility")
667677
case .weak:
668678
return KeywordSpec("weak")
669679
case .where:
670-
return KeywordSpec("where", isLexerClassified: true, requiresLeadingSpace: true, requiresTrailingSpace: true)
680+
return KeywordSpec("where", isLexerClassified: true)
671681
case .while:
672-
return KeywordSpec("while", isLexerClassified: true, requiresTrailingSpace: true)
682+
return KeywordSpec("while", isLexerClassified: true)
673683
case .willSet:
674684
return KeywordSpec("willSet")
675685
case .witness_method:

CodeGeneration/Sources/SyntaxSupport/String+Extensions.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ public extension StringProtocol {
1515
var withFirstCharacterUppercased: String { prefix(1).uppercased() + dropFirst() }
1616
var backtickedIfNeeded: String {
1717
if Keyword.allCases.map(\.spec).contains(where: {
18-
$0.name == self && $0.isLexerClassified
18+
$0.name == self && ($0.isLexerClassified || $0.name == "Type" || $0.name == "Protocol")
1919
}) {
2020
return "`\(self)`"
2121
} else {

CodeGeneration/Sources/SyntaxSupport/Utils.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ public extension Collection {
6969
public extension TokenSyntax {
7070
var backtickedIfNeeded: TokenSyntax {
7171
if Keyword.allCases.map(\.spec).contains(where: {
72-
$0.name == self.description && $0.isLexerClassified
72+
$0.name == self.description && ($0.isLexerClassified || $0.name == "Type" || $0.name == "Protocol")
7373
}) {
7474
return "`\(self)`"
7575
} else {

CodeGeneration/Sources/Utils/SyntaxBuildableChild.swift

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -77,11 +77,7 @@ public extension Child {
7777
if case .token(let choices, _, _) = kind,
7878
case .keyword(let keyword) = choices.only
7979
{
80-
var name = keyword.spec.name
81-
if keyword == .`init` {
82-
name = "`init`"
83-
}
84-
return ExprSyntax(".\(token.varOrCaseName)(.\(raw: name))")
80+
return ExprSyntax(".\(token.varOrCaseName)(.\(keyword.spec.varOrCaseName))")
8581
}
8682
return nil
8783
}

CodeGeneration/Sources/generate-swiftsyntax/templates/swiftparser/IsLexerClassifiedFile.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ let isLexerClassifiedFile = SourceFileSyntax(leadingTrivia: copyrightHeader) {
3333
try! SwitchExprSyntax("switch self") {
3434
for keyword in Keyword.allCases {
3535
if keyword.spec.isLexerClassified {
36-
SwitchCaseSyntax("case .\(raw: keyword.spec.escapedName): return true")
36+
SwitchCaseSyntax("case .\(keyword.spec.varOrCaseName): return true")
3737
}
3838
}
3939
SwitchCaseSyntax("default: return false")

0 commit comments

Comments
 (0)