Skip to content

Commit 6e36f09

Browse files
caldanicklockwood
authored andcommitted
Make all tests pass
1 parent 07661d2 commit 6e36f09

9 files changed

+78
-50
lines changed

Sources/DeclarationV1.swift

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -987,28 +987,35 @@ extension Declaration {
987987
// DeclarationV1 handles disabling rules by setting the keyword to an empty string.
988988
guard !keyword.isEmpty else { return nil }
989989

990+
let declaration: DeclarationV2
990991
switch self {
991992
case let .type(kind, _, body, _, originalRange):
992-
return TypeDeclaration(
993+
declaration = TypeDeclaration(
993994
keyword: kind,
994995
range: originalRange,
995996
body: body.compactMap { $0.makeDeclarationV2(formatter: formatter) },
996997
formatter: formatter
997998
)
998999

9991000
case let .declaration(kind, _, originalRange):
1000-
return SimpleDeclaration(
1001+
declaration = SimpleDeclaration(
10011002
keyword: kind,
10021003
range: originalRange,
10031004
formatter: formatter
10041005
)
10051006

10061007
case let .conditionalCompilation(_, body, _, originalRange):
1007-
return ConditionalCompilationDeclaration(
1008+
declaration = ConditionalCompilationDeclaration(
10081009
range: originalRange,
10091010
body: body.compactMap { $0.makeDeclarationV2(formatter: formatter) },
10101011
formatter: formatter
10111012
)
10121013
}
1014+
1015+
guard declaration.isValid else {
1016+
return nil
1017+
}
1018+
1019+
return declaration
10131020
}
10141021
}

Sources/DeclarationV2.swift

Lines changed: 48 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
/// automatically kept up-to-date as tokens are added, removed, or modified
1717
/// in the associated formatter.
1818
///
19-
protocol DeclarationV2: AnyObject {
19+
protocol DeclarationV2: AnyObject, CustomDebugStringConvertible {
2020
/// The keyword of this declaration (`class`, `struct`, `func`, `let`, `var`, etc.)
2121
var keyword: String { get }
2222

@@ -50,9 +50,24 @@ extension DeclarationV2 {
5050
formatter.tokens[range]
5151
}
5252

53+
/// Whether or not this declaration reference is still valid
54+
var isValid: Bool {
55+
_keywordIndex != nil
56+
}
57+
5358
/// The index of this declaration's keyword in the associated formatter.
5459
/// Assumes that the declaration has not been invalidated, and still contains its `keyword`.
5560
var keywordIndex: Int {
61+
guard let keywordIndex = _keywordIndex else {
62+
assertionFailure("Declaration \(self) is no longer valid.")
63+
return range.lowerBound
64+
}
65+
66+
return keywordIndex
67+
}
68+
69+
/// The index of this declaration's keyword token, if the declaration is still valid.
70+
var _keywordIndex: Int? {
5671
let expectedKeywordToken: Token
5772
switch kind {
5873
case .declaration, .type:
@@ -61,12 +76,7 @@ extension DeclarationV2 {
6176
expectedKeywordToken = .startOfScope("#if")
6277
}
6378

64-
guard let keywordIndex = formatter.index(of: expectedKeywordToken, after: range.lowerBound - 1) else {
65-
assertionFailure("Declaration \(self) is no longer valid.")
66-
return range.lowerBound
67-
}
68-
69-
return keywordIndex
79+
return formatter.index(of: expectedKeywordToken, after: range.lowerBound - 1)
7080
}
7181

7282
/// The name of this declaration, which is always the identifier or type following the primary keyword.
@@ -139,7 +149,7 @@ extension DeclarationV2 {
139149
}
140150

141151
/// Full information about this `let` or `var` property declaration.
142-
var asPropertyDeclaration: Formatter.PropertyDeclaration? {
152+
func parsePropertyDeclaration() -> Formatter.PropertyDeclaration? {
143153
guard keyword == "let" || keyword == "var" else { return nil }
144154
return formatter.parsePropertyDeclaration(atIntroducerIndex: keywordIndex)
145155
}
@@ -155,6 +165,19 @@ extension DeclarationV2 {
155165
return parent.parentDeclarations + [parent]
156166
}
157167

168+
/// The `CustomDebugStringConvertible` representation of this declaration
169+
var debugDescription: String {
170+
guard isValid else {
171+
return "Invalid \(keyword) declaration reference at \(range)"
172+
}
173+
174+
let indentation = formatter.currentIndentForLine(at: range.lowerBound)
175+
return """
176+
\(indentation)/* \(keyword) declaration at \(range) */
177+
\(tokens.string)
178+
"""
179+
}
180+
158181
/// Removes this declaration from the source file.
159182
/// After this point, this declaration reference is no longer valid.
160183
func remove() {
@@ -173,10 +196,14 @@ final class SimpleDeclaration: DeclarationV2 {
173196
formatter.registerDeclaration(self)
174197
}
175198

199+
deinit {
200+
formatter.unregisterDeclaration(self)
201+
}
202+
176203
var keyword: String
177204
var range: ClosedRange<Int>
178-
weak var parent: DeclarationV2?
179205
let formatter: Formatter
206+
weak var parent: DeclarationV2?
180207

181208
var kind: DeclarationKind {
182209
.declaration(self)
@@ -198,16 +225,22 @@ final class TypeDeclaration: DeclarationV2 {
198225
}
199226
}
200227

228+
deinit {
229+
formatter.unregisterDeclaration(self)
230+
}
231+
201232
var keyword: String
202233
var range: ClosedRange<Int>
203234
var body: [DeclarationV2]
204-
weak var parent: DeclarationV2?
205235
let formatter: Formatter
236+
weak var parent: DeclarationV2?
206237

207238
var kind: DeclarationKind {
208239
.type(self)
209240
}
241+
}
210242

243+
extension TypeDeclaration {
211244
/// The index of the open brace (`{`) before the type's body.
212245
/// Assumes that the declaration has not been invalidated.
213246
var openBraceIndex: Int {
@@ -238,11 +271,15 @@ final class ConditionalCompilationDeclaration: DeclarationV2 {
238271
}
239272
}
240273

274+
deinit {
275+
formatter.unregisterDeclaration(self)
276+
}
277+
241278
let keyword = "#if"
242279
var range: ClosedRange<Int>
243280
var body: [DeclarationV2]
244-
weak var parent: DeclarationV2?
245281
let formatter: Formatter
282+
weak var parent: DeclarationV2?
246283

247284
var kind: DeclarationKind {
248285
.conditionalCompilation(self)
@@ -286,18 +323,3 @@ extension DeclarationV2 {
286323
formatter.removeDeclarationVisibility(visibilityKeyword, declarationKeywordIndex: keywordIndex)
287324
}
288325
}
289-
290-
/// We want to avoid including a Hashable requirement on DeclarationV2,
291-
/// so instead you can use this container type if you need a Hashable declaration.
292-
/// Uses reference identity of the `DeclarationV2` class value.
293-
struct HashableDeclaration: Hashable {
294-
let declaration: DeclarationV2
295-
296-
static func == (lhs: HashableDeclaration, rhs: HashableDeclaration) -> Bool {
297-
lhs.declaration === rhs.declaration
298-
}
299-
300-
func hash(into hasher: inout Hasher) {
301-
hasher.combine(ObjectIdentifier(declaration))
302-
}
303-
}

Sources/Formatter.swift

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -830,10 +830,12 @@ extension Array where Element == WeakDeclarationReference {
830830
// so doesn't invalidate the indices.
831831
}
832832

833-
// If you get a crash here where `endIndex` is less than `startIndex`,
834-
// it means that the declaration is no longer valid, usually because it's
835-
// been removed, but the declaration is unexpectedly still subscribed
836-
// to updates from this formatter.
833+
// Defend against a potential crash here if `endIndex` is less than `startIndex`.
834+
guard startIndex <= endIndex else {
835+
declaration.range = startIndex ... startIndex
836+
continue
837+
}
838+
837839
declaration.range = startIndex ... endIndex
838840
}
839841
}

Sources/ParsingHelpers.swift

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1705,7 +1705,8 @@ extension Formatter {
17051705
let rangeInsideBody: ClosedRange<Int>
17061706
if startOfBodyIndex + 1 != endOfScope {
17071707
if let firstTokenInBody = index(of: .nonSpaceOrCommentOrLinebreak, after: startOfBodyIndex + 1),
1708-
let lastTokenInBody = index(of: .nonSpaceOrCommentOrLinebreak, before: endOfScope)
1708+
let lastTokenInBody = index(of: .nonSpaceOrCommentOrLinebreak, before: endOfScope),
1709+
firstTokenInBody <= lastTokenInBody
17091710
{
17101711
rangeInsideBody = firstTokenInBody ... lastTokenInBody
17111712
} else {
@@ -2365,11 +2366,9 @@ extension Formatter {
23652366

23662367
/// The explicit `Visibility` of the `Declaration` with its keyword at the given index
23672368
func declarationVisibility(keywordIndex: Int) -> Visibility? {
2368-
let startOfModifiers = startOfModifiers(at: keywordIndex, includingAttributes: false)
2369-
23702369
// Search for a visibility keyword in the tokens before the primary keyword,
23712370
// making sure we exclude groups like private(set).
2372-
var searchIndex = startOfModifiers
2371+
var searchIndex = startOfModifiers(at: keywordIndex, includingAttributes: false)
23732372
while searchIndex < keywordIndex {
23742373
if let visibility = Visibility(rawValue: tokens[searchIndex].string),
23752374
next(.nonSpaceOrComment, after: searchIndex) != .startOfScope("(")
@@ -2400,11 +2399,9 @@ extension Formatter {
24002399
}
24012400
}
24022401

2403-
let startOfModifiers = startOfModifiers(at: declarationKeywordIndex, includingAttributes: false)
2404-
24052402
insert(
24062403
[.keyword(visibilityKeyword.rawValue), .space(" ")],
2407-
at: startOfModifiers
2404+
at: startOfModifiers(at: declarationKeywordIndex, includingAttributes: false)
24082405
)
24092406
}
24102407

Sources/Rules/EnvironmentEntry.swift

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@ extension Formatter {
6868
var environmentKeys = [String: EnvironmentKey]()
6969

7070
for declaration in declarations {
71-
guard let typeDeclaration = declaration as? TypeDeclaration,
71+
guard let typeDeclaration = declaration.asTypeDeclaration,
7272
typeDeclaration.keyword == "struct" || typeDeclaration.keyword == "enum",
7373
typeDeclaration.conformances.contains(where: { $0.conformance == "EnvironmentKey" }),
7474
let keyName = typeDeclaration.name,
@@ -89,7 +89,7 @@ extension Formatter {
8989
}
9090

9191
func findEnvironmentKeyDefaultValue(_ defaultValueDeclaration: DeclarationV2) -> [Token]? {
92-
guard let property = defaultValueDeclaration.asPropertyDeclaration else { return nil }
92+
guard let property = defaultValueDeclaration.parsePropertyDeclaration() else { return nil }
9393

9494
if let valueRange = property.value?.expressionRange {
9595
return Array(tokens[valueRange])
@@ -125,7 +125,7 @@ extension Formatter {
125125

126126
// Ensure the property has a setter and a getter, this can avoid edge cases where
127127
// a property references a `EnvironmentKey` and consumes it to perform some computation.
128-
guard let bodyRange = propertyDeclaration.asPropertyDeclaration?.body?.range,
128+
guard let bodyRange = propertyDeclaration.parsePropertyDeclaration()?.body?.range,
129129
let indexOfSetter = index(of: .identifier("set"), in: Range(bodyRange)),
130130
isAccessorKeyword(at: indexOfSetter)
131131
else { return nil }
@@ -141,7 +141,7 @@ extension Formatter {
141141

142142
func modifyEnvironmentValuesProperties(_ environmentValuesPropertiesDeclarations: [EnvironmentValueProperty]) {
143143
for envProperty in environmentValuesPropertiesDeclarations {
144-
guard let propertyDeclaration = envProperty.declaration.asPropertyDeclaration,
144+
guard let propertyDeclaration = envProperty.declaration.parsePropertyDeclaration(),
145145
let bodyScopeRange = propertyDeclaration.body?.scopeRange
146146
else { continue }
147147

Sources/Rules/ExtensionAccessControl.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ public extension FormatRule {
1717

1818
let declarations = formatter.parseDeclarationsV2()
1919
declarations.forEachRecursiveDeclaration { declaration in
20-
guard let extensionDeclaration = declaration as? TypeDeclaration,
20+
guard let extensionDeclaration = declaration.asTypeDeclaration,
2121
extensionDeclaration.keyword == "extension"
2222
else { return }
2323

@@ -58,7 +58,7 @@ public extension FormatRule {
5858
if memberVisibility > extensionVisibility ?? .internal {
5959
// Check type being extended does not have lower visibility
6060
for extendedType in declarations where extendedType.name == extensionDeclaration.name {
61-
guard let type = extendedType as? TypeDeclaration else { continue }
61+
guard let type = extendedType.asTypeDeclaration else { continue }
6262

6363
if extendedType.keyword != "extension",
6464
extendedType.visibility() ?? .internal < memberVisibility

Tests/ParsingHelpersTests.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2439,7 +2439,7 @@ class ParsingHelpersTests: XCTestCase {
24392439
XCTAssertFalse(isStoredProperty("""
24402440
var foo: String {
24412441
get { "foo" }
2442-
set { print(newValue} }
2442+
set { print(newValue) }
24432443
}
24442444
"""))
24452445
}

Tests/Rules/BracesTests.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -256,7 +256,7 @@ class BracesTests: XCTestCase {
256256
// foo
257257
}
258258
"""
259-
testFormatting(for: input, output, rule: .braces)
259+
testFormatting(for: input, output, rule: .braces, exclude: [.emptyExtensions])
260260
}
261261

262262
func testBracesForOptionalInit() {
@@ -505,7 +505,7 @@ class BracesTests: XCTestCase {
505505
}
506506
"""
507507
let options = FormatOptions(allmanBraces: true)
508-
testFormatting(for: input, output, rule: .braces, options: options)
508+
testFormatting(for: input, output, rule: .braces, options: options, exclude: [.emptyExtensions])
509509
}
510510

511511
func testEmptyAllmanIfElseBraces() {

Tests/Rules/ExtensionAccessControlTests.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -458,6 +458,6 @@ class ExtensionAccessControlTests: XCTestCase {
458458
}
459459
"""
460460

461-
testFormatting(for: input, rule: .extensionAccessControl)
461+
testFormatting(for: input, rule: .extensionAccessControl, exclude: [.emptyExtensions])
462462
}
463463
}

0 commit comments

Comments
 (0)