SwiftFormat icon indicating copy to clipboard operation
SwiftFormat copied to clipboard

RedundantSelf Rule Removal Causes Compilation Error in SwiftFormat 0.50.6 - 0.54.0

Open filimo opened this issue 1 year ago • 7 comments

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

  1. Define an actor with a property.
  2. Access the property within a method, using self explicitly.
  3. Run SwiftFormat version 0.50.6 to 0.54.0 on the codebase.
  4. Observe the removal of self and 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 avatar Jun 14 '24 03:06 filimo

@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

nicklockwood avatar Jun 15 '24 10:06 nicklockwood

The --selfrequired logger.info provides a partial workaround, but the problem persists in contexts involving String(describing:).

filimo avatar Jun 15 '24 13:06 filimo

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.

OliverBrown-Next avatar Jan 28 '25 08:01 OliverBrown-Next

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() { }
}

calda avatar Jan 28 '25 14:01 calda

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.

calda avatar Jan 28 '25 14:01 calda

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?

calda avatar Jan 28 '25 14:01 calda

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

calda avatar Feb 03 '25 03:02 calda