Skip to content

Commit 9a06329

Browse files
committed
Track value locations in XCConfigs per assignment
In #513, initial support for this was added, this PR moves the location to be per assignment which allows emitting fix its not just for the last seen assignments. This will also allow looking at any conditions of assignments when choosing the location for emitting fix its.
1 parent eca9ed5 commit 9a06329

File tree

2 files changed

+42
-63
lines changed

2 files changed

+42
-63
lines changed

Sources/SWBMacro/MacroEvaluationScope.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ private extension MacroValueAssignmentTable {
1717
func lookupMacro(_ macro: MacroDeclaration, overrideLookup: ((MacroDeclaration) -> MacroExpression?)? = nil) -> MacroValueAssignment? {
1818
// See if we have an overriding binding.
1919
if let override = overrideLookup?(macro) {
20-
return MacroValueAssignment(expression: override, conditions: nil, next: lookupMacro(macro))
20+
return MacroValueAssignment(expression: override, conditions: nil, next: lookupMacro(macro), location: nil)
2121
}
2222

2323
// Otherwise, return the normal lookup.

Sources/SWBMacro/MacroValueAssignmentTable.swift

Lines changed: 41 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -20,23 +20,18 @@ public struct MacroValueAssignmentTable: Serializable, Sendable {
2020
/// Maps macro declarations to corresponding linked lists of assignments.
2121
public var valueAssignments: [MacroDeclaration: MacroValueAssignment]
2222

23-
private var valueLocations: [String: InternedMacroValueAssignmentLocation]
24-
private var macroConfigPaths: OrderedSet<Path>
25-
26-
private init(namespace: MacroNamespace, valueAssignments: [MacroDeclaration: MacroValueAssignment], valueLocations: [String: InternedMacroValueAssignmentLocation], macroConfigPaths: OrderedSet<Path>) {
23+
private init(namespace: MacroNamespace, valueAssignments: [MacroDeclaration: MacroValueAssignment]) {
2724
self.namespace = namespace
2825
self.valueAssignments = valueAssignments
29-
self.valueLocations = valueLocations
30-
self.macroConfigPaths = macroConfigPaths
3126
}
3227

3328
public init(namespace: MacroNamespace) {
34-
self.init(namespace: namespace, valueAssignments: [:], valueLocations: [:], macroConfigPaths: OrderedSet())
29+
self.init(namespace: namespace, valueAssignments: [:])
3530
}
3631

3732
/// Convenience initializer to create a `MacroValueAssignmentTable` from another instance (i.e., to create a copy).
3833
public init(copying table: MacroValueAssignmentTable) {
39-
self.init(namespace: table.namespace, valueAssignments: table.valueAssignments, valueLocations: table.valueLocations, macroConfigPaths: table.macroConfigPaths)
34+
self.init(namespace: table.namespace, valueAssignments: table.valueAssignments)
4035
}
4136

4237
/// Remove all assignments for the given macro.
@@ -86,28 +81,14 @@ public struct MacroValueAssignmentTable: Serializable, Sendable {
8681
assert(namespace.lookupMacroDeclaration(macro.name) === macro)
8782
// Validate the type.
8883
assert(macro.type.matchesExpressionType(value))
89-
valueAssignments[macro] = MacroValueAssignment(expression: value, conditions: conditions, next: valueAssignments[macro])
90-
91-
if let location {
92-
let index = macroConfigPaths.append(location.path).index
93-
valueLocations[macro.name] = InternedMacroValueAssignmentLocation(pathRef: index, line: location.line, startColumn: location.startColumn, endColumn: location.endColumn)
94-
}
95-
}
96-
97-
private mutating func mergeLocations(from otherTable: MacroValueAssignmentTable) {
98-
otherTable.valueLocations.forEach {
99-
let path = otherTable.macroConfigPaths[$0.value.pathRef]
100-
let index = macroConfigPaths.append(path).index
101-
valueLocations[$0.key] = .init(pathRef: index, line: $0.value.line, startColumn: $0.value.startColumn, endColumn: $0.value.endColumn)
102-
}
84+
valueAssignments[macro] = MacroValueAssignment(expression: value, conditions: conditions, next: valueAssignments[macro], location: location)
10385
}
10486

10587
/// Adds a mapping from each of the macro-to-value mappings in `otherTable`, inserting them ahead of any already existing assignments in the receiving table. The other table isn’t affected in any way (in particular, no reference is kept from the receiver to the other table).
10688
public mutating func pushContentsOf(_ otherTable: MacroValueAssignmentTable) {
10789
for (macro, firstAssignment) in otherTable.valueAssignments {
10890
valueAssignments[macro] = insertCopiesOfMacroValueAssignmentNodes(firstAssignment, inFrontOf: valueAssignments[macro])
10991
}
110-
mergeLocations(from: otherTable)
11192
}
11293

11394
/// Looks up and returns the first (highest-precedence) macro value assignment for `macro`, if there is one.
@@ -126,15 +107,7 @@ public struct MacroValueAssignmentTable: Serializable, Sendable {
126107
}
127108

128109
public func location(of macro: MacroDeclaration) -> MacroValueAssignmentLocation? {
129-
guard let location = valueLocations[macro.name] else {
130-
return nil
131-
}
132-
return MacroValueAssignmentLocation(
133-
path: macroConfigPaths[location.pathRef],
134-
line: location.line,
135-
startColumn: location.startColumn,
136-
endColumn: location.endColumn
137-
)
110+
return lookupMacro(macro)?.location
138111
}
139112

140113
public func bindConditionParameter(_ parameter: MacroConditionParameter, _ conditionValues: [String]) -> MacroValueAssignmentTable {
@@ -223,7 +196,6 @@ public struct MacroValueAssignmentTable: Serializable, Sendable {
223196
bindAndPushAssignment(firstAssignment)
224197

225198
}
226-
table.mergeLocations(from: self)
227199
return table
228200
}
229201

@@ -251,7 +223,7 @@ public struct MacroValueAssignmentTable: Serializable, Sendable {
251223
// MARK: Serialization
252224

253225
public func serialize<T: Serializer>(to serializer: T) {
254-
serializer.beginAggregate(3)
226+
serializer.beginAggregate(1)
255227

256228
// We don't directly serialize MacroDeclarations, but rather serialize their contents "by hand" so when we deserialize we can re-use existing declarations in our namespace.
257229
serializer.beginAggregate(valueAssignments.count)
@@ -279,17 +251,6 @@ public struct MacroValueAssignmentTable: Serializable, Sendable {
279251
}
280252
serializer.endAggregate() // valueAssignments
281253

282-
serializer.beginAggregate(valueLocations.count)
283-
for (decl, loc) in valueLocations.sorted(by: { $0.0 < $1.0 }) {
284-
serializer.beginAggregate(2)
285-
serializer.serialize(decl)
286-
serializer.serialize(loc)
287-
serializer.endAggregate()
288-
}
289-
serializer.endAggregate()
290-
291-
serializer.serialize(macroConfigPaths)
292-
293254
serializer.endAggregate() // the whole table
294255
}
295256

@@ -298,10 +259,9 @@ public struct MacroValueAssignmentTable: Serializable, Sendable {
298259
guard let delegate = deserializer.delegate as? (any MacroValueAssignmentTableDeserializerDelegate) else { throw DeserializerError.invalidDelegate("delegate must be a MacroValueAssignmentTableDeserializerDelegate") }
299260
self.namespace = delegate.namespace
300261
self.valueAssignments = [:]
301-
self.valueLocations = [:]
302262

303263
// Deserialize the table.
304-
try deserializer.beginAggregate(3)
264+
try deserializer.beginAggregate(1)
305265

306266
// Iterate over all the key-value pairs.
307267
let count: Int = try deserializer.beginAggregate()
@@ -348,16 +308,6 @@ public struct MacroValueAssignmentTable: Serializable, Sendable {
348308
// Add it to the dictionary.
349309
self.valueAssignments[decl] = asgn
350310
}
351-
352-
let count2 = try deserializer.beginAggregate()
353-
for _ in 0..<count2 {
354-
try deserializer.beginAggregate(2)
355-
let name: String = try deserializer.deserialize()
356-
let location: InternedMacroValueAssignmentLocation = try deserializer.deserialize()
357-
self.valueLocations[name] = location
358-
}
359-
360-
self.macroConfigPaths = try deserializer.deserialize()
361311
}
362312
}
363313

@@ -379,11 +329,38 @@ public final class MacroValueAssignment: Serializable, CustomStringConvertible,
379329
/// Reference to the next (lower precedence) assignment in the linked list, or nil if this is the last one.
380330
public let next: MacroValueAssignment?
381331

332+
private let _location: InternedMacroValueAssignmentLocation?
333+
private static let macroConfigPaths = SWBMutex<OrderedSet<Path>>(OrderedSet())
334+
335+
var location: MacroValueAssignmentLocation? {
336+
if let _location {
337+
return .init(
338+
path: Self.macroConfigPaths.withLock { $0[_location.pathRef] },
339+
line: _location.line,
340+
startColumn: _location.startColumn,
341+
endColumn: _location.endColumn
342+
)
343+
} else {
344+
return nil
345+
}
346+
}
347+
382348
/// Initializes the macro value assignment to represent `expression`, with the next existing macro value assignment (if any).
383-
init(expression: MacroExpression, conditions: MacroConditionSet? = nil, next: MacroValueAssignment?) {
349+
init(expression: MacroExpression, conditions: MacroConditionSet? = nil, next: MacroValueAssignment?, location: MacroValueAssignmentLocation?) {
384350
self.expression = expression
385351
self.conditions = conditions
386352
self.next = next
353+
354+
if let location {
355+
self._location = InternedMacroValueAssignmentLocation(
356+
pathRef: Self.macroConfigPaths.withLock({ $0.append(location.path).index }),
357+
line: location.line,
358+
startColumn: location.startColumn,
359+
endColumn: location.endColumn
360+
)
361+
} else {
362+
self._location = nil
363+
}
387364
}
388365

389366
/// Returns the first macro value assignment that is reachable from the receiver and whose conditions match the given set of parameter values, or nil if there is no such assignment value. The returned assignment may be the receiver itself, or it may be any assignment that’s downstream in the linked list of macro value assignments, or it may be nil if there is none. Unconditional macro value assignments are considered to match any conditions. Conditions that reference parameters that don’t have a value in `paramValues` are only considered to match if the match pattern is `*`, i.e. the “match-anything” pattern (which is effectively a no-op).
@@ -435,18 +412,20 @@ public final class MacroValueAssignment: Serializable, CustomStringConvertible,
435412
// MARK: Serialization
436413

437414
public func serialize<T: Serializer>(to serializer: T) {
438-
serializer.beginAggregate(3)
415+
serializer.beginAggregate(4)
439416
serializer.serialize(expression)
440417
serializer.serialize(conditions)
441418
serializer.serialize(next)
419+
serializer.serialize(_location)
442420
serializer.endAggregate()
443421
}
444422

445423
public init(from deserializer: any Deserializer) throws {
446-
try deserializer.beginAggregate(3)
424+
try deserializer.beginAggregate(4)
447425
self.expression = try deserializer.deserialize()
448426
self.conditions = try deserializer.deserialize()
449427
self.next = try deserializer.deserialize()
428+
self._location = try deserializer.deserialize()
450429
}
451430
}
452431

@@ -510,10 +489,10 @@ private func insertCopiesOfMacroValueAssignmentNodes(_ srcAsgn: MacroValueAssign
510489
}
511490

512491
if let srcNext = srcAsgn.next {
513-
return MacroValueAssignment(expression: srcAsgn.expression, conditions:srcAsgn.conditions, next: insertCopiesOfMacroValueAssignmentNodes(srcNext, inFrontOf: dstAsgn))
492+
return MacroValueAssignment(expression: srcAsgn.expression, conditions:srcAsgn.conditions, next: insertCopiesOfMacroValueAssignmentNodes(srcNext, inFrontOf: dstAsgn), location: srcAsgn.location)
514493
}
515494
else {
516-
return MacroValueAssignment(expression: srcAsgn.expression, conditions:srcAsgn.conditions, next: dstAsgn)
495+
return MacroValueAssignment(expression: srcAsgn.expression, conditions:srcAsgn.conditions, next: dstAsgn, location: srcAsgn.location)
517496
}
518497
}
519498

0 commit comments

Comments
 (0)