@@ -17,7 +17,10 @@ public struct Identifier: Equatable, Hashable, Sendable {
17
17
String ( syntaxText: raw. name)
18
18
}
19
19
20
- public let dollarIdentifierStr : String ?
20
+ /// `true` if the identifier is a dollar identifier.
21
+ public var isDollarIdentifier : Bool {
22
+ raw. original. hasPrefix ( SyntaxText ( " $ " ) ) && Int ( String ( syntaxText: raw. original) . dropFirst ( ) ) != nil
23
+ }
21
24
22
25
@_spi ( RawSyntax)
23
26
public let raw : RawIdentifier
@@ -26,61 +29,86 @@ public struct Identifier: Equatable, Hashable, Sendable {
26
29
public init ? ( _ token: TokenSyntax ) {
27
30
switch token. tokenKind {
28
31
case . identifier, . keyword( . self ) , . keyword( . Self) :
29
- self . raw = RawIdentifier ( token. tokenView)
32
+ self . raw = RawIdentifier ( token. tokenView. rawText )
30
33
self . arena = token. raw. arenaReference
31
-
32
- self . dollarIdentifierStr = nil
33
34
case . dollarIdentifier( let dollarIdentifierStr) :
34
- self . raw = RawIdentifier ( token. tokenView)
35
35
self . arena = token. raw. arenaReference
36
36
37
- self . dollarIdentifierStr = dollarIdentifierStr
37
+ if Self . isPaddedDollarIdentifier ( dollarIdentfierStr: dollarIdentifierStr) ,
38
+ let newDollarIdentifierNumber = Int ( dollarIdentifierStr. dropFirst ( ) )
39
+ {
40
+ let newDollarIdentifierStr = " $ \( newDollarIdentifierNumber) "
41
+ let sanitizedDollarIdentifierSyntaxText = token. raw. arenaReference. intern ( newDollarIdentifierStr)
42
+
43
+ self . raw = RawIdentifier ( sanitizedDollarIdentifierSyntaxText)
44
+ } else {
45
+ self . raw = RawIdentifier ( token. tokenView. rawText)
46
+ }
38
47
default :
39
48
return nil
40
49
}
41
50
}
42
51
43
- public init ( _ staticString: StaticString ) {
44
- self . raw = RawIdentifier ( staticString)
45
- self . arena = nil
46
-
47
- let name = String ( syntaxText: raw. name)
52
+ /// Create a new `Identifier` from given `canonicalName`.
53
+ ///
54
+ /// - Precondition: `canonicalName` is a canonical identifier i.e. doesn't
55
+ /// use backticks and is not a dollar identifier with leading zeros.
56
+ public init ( canonicalName: StaticString ) {
57
+ precondition (
58
+ Self . isCanonicalRepresentation ( canonicalName) ,
59
+ " \( canonicalName) is not a canonical identifier. "
60
+ )
48
61
49
- if name. first == " $ " && Int ( name. dropFirst ( ) ) != nil {
50
- self . dollarIdentifierStr = name
51
- } else {
52
- self . dollarIdentifierStr = nil
53
- }
62
+ self . raw = RawIdentifier ( SyntaxText ( canonicalName) )
63
+ self . arena = nil
54
64
}
55
65
56
66
public static func == ( lhs: Self , rhs: Self ) -> Bool {
57
67
lhs. name == rhs. name
58
68
}
59
69
60
- private static func getDollarIdentifierNumber( str: String ) -> Int ? {
61
- guard str. first == " $ " else { return nil }
70
+ /// Returns `true` if `staticString` is a canonical identifier i.e. doesn't
71
+ /// use backticks and is not a dollar identifier with leading zeros.
72
+ private static func isCanonicalRepresentation( _ staticString: StaticString ) -> Bool {
73
+ let text = SyntaxText ( staticString)
74
+
75
+ guard !Self. hasBackticks ( text) else { return false }
76
+
77
+ let str = String ( syntaxText: text)
78
+ let isDollarIdentifier = str. first == " $ " && Int ( str. dropFirst ( ) ) != nil
62
79
63
- return Int ( str. dropFirst ( ) )
80
+ return !( isDollarIdentifier && Self . isPaddedDollarIdentifier ( dollarIdentfierStr: str) )
81
+ }
82
+
83
+ /// Returns `true` if `rawText` doesn't use backticks.
84
+ fileprivate static func hasBackticks( _ rawText: SyntaxText ) -> Bool {
85
+ let backtick = SyntaxText ( " ` " )
86
+ return rawText. count > 2 && rawText. hasPrefix ( backtick) && rawText. hasSuffix ( backtick)
87
+ }
88
+
89
+ /// Returns `true` if `dollarIdentfierStr` is not a
90
+ /// dollar identifier with leading zeros.
91
+ fileprivate static func isPaddedDollarIdentifier( dollarIdentfierStr: String ) -> Bool {
92
+ dollarIdentfierStr. count > 2 && dollarIdentfierStr. hasPrefix ( " $0 " )
64
93
}
65
94
}
66
95
67
96
@_spi ( RawSyntax)
68
97
public struct RawIdentifier : Equatable , Hashable , Sendable {
98
+ fileprivate let original : SyntaxText
69
99
public let name : SyntaxText
70
100
71
101
@_spi ( RawSyntax)
72
- fileprivate init ( _ raw: RawSyntaxTokenView ) {
73
- let backtick = SyntaxText ( " ` " )
74
- if raw. rawText. count > 2 && raw. rawText. hasPrefix ( backtick) && raw. rawText. hasSuffix ( backtick) {
75
- let startIndex = raw. rawText. index ( after: raw. rawText. startIndex)
76
- let endIndex = raw. rawText. index ( before: raw. rawText. endIndex)
77
- self . name = SyntaxText ( rebasing: raw. rawText [ startIndex..< endIndex] )
78
- } else {
79
- self . name = raw. rawText
102
+ fileprivate init ( _ rawText: SyntaxText ) {
103
+ self . original = rawText
104
+
105
+ guard Identifier . hasBackticks ( rawText) else {
106
+ self . name = rawText
107
+ return
80
108
}
81
- }
82
109
83
- fileprivate init ( _ staticString: StaticString ) {
84
- name = SyntaxText ( staticString)
110
+ let startIndex = rawText. index ( after: rawText. startIndex)
111
+ let endIndex = rawText. index ( before: rawText. endIndex)
112
+ self . name = SyntaxText ( rebasing: rawText [ startIndex..< endIndex] )
85
113
}
86
114
}
0 commit comments