realm / realm/SwiftLint

Rule Request: Detect `localizedDescription` implementations on `Error`-conforming Types

Open
#6,533 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

rule-request
Dominant language
Swift
Stars
19.7k
Forks
2.3k
Avg merge
1d 1h
Merged PRs (30d)
11

Description

New Issue Checklist

New Rule Request

Overview

When implementing Error, providing an implementation for localizedDescription to provide a customized description for an error does not use that custom implementation when the storage for the error is typed as Error:

struct MyCustomError: Error {
    var localizedDescription: String { "This is the custom error description" }
}

MyCustomError().localizedDescription // "This is the custom error description"
(MyCustomError() as Error).localizedDescription // "The operation couldn’t be completed. (MyCustomError error 1.)"

Custom errors get typed as Error most commonly as a general catch branch of a do...catch block:

do {
    try someMethod()
} catch {
    print(error.localizedDescription) // `error` is of type `Error`
}

The proper way to provide a custom description for the error is to conform to the LocalizedError protocol from Foundation and implement the errorDescription property:

struct MyCustomError: Error, LocalizedError {
    var errorDescription: String { "This is the custom error description" }
}

MyCustomError().localizedDescription // "This is the custom error description"
(MyCustomError() as Error).localizedDescription // "This is the custom error description"

It'd be helpful to have a SwiftLint rule that detected when a type conforms to Error and provides an implementation for localizedDescription to push people to use LocalizedError instead.

Examples
// WOULD trigger violations
struct MyCustomError: Error {
    var localizedDescription: String { "Custom description" }
}

enum MyCustomError: Error {
    var localizedDescription: String { "Custom description" }
}

struct MyCustomError: LocalizedError {
    var localizedDescription: String { "Custom description" }
}

// WOULD NOT trigger violations
struct MyCustomError: CustomErrorProtocol {
    var localizedDescription: String { "Custom description" }
}

struct OtherType {
    var localizedDescription: String { "Custom description" }
}
Rule Configuration

There should be a configuration option to specify what protocols a type must inherit from for this rule to be active. By default, this option will contain the values Error and LocalizedError (though there may be other protocols that ship with the standard library or Foundation the refine the Error protocol that we should also include that I'm not aware of). User of SwiftLint can customize this with any additional protocols they define in their codebase that also refine the Error protocol.

Opt-in or Opt-out?

While using typed throws does get the original code example of a do...catch to work:

func doSomething() throws(MyCustomError) {
    throw MyCustomError()
}

do {
    try doSomething()
} catch {
    print(error.localizedDescription) // prints "This is the custom error description" since the compiler knows that `error` is `MyError`.
}

There are a number of issues with typed throws right now that cause the types to not be preserved in various circumstances, most significantly about inferring types in closures (which stems the fact that this was not implemented in time for Swift 6.0, as mentioned in the introduction for the evolution proposal for typed throws). Additionally, there are instances where typed throws are not propagated from certain types in the standard library (such as AsyncIteratorProtocol).

Because of the above, I think it makes sense for this new rule to be enabled by default and require opting out. I could foresee a future where this rule is not enabled by default anymore once the issues with typed throws are resolved, and there is better support for it in the standard library and other frameworks included in the Swift runtime (or maybe even be removed at that point).

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start by examining SwiftLint's existing rule implementations and configuration patterns for protocol-conformance checks. Use the examples as behavioral cases: flag localizedDescription on Error- or LocalizedError-conforming types, allow unrelated types, and support configurable protocols; add tests covering these cases.

Written by the indexing model from the issue text.

Assessment

Tech stack
swift
Domain
tooling
Issue type
Feature
Difficulty
4/5
Estimated time
3-5 days
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
45/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.