Pitch: Allow listing inside a throw block

According to the official documentation, only a single Throws block is supported with DocC in Xcode. However, sometimes, functions have various reasons that could cause them to throw. Documenting them in detail is impossible with the current functionality.

Here's an example of a sample documentation for a function that could have multiple reasons to throw:

    /// This is a short description
    /// - Parameter param1: The first parameter
    /// - Returns: an arbitrary value.
    /// - Throws: `MyHorribleError.FirstIssue` when case ABC occurs.
    /// - Throws: `MyHorribleError.SecondIssue` when case XYZ occurs.
    /// - Throws: `MyHorribleError.ThirdIssue` when all hope is lost.
    func mySampleFunction(param1: String) throws -> String

Now, above, I use multiple Throws statements - while that is rendered semi-reasonably in Xcode with multiple Throws blocks listed below each other, it is undefined behavior according to the official documentation.

If I were to comply with the documentation, I could only have a single Throws block containing all the possible error cases. That results in them being hard to read and identify.

Therefore my suggestion: allow for more granularity in the documentation for Throws blocks by allowing the listing of items, similar to the way one can list function parameters in a Parameters node:

    /// This is a short description
    /// - Parameter param1: The first parameter
    /// - Returns: an arbitrary value.
    /// - Throws: 
    ///    - `MyHorribleError.FirstIssue`: when case ABC occurs.
    ///    - `MyHorribleError.SecondIssue`: when case XYZ occurs.
    ///    - `MyHorribleError.ThirdIssue`: when all hope is lost.
    func mySampleFunction(param1: String) throws -> String

Any thoughts on this?

6 Likes

+1.

And once typed throws are in, it should accept enum shorthand, e.g.:

    /// This is a short description
    /// - Parameter param1: The first parameter
    /// - Returns: an arbitrary value.
    /// - Throws: 
    ///    - `.FirstIssue`: when case ABC occurs.
    ///    - `.SecondIssue`: when case XYZ occurs.
    ///    - `.ThirdIssue`: when all hope is lost.
    func mySampleFunction(param1: String) throws(MyHorribleError) -> String
1 Like

I think DocC's callout syntax may already support this. Does this work for you?

/// This is a short description
/// - Parameter param1: The first parameter
/// - Returns: an arbitrary value.
/// 
/// > Throws:
/// >   - `MyHorribleError.FirstIssue`: when case ABC occurs.
/// >   - `MyHorribleError.SecondIssue`: when case XYZ occurs.
/// >   - `MyHorribleError.ThirdIssue`: when all hope is lost.

I still think it's worth considering adding specialized rendering for this kind of scenario (like DocC has for parameters) but this may at least get you close to what you're looking for.

It may also be worth considering using a term list here, which is basically what a parameters list approximates.

/// This is a short description
/// - Parameter param1: The first parameter
/// - Returns: an arbitrary value.
/// 
/// > Throws:
/// >   - term `MyHorribleError.FirstIssue`: when case ABC occurs.
/// >   - term `MyHorribleError.SecondIssue`: when case XYZ occurs.
/// >   - term `MyHorribleError.ThirdIssue`: when all hope is lost.
1 Like

We have a similar situation with documenting the individual elements of tuple return values.

1 Like

The Swift API guidelines even have an example that documents names tuple components but IIRC DocC doesn't support that yet.

  • Label tuple members and name closure parameters where they appear in your API.

    These names have explanatory power, can be referenced from documentation comments, and provide expressive access to tuple members.

    /// Ensure that we hold uniquely-referenced storage for at least
    /// `requestedCapacity` elements.
    ///
    /// If more storage is needed, `allocate` is called with
    /// `byteCount` equal to the number of maximally-aligned
    /// bytes to allocate.
    ///
    /// - Returns:
    ///   - reallocated: `true` if a new block of memory
    ///     was allocated; otherwise, `false`.
    ///   - capacityChanged: `true` if `capacity` was updated;
    ///     otherwise, `false`.
    mutating func ensureUniqueStorage(
        minimumCapacity requestedCapacity: Int,
        allocate: (_ byteCount: Int) -> UnsafePointer<Void>
    ) -> (reallocated: Bool, capacityChanged: Bool)