Skip to content

Commit 74c759f

Browse files
authored
Merge pull request #2666 from ahoppen/6.0/identifier
[6.0] Add Identifier wrapper that strips backticks from token text
2 parents d9c1feb + 53952a4 commit 74c759f

File tree

5 files changed

+118
-0
lines changed

5 files changed

+118
-0
lines changed

Release Notes/600.md

+4
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,10 @@
9595
- Description: A version of the `SwiftSyntaxMacrosTestSupport` module that doesn't depend on `Foundation` or `XCTest` and can thus be used to write macro tests using `swift-testing`. Since swift-syntax can't depend on swift-testing (which would incur a circular dependency since swift-testing depends on swift-syntax), users need to manually specify a failure handler like the following, that fails the swift-testing test: `Issue.record("\($0.message)", fileID: $0.location.fileID, filePath: $0.location.filePath, line: $0.location.line, column: $0.location.column)`
9696
- Pull request: https://github.com/apple/swift-syntax/pull/2647
9797

98+
- `TokenSyntax.identifier`
99+
- Description: Adds an `identifier` property to `TokenSyntax` which returns a canonicalized representation of an identifier that strips away backticks.
100+
- Pull request: https://github.com/apple/swift-syntax/pull/2576
101+
98102
## API Behavior Changes
99103

100104
## Deprecations

Sources/SwiftSyntax/CMakeLists.txt

+1
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ add_swift_syntax_library(SwiftSyntax
1515
CommonAncestor.swift
1616
Convenience.swift
1717
CustomTraits.swift
18+
Identifier.swift
1819
MemoryLayout.swift
1920
MissingNodeInitializers.swift
2021
SourceEdit.swift

Sources/SwiftSyntax/Identifier.swift

+50
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
//===----------------------------------------------------------------------===//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 2014 - 2024 Apple Inc. and the Swift project authors
6+
// Licensed under Apache License v2.0 with Runtime Library Exception
7+
//
8+
// See https://swift.org/LICENSE.txt for license information
9+
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
10+
//
11+
//===----------------------------------------------------------------------===//
12+
13+
/// A canonicalized representation of an identifier that strips away backticks.
14+
public struct Identifier: Equatable, Hashable, Sendable {
15+
/// The sanitized `text` of a token.
16+
public var name: String {
17+
String(syntaxText: raw.name)
18+
}
19+
20+
@_spi(RawSyntax)
21+
public let raw: RawIdentifier
22+
23+
private let arena: SyntaxArenaRef
24+
25+
public init?(_ token: TokenSyntax) {
26+
guard case .identifier = token.tokenKind else {
27+
return nil
28+
}
29+
30+
self.raw = RawIdentifier(token.tokenView)
31+
self.arena = token.tokenView.raw.arenaReference
32+
}
33+
}
34+
35+
@_spi(RawSyntax)
36+
public struct RawIdentifier: Equatable, Hashable, Sendable {
37+
public let name: SyntaxText
38+
39+
@_spi(RawSyntax)
40+
fileprivate init(_ raw: RawSyntaxTokenView) {
41+
let backtick = SyntaxText("`")
42+
if raw.rawText.count > 2 && raw.rawText.hasPrefix(backtick) && raw.rawText.hasSuffix(backtick) {
43+
let startIndex = raw.rawText.index(after: raw.rawText.startIndex)
44+
let endIndex = raw.rawText.index(before: raw.rawText.endIndex)
45+
self.name = SyntaxText(rebasing: raw.rawText[startIndex..<endIndex])
46+
} else {
47+
self.name = raw.rawText
48+
}
49+
}
50+
}

Sources/SwiftSyntax/TokenSyntax.swift

+10
Original file line numberDiff line numberDiff line change
@@ -153,6 +153,16 @@ public struct TokenSyntax: SyntaxProtocol, SyntaxHashable {
153153
}
154154
}
155155

156+
/// An identifier created from `self`.
157+
public var identifier: Identifier? {
158+
switch self.tokenKind {
159+
case .identifier, .dollarIdentifier:
160+
return Identifier(self)
161+
default:
162+
return nil
163+
}
164+
}
165+
156166
/// A token by itself has no structure, so we represent its structure by an
157167
/// empty layout node.
158168
///
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
//===----------------------------------------------------------------------===//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 2014 - 2024 Apple Inc. and the Swift project authors
6+
// Licensed under Apache License v2.0 with Runtime Library Exception
7+
//
8+
// See https://swift.org/LICENSE.txt for license information
9+
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
10+
//
11+
//===----------------------------------------------------------------------===//
12+
13+
@_spi(RawSyntax) import SwiftSyntax
14+
import XCTest
15+
16+
class IdentifierTests: XCTestCase {
17+
public func testIdentifierInit() {
18+
let someToken = TokenSyntax(stringLiteral: "someToken")
19+
XCTAssertNotNil(Identifier(someToken))
20+
21+
let nonIdentifierToken = DeclSyntax("let a = 1").firstToken(viewMode: .all)!
22+
XCTAssertNil(Identifier(nonIdentifierToken))
23+
}
24+
25+
public func testName() {
26+
let basicToken = TokenSyntax(stringLiteral: "basicToken")
27+
XCTAssertEqual(Identifier(basicToken)?.name, "basicToken")
28+
29+
let backtickedToken = TokenSyntax(stringLiteral: "`backtickedToken`")
30+
XCTAssertEqual(Identifier(backtickedToken)?.name, "backtickedToken")
31+
32+
let multiBacktickedToken = TokenSyntax(stringLiteral: "```multiBacktickedToken```")
33+
XCTAssertEqual(Identifier(multiBacktickedToken)?.name, "``multiBacktickedToken``")
34+
35+
let unicodeNormalizedToken = TokenSyntax(stringLiteral: "\u{e0}") // "a`"
36+
XCTAssertEqual(Identifier(unicodeNormalizedToken)?.name, "\u{61}\u{300}") // "à"
37+
}
38+
39+
public func testIdentifier() {
40+
let token = TokenSyntax(stringLiteral: "sometoken")
41+
withExtendedLifetime(token) { token in
42+
XCTAssertEqual(token.identifier?.raw.name, SyntaxText("sometoken"))
43+
}
44+
}
45+
46+
public func testTokenSyntaxIdentifier() throws {
47+
let tokenSyntax = TokenSyntax(stringLiteral: "sometoken")
48+
XCTAssertEqual(tokenSyntax.identifier, Identifier(tokenSyntax))
49+
50+
let nonIdentifierToken = try XCTUnwrap(DeclSyntax("let a = 1").firstToken(viewMode: .all))
51+
XCTAssertNil(nonIdentifierToken.identifier)
52+
}
53+
}

0 commit comments

Comments
 (0)