RedundantSelf Rule Removal Causes Compilation Error in SwiftFormat 0.50.6 - 0.54.0
After updating to SwiftFormat versions between 0.50.6 and 0.54.0, the redundantSelf rule incorrectly removes the explicit use of self in some contexts, which leads to compilation errors. The issue appears when using properties within closures or async contexts in actor classes.
Steps to Reproduce
- Define an actor with a property.
- Access the property within a method, using
selfexplicitly. - Run SwiftFormat version 0.50.6 to 0.54.0 on the codebase.
- Observe the removal of
selfand the subsequent compilation error.
Example
Before running SwiftFormat:
let logger = Logger()
actor TestActor {
let test = ""
private func cancel() {
logger.info("\(String(describing: self.test))")
}
}
After running SwiftFormat:
let logger = Logger()
actor TestActor {
let test = ""
private func cancel() {
logger.info("\(String(describing: test))")
}
}
Error:
Reference to property 'test' in closure requires explicit use of 'self' to make capture semantics explicit
Expected Behavior
SwiftFormat should recognize the context in which self is necessary for property access within closures or async methods and avoid removing it.
Actual Behavior
The redundantSelf rule removes the necessary self prefix, causing a compilation error.
Environment
- SwiftFormat versions: 0.50.6 - 0.54.0
- Swift version: 6.0
- Xcode version: 16.0 beta (16A5171c)
@filimo I'm guessing the argument to Logger.info() is an autoclosure or something? Unfortunately SwiftFormat can't detect this automatically so you have to manually exclude such cases using --selfrequired info
The --selfrequired logger.info provides a partial workaround, but the problem persists in contexts involving String(describing:).
This is an example that reproduces it:
actor TestActor {
private let networkMonitor: NWPathMonitor
init() {
networkMonitor = NWPathMonitor()
}
func setUp() {
networkMonitor.pathUpdateHandler = { [weak self] path in
Task { [weak self] in
guard let self else {
return
}
await self.handleNetworkPathUpdate(path)
}
}
}
@MainActor
func handleNetworkPathUpdate(_: NWPath) { }
}
swiftformat 0.55.5 removes self. before handleNetworkPathUpdate.
What Swift version are you using? Based on the behavior defined in SE-0365 this is supposed to compile.
Can you share a piece of sample code that compiles successfully in a playground like swiftfiddle.com? I can't reproduce the compiler error when I tweak the sample code you shared so that it compiles:
// compiles with Swift 6.0 and 5.10
func takesEscapingClosure(_ closure: @escaping () -> Void) { closure() }
actor TestActor {
func setUp() {
takesEscapingClosure { [weak self] in
Task { [weak self] in
guard let self else {
return
}
await handleNetworkPathUpdate()
}
}
}
@MainActor
func handleNetworkPathUpdate() { }
}
There are certainly lots of bugs and edge cases related to the implementation of SE-0365. Generally I'd say that SwiftFormat should only try to account for and workaround compiler bugs that are particularly widespread. The best thing here would be to file a bug on https://github.com/swiftlang/swift/issues/new with standalone sample code that compiles with explicit self but unexpectedly fails to compile with implicit self.
Oh wow, this actually does fail to compile when I add -swift-version 6. Definitely a compiler bug. Could you file an issue with this info on the Swift repo @OliverBrown-Next?
Thanks for filing https://github.com/swiftlang/swift/issues/79014 -- I posted a fix on the Swift compiler: https://github.com/swiftlang/swift/pull/78738/files#r1938703878