@@ -17,7 +17,10 @@ public struct Identifier: Equatable, Hashable, Sendable {
1717 String ( syntaxText: raw. name)
1818 }
1919
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+ }
2124
2225 @_spi ( RawSyntax)
2326 public let raw : RawIdentifier
@@ -26,61 +29,86 @@ public struct Identifier: Equatable, Hashable, Sendable {
2629 public init ? ( _ token: TokenSyntax ) {
2730 switch token. tokenKind {
2831 case . identifier, . keyword( . self ) , . keyword( . Self) :
29- self . raw = RawIdentifier ( token. tokenView)
32+ self . raw = RawIdentifier ( token. tokenView. rawText )
3033 self . arena = token. raw. arenaReference
31-
32- self . dollarIdentifierStr = nil
3334 case . dollarIdentifier( let dollarIdentifierStr) :
34- self . raw = RawIdentifier ( token. tokenView)
3535 self . arena = token. raw. arenaReference
3636
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+ }
3847 default :
3948 return nil
4049 }
4150 }
4251
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+ )
4861
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
5464 }
5565
5666 public static func == ( lhs: Self , rhs: Self ) -> Bool {
5767 lhs. name == rhs. name
5868 }
5969
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
6279
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 " )
6493 }
6594}
6695
6796@_spi ( RawSyntax)
6897public struct RawIdentifier : Equatable , Hashable , Sendable {
98+ fileprivate let original : SyntaxText
6999 public let name : SyntaxText
70100
71101 @_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
80108 }
81- }
82109
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] )
85113 }
86114}
0 commit comments