Skip to content

Commit 8af5e54

Browse files
committed
swift-package-migrate: Call SwiftFixIt once for all diagnostic files
This excludes the possibility of repeated fix-it application caused by accidental emission of migration warnings in dependency modules. Also add a comment for why we exclude plugins from migration.
1 parent 89ba130 commit 8af5e54

File tree

2 files changed

+50
-28
lines changed

2 files changed

+50
-28
lines changed

Sources/Commands/PackageCommands/Migrate.swift

Lines changed: 49 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -90,43 +90,66 @@ extension SwiftPackageCommand {
9090
features.append(feature)
9191
}
9292

93-
var targets = self.options.targets
94-
9593
let buildSystem = try await createBuildSystem(
9694
swiftCommandState,
97-
targets: targets,
95+
targets: self.options.targets,
9896
features: features
9997
)
10098

101-
// Next, let's build all of the individual targets or the
102-
// whole project to get diagnostic files.
99+
// Compute the set of targets to migrate.
100+
let targetsToMigrate: Set<String>
101+
if self.options.targets.isEmpty {
102+
let graph = try await buildSystem.getPackageGraph()
103+
targetsToMigrate = Set(
104+
graph.rootPackages.lazy.map { package in
105+
package.manifest.targets.lazy.filter { target in
106+
// FIXME: Plugin target init does not have Swift settings.
107+
// Exclude them from migration.
108+
target.type != .plugin
109+
}.map(\.name)
110+
}.joined()
111+
)
112+
} else {
113+
targetsToMigrate = Set(self.options.targets.elements)
114+
}
115+
116+
// Next, let's build the requested targets or, if none were given,
117+
// the whole project to get diagnostic files.
103118
print("> Starting the build")
104-
var diagnosticsPaths: [String: [AbsolutePath]] = [:]
105-
if !targets.isEmpty {
106-
for target in targets {
107-
let buildResult = try await buildSystem.build(subset: .target(target))
108-
diagnosticsPaths.merge(try buildResult.serializedDiagnosticPathsByTargetName.get(), uniquingKeysWith: { $0 + $1 })
119+
var diagnosticFiles: [[AbsolutePath]] = []
120+
if self.options.targets.isEmpty {
121+
// No targets were requested. Build everything.
122+
let buildResult = try await buildSystem.build(subset: .allIncludingTests)
123+
for (target, files) in try buildResult.serializedDiagnosticPathsByTargetName.get() {
124+
if targetsToMigrate.contains(target) {
125+
diagnosticFiles.append(files)
126+
}
109127
}
110128
} else {
111-
diagnosticsPaths = try await buildSystem.build(subset: .allIncludingTests).serializedDiagnosticPathsByTargetName.get()
129+
// Build only requested targets.
130+
for target in self.options.targets.elements {
131+
// TODO: It would be nice if BuildSubset had a case for an
132+
// array of targets so that we can move the build out of
133+
// this enclosing if/else and avoid repetition.
134+
let buildResult = try await buildSystem.build(subset: .target(target))
135+
for (target, files) in try buildResult.serializedDiagnosticPathsByTargetName.get() {
136+
if targetsToMigrate.contains(target) {
137+
diagnosticFiles.append(files)
138+
}
139+
}
140+
}
112141
}
113142

114-
var summary = SwiftFixIt.Summary(numberOfFixItsApplied: 0, numberOfFilesChanged: 0)
115-
let graph = try await buildSystem.getPackageGraph()
116-
if targets.isEmpty {
117-
targets = OrderedSet(graph.rootPackages.flatMap { $0.manifest.targets.filter { $0.type != .plugin }.map(\.name) })
118-
}
119143
print("> Applying fix-its")
144+
var summary = SwiftFixIt.Summary(numberOfFixItsApplied: 0, numberOfFilesChanged: 0)
120145
let fixItDuration = try ContinuousClock().measure {
121-
for target in targets {
122-
let fixit = try SwiftFixIt(
123-
diagnosticFiles: Array(diagnosticsPaths[target] ?? []),
124-
categories: Set(features.flatMap(\.categories)),
125-
excludedSourceDirectories: [swiftCommandState.scratchDirectory],
126-
fileSystem: swiftCommandState.fileSystem
127-
)
128-
summary += try fixit.applyFixIts()
129-
}
146+
let applier = try SwiftFixIt(
147+
diagnosticFiles: diagnosticFiles.joined(),
148+
categories: Set(features.flatMap(\.categories)),
149+
excludedSourceDirectories: [swiftCommandState.scratchDirectory],
150+
fileSystem: swiftCommandState.fileSystem
151+
)
152+
summary = try applier.applyFixIts()
130153
}
131154

132155
// Report the changes.
@@ -154,9 +177,8 @@ extension SwiftPackageCommand {
154177

155178
// Once the fix-its were applied, it's time to update the
156179
// manifest with newly adopted feature settings.
157-
158180
print("> Updating manifest")
159-
for target in targets {
181+
for target in targetsToMigrate {
160182
swiftCommandState.observabilityScope.emit(debug: "Adding feature(s) to '\(target)'")
161183
try self.updateManifest(
162184
for: target,

Sources/SwiftFixIt/SwiftFixIt.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -211,7 +211,7 @@ package struct SwiftFixIt /*: ~Copyable */ { // TODO: Crashes with ~Copyable
211211
private let diagnosticsPerFile: DiagnosticsPerFile
212212

213213
package init(
214-
diagnosticFiles: [AbsolutePath],
214+
diagnosticFiles: some Collection<AbsolutePath>,
215215
categories: Set<String> = [],
216216
excludedSourceDirectories: Set<AbsolutePath> = [],
217217
fileSystem: any FileSystem

0 commit comments

Comments
 (0)