Skip to content

Commit 5b18fb4

Browse files
committed
[SwiftWarningControl] Add ability to specify diagnostic group links
These links establish group inheritance relationships amongst diagnostic groups. This takes form of a new parameter: `groupInheritanceTree: [DiagnosticGroupIdentifier: [DiagnosticGroupIdentifier]] = [:]` on both: - `SyntaxProtocol.warningGroupBehavior` - `SyntaxProtocol.warningGroupControlRegionTree` Where the dictionary key is a super-group and dictionary values are its sub-groups. Upon encountering a warning group control (`@warn`), its corresponding region is populated with its identifier and behavior, as well as the same corresponding behavior for each of its sub-groups, direct and transitive.
1 parent 90e6d96 commit 5b18fb4

File tree

4 files changed

+84
-11
lines changed

4 files changed

+84
-11
lines changed

Sources/SwiftWarningControl/SyntaxProtocol+WarningControl.swift

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,10 +27,12 @@ extension SyntaxProtocol {
2727
@_spi(ExperimentalLanguageFeatures)
2828
public func warningGroupControl(
2929
for diagnosticGroupIdentifier: DiagnosticGroupIdentifier,
30-
globalControls: [DiagnosticGroupIdentifier: WarningGroupControl] = [:]
30+
globalControls: [DiagnosticGroupIdentifier: WarningGroupControl] = [:],
31+
groupInheritanceTree: [DiagnosticGroupIdentifier: [DiagnosticGroupIdentifier]] = [:]
3132
) -> WarningGroupControl? {
3233
let warningControlRegions = root.warningGroupControlRegionTreeImpl(
3334
globalControls: globalControls,
35+
groupInheritanceTree: groupInheritanceTree,
3436
containing: self.position
3537
)
3638
return warningControlRegions.warningGroupControl(at: self.position, for: diagnosticGroupIdentifier)

Sources/SwiftWarningControl/WarningControlRegionBuilder.swift

Lines changed: 18 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,13 @@ import SwiftSyntax
1616
extension SyntaxProtocol {
1717
@_spi(ExperimentalLanguageFeatures)
1818
public func warningGroupControlRegionTree(
19-
globalControls: [DiagnosticGroupIdentifier: WarningGroupControl] = [:]
19+
globalControls: [DiagnosticGroupIdentifier: WarningGroupControl] = [:],
20+
groupInheritanceTree: [DiagnosticGroupIdentifier: [DiagnosticGroupIdentifier]] = [:]
2021
) -> WarningControlRegionTree {
21-
return warningGroupControlRegionTreeImpl(globalControls: globalControls)
22+
return warningGroupControlRegionTreeImpl(
23+
globalControls: globalControls,
24+
groupInheritanceTree: groupInheritanceTree
25+
)
2226
}
2327

2428
/// Implementation of constructing a region tree with an optional parameter
@@ -27,9 +31,14 @@ extension SyntaxProtocol {
2731
/// queries.
2832
func warningGroupControlRegionTreeImpl(
2933
globalControls: [DiagnosticGroupIdentifier: WarningGroupControl],
34+
groupInheritanceTree: [DiagnosticGroupIdentifier: [DiagnosticGroupIdentifier]],
3035
containing position: AbsolutePosition? = nil
3136
) -> WarningControlRegionTree {
32-
let visitor = WarningControlRegionVisitor(self.range, containing: position)
37+
let visitor = WarningControlRegionVisitor(
38+
self.range,
39+
containing: position,
40+
groupInheritanceTree: groupInheritanceTree
41+
)
3342
visitor.tree.addWarningGroupControls(range: self.range, controls: globalControls)
3443
visitor.walk(self)
3544
return visitor.tree
@@ -53,8 +62,12 @@ private class WarningControlRegionVisitor: SyntaxAnyVisitor {
5362
var tree: WarningControlRegionTree
5463
let containingPosition: AbsolutePosition?
5564

56-
init(_ topLevelRange: Range<AbsolutePosition>, containing position: AbsolutePosition? = nil) {
57-
self.tree = WarningControlRegionTree(range: topLevelRange)
65+
init(
66+
_ topLevelRange: Range<AbsolutePosition>,
67+
containing position: AbsolutePosition?,
68+
groupInheritanceTree: [DiagnosticGroupIdentifier: [DiagnosticGroupIdentifier]]
69+
) {
70+
self.tree = WarningControlRegionTree(range: topLevelRange, groupInheritanceTree: groupInheritanceTree)
5871
containingPosition = position
5972
super.init(viewMode: .fixedUp)
6073
}

Sources/SwiftWarningControl/WarningControlRegions.swift

Lines changed: 31 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -103,8 +103,16 @@ public struct WarningControlRegionTree {
103103
/// Root region representing top-level (file) scope
104104
private var rootRegionNode: WarningControlRegionNode
105105

106-
init(range: Range<AbsolutePosition>) {
107-
rootRegionNode = WarningControlRegionNode(range: range)
106+
/// Inheritance relationships between diagnostic group identifiers
107+
/// Key: super-group, Values: sub-groups
108+
let groupInheritanceTree: DiagnosticGroupInheritanceTree
109+
110+
init(
111+
range: Range<AbsolutePosition>,
112+
groupInheritanceTree: [DiagnosticGroupIdentifier: [DiagnosticGroupIdentifier]]
113+
) {
114+
self.rootRegionNode = WarningControlRegionNode(range: range)
115+
self.groupInheritanceTree = DiagnosticGroupInheritanceTree(subGroups: groupInheritanceTree)
108116
}
109117

110118
/// Add a warning control region to the tree
@@ -115,7 +123,14 @@ public struct WarningControlRegionTree {
115123
guard !controls.isEmpty else { return }
116124
let newNode = WarningControlRegionNode(range: range)
117125
for (diagnosticGroupIdentifier, control) in controls {
118-
newNode.addWarningGroupControl(for: diagnosticGroupIdentifier, control: control)
126+
var groups: [DiagnosticGroupIdentifier] = [diagnosticGroupIdentifier]
127+
while !groups.isEmpty {
128+
let groupIdentifier = groups.removeFirst()
129+
newNode.addWarningGroupControl(for: groupIdentifier, control: control)
130+
// Ensure we add a corresponding control to each direct and
131+
// transitive sub-group of the one specified on this control.
132+
groups.append(contentsOf: groupInheritanceTree.subgroups(of: groupIdentifier))
133+
}
119134
}
120135
insertIntoSubtree(newNode, parent: rootRegionNode)
121136
}
@@ -170,6 +185,19 @@ extension WarningControlRegionTree: CustomDebugStringConvertible {
170185
}
171186
}
172187

188+
/// A struct wrapper for a diagnostic group inheritance tree
189+
/// represented with a dictionary of a group identifier to an array
190+
/// of its sub-group identifiers.
191+
struct DiagnosticGroupInheritanceTree {
192+
private let subGroups: [DiagnosticGroupIdentifier: [DiagnosticGroupIdentifier]]
193+
init(subGroups: [DiagnosticGroupIdentifier: [DiagnosticGroupIdentifier]]) {
194+
self.subGroups = subGroups
195+
}
196+
197+
/// Diagnostic groups that inherit from `group`.
198+
func subgroups(of group: DiagnosticGroupIdentifier) -> [DiagnosticGroupIdentifier] { subGroups[group] ?? [] }
199+
}
200+
173201
extension WarningControlRegionTree {
174202
/// Determine the warning group behavior control at a specified position
175203
/// for a given diagnostic group.

Tests/SwiftWarningControlTest/WarningControlTests.swift

Lines changed: 32 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -279,13 +279,38 @@ public class WarningGroupControlTests: XCTestCase {
279279
]
280280
)
281281
}
282+
283+
func testSubGroupInheritance() throws {
284+
try assertWarningGroupControl(
285+
"""
286+
@warn(SuperGroupID, as: error)
287+
func foo() {
288+
1️⃣let x = 1
289+
}
290+
@warn(SuperSuperGroupID, as: ignored)
291+
func bar() {
292+
2️⃣let x = 1
293+
}
294+
""",
295+
groupInheritanceTree: [
296+
"SuperGroupID": ["GroupID"],
297+
"SuperSuperGroupID": ["SuperGroupID"],
298+
],
299+
diagnosticGroupID: "GroupID",
300+
states: [
301+
"1️⃣": .error,
302+
"2️⃣": .ignored,
303+
]
304+
)
305+
}
282306
}
283307

284308
/// Assert that the various marked positions in the source code have the
285309
/// expected warning behavior controls.
286310
private func assertWarningGroupControl(
287311
_ markedSource: String,
288312
globalControls: [DiagnosticGroupIdentifier: WarningGroupControl] = [:],
313+
groupInheritanceTree: [DiagnosticGroupIdentifier: [DiagnosticGroupIdentifier]] = [:],
289314
diagnosticGroupID: DiagnosticGroupIdentifier,
290315
states: [String: WarningGroupControl?],
291316
file: StaticString = #filePath,
@@ -308,10 +333,15 @@ private func assertWarningGroupControl(
308333
continue
309334
}
310335

311-
let warningControlRegions = tree.warningGroupControlRegionTree(globalControls: globalControls)
336+
let warningControlRegions = tree.warningGroupControlRegionTree(
337+
globalControls: globalControls,
338+
groupInheritanceTree: groupInheritanceTree
339+
)
340+
312341
let groupControl = token.warningGroupControl(
313342
for: diagnosticGroupID,
314-
globalControls: globalControls
343+
globalControls: globalControls,
344+
groupInheritanceTree: groupInheritanceTree
315345
)
316346
XCTAssertEqual(groupControl, expectedState)
317347

0 commit comments

Comments
 (0)