Skip to content

Commit 5422596

Browse files
committed
[ASTGen] Add syntax tree based Fingerprint
1 parent 0dfca46 commit 5422596

File tree

3 files changed

+387
-0
lines changed

3 files changed

+387
-0
lines changed

lib/ASTGen/Sources/ASTGen/CMakeLists.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,14 @@ add_pure_swift_host_library(swiftASTGen STATIC CXX_INTEROP
88
Diagnostics.swift
99
DiagnosticsBridge.swift
1010
Exprs.swift
11+
Fingerprint.swift
1112
Generics.swift
1213
Literals.swift
1314
ParameterClause.swift
1415
Patterns.swift
1516
Regex.swift
1617
SourceFile.swift
18+
StableHasher.swift
1719
Stmts.swift
1820
TypeAttrs.swift
1921
Types.swift
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
//===--- Fingerprint.swift -------------------------------------------------===//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 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)
14+
import SwiftSyntax
15+
import SwiftIfConfig
16+
17+
struct Fingerprint: Equatable {
18+
typealias Core = (UInt64, UInt64)
19+
var core: Core
20+
init(core: Core) {
21+
self.core = core
22+
}
23+
24+
static func == (lhs: Self, rhs: Self) -> Bool {
25+
return lhs.core == rhs.core
26+
}
27+
}
28+
29+
extension StableHasher {
30+
@inline(__always)
31+
mutating func combine(text: SyntaxText) {
32+
let buffer = UnsafeRawBufferPointer(
33+
start: UnsafeRawPointer(text.baseAddress),
34+
count: text.count
35+
)
36+
self.combine(UInt(truncatingIfNeeded: buffer.count))
37+
self.combine(bytes: buffer)
38+
}
39+
}
40+
41+
final class FingerprintVisitor: SyntaxVisitor {
42+
let configuredRegions: ConfiguredRegions
43+
var hasher: StableHasher
44+
45+
init(configuredRegions: ConfiguredRegions) {
46+
self.configuredRegions = configuredRegions
47+
self.hasher = StableHasher()
48+
super.init(viewMode: .sourceAccurate)
49+
}
50+
51+
func finalize() -> Fingerprint {
52+
Fingerprint(core: hasher.finalize())
53+
}
54+
55+
override func visit(_ token: TokenSyntax) -> SyntaxVisitorContinueKind {
56+
// Collect all token texts.
57+
hasher.combine(text: token.rawText)
58+
return .skipChildren
59+
}
60+
61+
override func visit(_ node: MemberBlockSyntax) -> SyntaxVisitorContinueKind {
62+
// Skip nominal decl / extension member blocks.
63+
return .skipChildren
64+
}
65+
66+
override func visit(_ node: CodeBlockSyntax) -> SyntaxVisitorContinueKind {
67+
// Skip function bodies.
68+
// FIXME: Don't skip closure bodies or other control flow statement blocks.
69+
// E.g. 'var val = { if condition { "foo" } else { "bar" } }()'
70+
return .skipChildren
71+
}
72+
73+
override func visit(_ node: IfConfigDeclSyntax) -> SyntaxVisitorContinueKind {
74+
if let active = configuredRegions.activeClause(for: node) {
75+
self.walk(active)
76+
}
77+
return .skipChildren
78+
}
79+
}

0 commit comments

Comments
 (0)