nicklockwood / SwiftFormat

A command-line tool and Xcode Extension for formatting Swift code
MIT License
7.77k stars 629 forks source link

Compile error with conditionalAssignment and Swift version 5.9 #1542

Closed honkmaster closed 8 months ago

honkmaster commented 10 months ago

With swift-version 5.9 the following code

let hotspotConfiguration: NEHotspotConfiguration
if let passphrase {
  hotspotConfiguration = NEHotspotConfiguration(ssid: ssid, passphrase: passphrase, isWEP: isWEP)
 } else {
  hotspotConfiguration = NEHotspotConfiguration(ssid: ssid)
}

becomes:

let hotspotConfiguration: NEHotspotConfiguration = if let passphrase {
  .init(ssid: ssid, passphrase: passphrase, isWEP: isWEP)
} else {
  .init(ssid: ssid)
}

Subsequently the compilation fails.

Command SwiftCompile failed with a nonzero exit code

Currently, the compiler does not give more information, the current workaround, besides the possibility to revert to 5.8, is to disable conditionalAssignment. The code itself looks fine...

nicklockwood commented 10 months ago

@calda possible Swift bug?

calda commented 10 months ago

Which rule is converting NEHotspotConfiguration( to just .init(? Does the code compile without that change?

nicklockwood commented 10 months ago

@calda good point. Presumably it's https://github.com/nicklockwood/SwiftFormat/blob/main/Rules.md#redundanttype

honkmaster commented 10 months ago

When removing the code above, I get the same error in files where the subsequent shown code snippets were changed (each before / after). Before, the code compiles fine.

(1)

var text: String
if entry.yValues.contains(where: { $0.key == .foo }) {
  text = startFormatted
} else {
  text = startFormatted + "\u{00a0}–\u{00a0}" + endFormatted
}
var text: String = if entry.yValues.contains(where: { $0.key == .foo }) {
  startFormatted
} else {
  startFormatted + "\u{00a0}–\u{00a0}" + endFormatted
}

(2)

let data: Data?
if case let .jpeg(compressionQuality) = imageType {
  data = newValue.jpegData(compressionQuality: compressionQuality)
} else {
  data = newValue.pngData()
 }
let data: Data? = if case let .jpeg(compressionQuality) = imageType {
  newValue.jpegData(compressionQuality: compressionQuality)
} else {
  newValue.pngData()
}

(3) (code changed not due to conditionalAssignment but redundantReturn)

extension MSALManagerError: CustomDebugStringConvertible {
    var debugDescription: String {
        switch self {
        case .invalidURL:
            return "Invalid authority URL provided."
        case .noRootViewController:
            return "App has no root view controller."
        case let .authorityCreation(error):
            return "Can't create authority (\(error.localizedDescription))."
        case let .publicClientApplicationCreation(error):
            return "Can't create application (\(error.localizedDescription))."
        case let .accessAccounts(error):
            return "Error accessing accounts (\(error.localizedDescription))."
        case .noAccountInCache:
            return "No account in cache."
        case .multipleAccountsInCache:
            return "Multiple accounts in cache."
        case .userNotSignedIn:
            return "User not signed in."
        case .unknownTokenError:
            return "Unknown token error."
        case .unknownMSALResultError:
            return "Unknown MSALResult error."
        }
    }
}
extension MSALManagerError: CustomDebugStringConvertible {
    var debugDescription: String {
        switch self {
        case .invalidURL:
            "Invalid authority URL provided."
        case .noRootViewController:
            "App has no root view controller."
        case let .authorityCreation(error):
            "Can't create authority (\(error.localizedDescription))."
        case let .publicClientApplicationCreation(error):
            "Can't create application (\(error.localizedDescription))."
        case let .accessAccounts(error):
            "Error accessing accounts (\(error.localizedDescription))."
        case .noAccountInCache:
            "No account in cache."
        case .multipleAccountsInCache:
            "Multiple accounts in cache."
        case .userNotSignedIn:
            "User not signed in."
        case .unknownTokenError:
            "Unknown token error."
        case .unknownMSALResultError:
            "Unknown MSALResult error."
        }
    }
}

Just to be sure: swiftformat --version <-> 0.52.7; Xcode Version 15.0 (15A240d)

nicklockwood commented 10 months ago

@honkmaster all of the output examples you've given seem to be valid Swift code, and compile OK when I run them in isolation. I suspect you've stumbled across a bug in Swift itself.

In principle I'm OK with adding version-specific fixes to SwiftFormat to work around known Swift bugs, but in this case I think the circumstance that produce the bug probably depend on project-specific context that SwiftFormat wouldn't be able to determine.

My suggestion would be to report this bug to Apple, and to make use of SwiftFormat's // swiftformat:disable:next directives to conditionally exclude these particular cases from being formatted until Apple fixes the issue.