Skip to content

Commit fc34c2e

Browse files
committed
[SwiftWarningControl] Specify control inputs to tree node in order
Specifically for global controls, the ordering is important because later controls override earlier controls. Similarly, this will result in the same semantic for syntactic controls, with the first attribute being able to be overriden by subsequent attributes. The control configuration logic as-is handles that correctly *except* for iterating over input controls which are supplied in an unordered dictionary. This refactors the API surface to accept an array of pairs '(groupIdentifier, groupControl)', instead of a dictionary.
1 parent d7cde2f commit fc34c2e

File tree

5 files changed

+47
-11
lines changed

5 files changed

+47
-11
lines changed

Sources/SwiftWarningControl/SyntaxProtocol+WarningControl.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ extension SyntaxProtocol {
2727
@_spi(ExperimentalLanguageFeatures)
2828
public func warningGroupControl(
2929
for diagnosticGroupIdentifier: DiagnosticGroupIdentifier,
30-
globalControls: [DiagnosticGroupIdentifier: WarningGroupControl] = [:],
30+
globalControls: [(DiagnosticGroupIdentifier, WarningGroupControl)] = [],
3131
groupInheritanceTree: DiagnosticGroupInheritanceTree? = nil
3232
) -> WarningGroupControl? {
3333
let warningControlRegions = root.warningGroupControlRegionTreeImpl(

Sources/SwiftWarningControl/WarningControlDeclSyntax.swift

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,8 @@ import SwiftSyntax
1515
extension WithAttributesSyntax {
1616
/// Compute a dictionary of all `@warn` diagnostic group behavior controls
1717
/// specified on this warning control declaration scope.
18-
var allWarningGroupControls: [DiagnosticGroupIdentifier: WarningGroupControl] {
19-
attributes.reduce(into: [DiagnosticGroupIdentifier: WarningGroupControl]()) { result, attr in
18+
var allWarningGroupControls: [(DiagnosticGroupIdentifier, WarningGroupControl)] {
19+
attributes.reduce(into: [(DiagnosticGroupIdentifier, WarningGroupControl)]()) { result, attr in
2020
// `@warn` attributes
2121
guard case .attribute(let attributeSyntax) = attr,
2222
attributeSyntax.attributeName.as(IdentifierTypeSyntax.self)?.name.text == "warn"
@@ -50,7 +50,7 @@ extension WithAttributesSyntax {
5050
else {
5151
return
5252
}
53-
result[DiagnosticGroupIdentifier(diagnosticGroupID)] = control
53+
result.append((DiagnosticGroupIdentifier(diagnosticGroupID), control))
5454
}
5555
}
5656
}

Sources/SwiftWarningControl/WarningControlRegionBuilder.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ import SwiftSyntax
1616
extension SyntaxProtocol {
1717
@_spi(ExperimentalLanguageFeatures)
1818
public func warningGroupControlRegionTree(
19-
globalControls: [DiagnosticGroupIdentifier: WarningGroupControl] = [:],
19+
globalControls: [(DiagnosticGroupIdentifier, WarningGroupControl)] = [],
2020
groupInheritanceTree: DiagnosticGroupInheritanceTree? = nil
2121
) -> WarningControlRegionTree {
2222
return warningGroupControlRegionTreeImpl(
@@ -30,7 +30,7 @@ extension SyntaxProtocol {
3030
/// a specific absolute position - meant to speed up tree generation for individual
3131
/// queries.
3232
func warningGroupControlRegionTreeImpl(
33-
globalControls: [DiagnosticGroupIdentifier: WarningGroupControl],
33+
globalControls: [(DiagnosticGroupIdentifier, WarningGroupControl)],
3434
groupInheritanceTree: DiagnosticGroupInheritanceTree?,
3535
containing position: AbsolutePosition? = nil
3636
) -> WarningControlRegionTree {

Sources/SwiftWarningControl/WarningControlRegions.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -123,7 +123,7 @@ public struct WarningControlRegionTree {
123123
/// Add a warning control region to the tree
124124
mutating func addWarningGroupControls(
125125
range: Range<AbsolutePosition>,
126-
controls: [DiagnosticGroupIdentifier: WarningGroupControl]
126+
controls: [(DiagnosticGroupIdentifier, WarningGroupControl)]
127127
) {
128128
guard !controls.isEmpty else { return }
129129
let newNode = WarningControlRegionNode(range: range)

Tests/SwiftWarningControlTest/WarningControlTests.swift

Lines changed: 40 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -272,7 +272,7 @@ public class WarningGroupControlTests: XCTestCase {
272272
1️⃣let x = 1
273273
}
274274
""",
275-
globalControls: ["GroupID": .warning],
275+
globalControls: [("GroupID", .warning)],
276276
diagnosticGroupID: "GroupID",
277277
states: [
278278
"1️⃣": .error
@@ -289,7 +289,7 @@ public class WarningGroupControlTests: XCTestCase {
289289
}
290290
}
291291
""",
292-
globalControls: ["GroupID": .error],
292+
globalControls: [("GroupID", .error)],
293293
diagnosticGroupID: "GroupID",
294294
states: [
295295
"1️⃣": .error,
@@ -306,7 +306,7 @@ public class WarningGroupControlTests: XCTestCase {
306306
1️⃣let x = 1
307307
}
308308
""",
309-
globalControls: ["GroupID": .warning],
309+
globalControls: [("GroupID", .warning)],
310310
diagnosticGroupID: "GroupID",
311311
states: [
312312
"1️⃣": .warning
@@ -351,13 +351,49 @@ public class WarningGroupControlTests: XCTestCase {
351351
)
352352
}
353353
}
354+
355+
func testOrderedGlobalControls() throws {
356+
// Parent group is ignored, followed by sub-group treated as warning
357+
try assertWarningGroupControl(
358+
"""
359+
func foo() {
360+
1️⃣let x = 1
361+
}
362+
""",
363+
globalControls: [("SuperGroupID", .ignored), ("GroupID", .warning)],
364+
groupInheritanceTree: DiagnosticGroupInheritanceTree(subGroups: [
365+
"SuperGroupID": ["GroupID"]
366+
]),
367+
diagnosticGroupID: "GroupID",
368+
states: [
369+
"1️⃣": .warning
370+
]
371+
)
372+
373+
// Parent group is treated as warning, followed by ignored sub-group
374+
try assertWarningGroupControl(
375+
"""
376+
func foo() {
377+
1️⃣let x = 1
378+
}
379+
""",
380+
globalControls: [("SuperGroupID", .warning), ("GroupID", .ignored)],
381+
groupInheritanceTree: DiagnosticGroupInheritanceTree(subGroups: [
382+
"SuperGroupID": ["GroupID"]
383+
]),
384+
diagnosticGroupID: "GroupID",
385+
states: [
386+
"1️⃣": .ignored
387+
]
388+
)
389+
}
354390
}
355391

356392
/// Assert that the various marked positions in the source code have the
357393
/// expected warning behavior controls.
358394
private func assertWarningGroupControl(
359395
_ markedSource: String,
360-
globalControls: [DiagnosticGroupIdentifier: WarningGroupControl] = [:],
396+
globalControls: [(DiagnosticGroupIdentifier, WarningGroupControl)] = [],
361397
groupInheritanceTree: DiagnosticGroupInheritanceTree? = nil,
362398
diagnosticGroupID: DiagnosticGroupIdentifier,
363399
states: [String: WarningGroupControl?],

0 commit comments

Comments
 (0)