Skip to content

Conversation

@ethan-kusters
Copy link
Contributor

@ethan-kusters ethan-kusters commented Jun 24, 2022

Bug/issue #, if applicable: rdar://95808950

Summary

With the recent fix for supporting generated implementation sections for
symbols that include periods (like Comparable...<) (#257), we introduced a
regression where it's possible to end up with a duplicate default
implementation section.

This can happen when a protocol has a mix of documentation coverage. For example

public protocol Foo {
    /// This is my great doc.
    func fooMemberWithDocComment()
    func fooMemberWithoutDocComment()
}

public extension Foo {
    func fooMemberWithDocComment() { }
    func fooMemberWithoutDocComment() { }
}

public struct Bar: Foo {}

Here Bar would end up with two "Foo Implementations" sections.

This is because we're able to access the original parent symbol for
fooMemberWithDocComment() but not for fooMemberWithoutDocComment so
these relationships end up with a different unique identifier but the same
collection name: "Foo Implementations".

The fix is to not rely on the parent relationship being there and instead
look at the origin symbol's parent path components. We can rely on the origin
symbol always being there for protocols defined in the current module.


This also resolves an issue where conforming to compmlicated protocols in a
different module would produce a misnamed collection. For example:

// FirstTarget

infix operator .<.. : RangeFormationPrecedence
public protocol OtherFancyProtocol {
    static func .<.. (lhs: OtherFancyProtocol, rhs: OtherFancyProtocol)
}

public extension OtherFancyProtocol {
    static func .<.. (lhs: OtherFancyProtocol, rhs: OtherFancyProtocol) {}
}
// SecondTarget
import FirstTarget

public struct OtherFancyProtocolConformer: OtherFancyProtocol {}

Here we would end up with a default collection named "< Implementations"
instead of "OtherFancyProtocol Implementations". The solution is to figure
out the actual name of the symbol based on the source symbol which is always
guaranteed to be there. We can use this to split out the parent symbol's name
from the source origin display name.

Testing

Build documentation with the examples included in the summary (also included in the attached Swift Package) and confirm that they produce automatic collections as expected.

Checklist

Make sure you check off the following items. If they cannot be completed, provide a reason.

  • Added tests
  • Ran the ./bin/test script and it succeeded
  • Updated documentation if necessary

With the recent fix for supporting generated implementation sections for
symbols that include periods (like `Comparable...<`), we introduced a
regression where it's possible to end up with a duplicate default
implementation section.

This can happen when a protocol has a mix of documentation coverage. For example

```swift
public protocol Foo {
    /// This is my great doc.
    func fooMemberWithDocComment()
    func fooMemberWithoutDocComment()
}

public extension Foo {
    func fooMemberWithDocComment() { }
    func fooMemberWithoutDocComment() { }
}

public struct Bar: Foo {}
```

Here `Bar` would end up with two "Foo Implementations" sections.

This is because we're able to access the original parent symbol for
`fooMemberWithDocComment()` but not for `fooMemberWithoutDocComment` so
these relationships end up with a different unique identifier but the same
collection name: "Foo Implementations".

The fix is to not rely on the parent relationship being there and instead
look at the origin symbol's parent path components. We can rely on the origin
symbol always being there for protocols defined in the current module.

This also resolves an issue where conforming to compmlicated protocols in a
different module would produce a misnamed collection. For example:

```swift
// FirstTarget

infix operator .<.. : RangeFormationPrecedence
public protocol OtherFancyProtocol {
    static func .<.. (lhs: OtherFancyProtocol, rhs: OtherFancyProtocol)
}

public extension OtherFancyProtocol {
    static func .<.. (lhs: OtherFancyProtocol, rhs: OtherFancyProtocol) {}
}
```

```swift
// SecondTarget
import FirstTarget

public struct OtherFancyProtocolConformer: OtherFancyProtocol {}
```

Here we would end up with a default collection named "< Implementations"
instead of "OtherFancyProtocol Implementations". The solution is to figure
out the actual name of the symbol based on the source symbol which is always
guaranteed to be there. We can use this to split out the parent symbol's name
from the source origin display name.

Resolves rdar://95808950.
@ethan-kusters ethan-kusters added the regression This used to work in earlier versions label Jun 24, 2022
@ethan-kusters ethan-kusters self-assigned this Jun 24, 2022
originParentSymbol = parentOfFunction(originSymbol.reference)
}
let originSymbol = context.symbolIndex[origin.identifier]?.symbol
let sourceSymbol = context.symbolIndex[relationship.source]?.symbol
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is checked above as let child = context.symbolIndex[relationship.source] so it should never be nil.

I think this means that the sourceSymbol argument to add(...) could also be non-optional which removes the need for the else case in that implementation.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh nice catch! Thanks. Addressed here: 976c858.

I think we should still leave the else to reduce risk here in case the child symbol is an unexpected length but this definitely make things clearer.

@ethan-kusters
Copy link
Contributor Author

@swift-ci please test

Copy link
Contributor

@d-ronnqvist d-ronnqvist left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

Copy link
Contributor

@QuietMisdreavus QuietMisdreavus left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice catch, thanks!

@ethan-kusters ethan-kusters merged commit a7c5e25 into swiftlang:main Jun 24, 2022
@ethan-kusters ethan-kusters deleted the duplicate-generated-collections branch June 24, 2022 22:38
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

regression This used to work in earlier versions

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants