Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

When documenting members of extensions to external protocols, display the external protocol's name #732

Merged

Conversation

patshaughnessy
Copy link
Contributor

@patshaughnessy patshaughnessy commented Oct 5, 2023

Explanation: When documenting members of extensions to external protocols, display the
external protocol's name.
Scope: A small amount of new text appears on documentation pages for one type of Swift symbol.
GitHub Issue: none.
Risk: Low. Minor code changes that only impact how protocol extensions of external protocols are documented.
Testing: Ran the ./bin/test script and it succeeded
Reviewer: @d-ronnqvist

rdar://112219546

Summary

When documenting members of extensions to external protocols, display the
external protocol's name. This is helpful because these pages don’t have the
context of being curated inside their “owning” type or protocol.

Expand the section that we use for conditional constraints to display the
external protocol name. For example, this screenshot shows the documentation for
the stablePartition(by:) symbol in the Swift Algorithms package. Since this
page is curated under "Partitioning and Rotating" it's not obvious that the
function is a member of a protocol extension, or that the protocol extension is
extending the MutableCollection protocol from the Swift Standard Library.

A screenshot of the stablePartitionBy documentation

This code change will now show the external protocol's name,
MutableCollection, just below the declaration:

An updated screenshot of the stablePartitionBy documentation showing the external protocol's name

Note the new text "Available when Self is MutableCollection."

The new "Self is ExternalProtocol" message will be combined with any other
existing generic constraints. For example, uniquePermutations(ofCount:)
already requires that the associated type Element conform to the Hashable
protocol.

A screenshot of the uniquePermutations(ofCount:) documentation

In this screenshot note the combined text "Available when Element conforms to
Hashable and Self is Collection."

Dependencies

This depends on a new initializer added to SymbolKit in swiftlang/swift-docc-symbolkit#63. We need to merge that PR first, before running the CI tests for this, or merging this.

Testing

  • The Swift DocC benchmark command
    did not show any significant performance regressions or differences.

  • Rebuilding the documentation for the Swift Algorithms package causes the names of external protocols to appear as expected.

  • Rebuilding the documentation for other large Swift projects did not introduce any unexpected changes or regressions.

  • Unit tests were added, including a new test bundle ModuleWithProtocolExtensions.docc that includes symbol graph files showing the use of an external protocol.

Checklist

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

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

the external protocol's name. This is helpful because these pages don’t
have the context of being curated inside their “owning” type or
protocol.

Expand the section that we use for conditional constraints to display
the external protocol name, for example: "Self is ExternalProtocol". The
new "Self is ExternalProtocol" message will be combined with any other
existing generic constraints. Do not do this for extended structs,
classes or other extended types, since this generic constraint syntax
wouldn't be accurate for them.

Also as a related bug fix remove the extendedModule property of the
Semanic Symbol class, and instead calculate this based on
swiftExtensionVariants. Same for constraintsVariants. Fix the
initializer of Semantic Symbol, which previously allowed a nil default
value for the extendedModule property, allowing callers to accidentally
leave it nil.
@patshaughnessy
Copy link
Contributor Author

@swift-ci please test

3 similar comments
@patshaughnessy
Copy link
Contributor Author

@swift-ci please test

@patshaughnessy
Copy link
Contributor Author

@swift-ci please test

@patshaughnessy
Copy link
Contributor Author

@swift-ci please test

@patshaughnessy
Copy link
Contributor Author

@swift-ci please test

// Double check the existing extension uses the same module and type. If it does not,
// we must have a tooling or data consistency problem.
assert(
existing.extendedModule == extendedModule, //&& existing.typeKind == typeKind,
Copy link
Contributor

Choose a reason for hiding this comment

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

Should the commented existing.typeKind == typeKind assertion be included or should it be removed?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Oops I had disabled the second condition while debugging something. I added it back.

- typeKind: The ``SymbolGraph/Symbol/KindIdentifier`` of the symbol this extension extends.
- constraint: The new generic constraints to add
- trait: Which generic constraints variant to append to
- Returns: True if the constraint was appended to an existing or new swift extension variant. False if there was an existing swift extension variant for a different module.
Copy link
Contributor

Choose a reason for hiding this comment

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

This function doesn't have a return value.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Removed

// MARK: - Private helpers

/// Return all of this symbol's Swift extension variants.
func swiftExtensionVariants() -> [DocumentationDataVariantsTrait : SymbolGraph.Symbol.Swift.Extension] {
Copy link
Contributor

Choose a reason for hiding this comment

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

If nothing other then Symbol needs to call this we could make it private

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yes thank you. Marked private.

- Remove incorrect doc comment about a return value
- Fix assertion in Symbol#addConstraint
- Symbol#swiftExtensionVariants is private
@patshaughnessy
Copy link
Contributor Author

@swift-ci please test

Comment on lines 352 to 356
public func addConstraint(
extendedModule: String,
typeKind: SymbolGraph.Symbol.KindIdentifier? = nil,
constraint newConstraint: SymbolGraph.Symbol.Swift.GenericConstraint,
trait: DocumentationDataVariantsTrait = DocumentationDataVariantsTrait.swift
Copy link
Contributor

Choose a reason for hiding this comment

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

It's good that you are supporting multiple traits here but I'm wondering if we really need to.

Considering that the added constraint is Swift specific, I think maybe we could remove the trait argument and rename the method to indicate that this is only applicable to Swift. That would allow for the implementation to ignore the other traits which realistically should never have this mixin.

Maybe something like this?

    /// Append a new generic constraint for the given extended module
    /// - Parameters:
    ///    - extendedModule: The name of the extended module.
    ///    - extendedSymbolKind: The kind of the extended symbol.
    ///    - constraint: The new generic constraints to add.
    public func addSwiftExtensionConstraint(
        extendedModule: String,
        extendedSymbolKind: SymbolGraph.Symbol.KindIdentifier? = nil,
        constraint newConstraint: SymbolGraph.Symbol.Swift.GenericConstraint
    ) {

- Rename Symbol#addConstraint to addSwiftExtensionConstraint and assume
the only relevant trait is DocumentationDataVariantsTrait.swift.
- Also rename the typeKind parameter to extendedSymbolKind.
@patshaughnessy
Copy link
Contributor Author

@swift-ci please test

Comment on lines 413 to 414
/// - engine: A diagnostic collecting engine.

Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
/// - engine: A diagnostic collecting engine.
/// - documentationCache: A documentation node lookup by the node's resolved reference.

Comment on lines 1128 to 1130
// This symbol starts with 3 generic constraints. See:

// jq < "Tests/SwiftDocCTests/Test Bundles/TestBundle.docc/mykit-iOS.symbols.json" '.symbols[] | select(.identifier.precise == "s:5MyKit0A5ClassC10myFunctionyyF") | .swiftExtension'
Copy link
Contributor

Choose a reason for hiding this comment

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

I would maybe just remove the "jq" part of the comment.

Suggested change
// This symbol starts with 3 generic constraints. See:
// jq < "Tests/SwiftDocCTests/Test Bundles/TestBundle.docc/mykit-iOS.symbols.json" '.symbols[] | select(.identifier.precise == "s:5MyKit0A5ClassC10myFunctionyyF") | .swiftExtension'
// The original symbol has 3 Swift extension constraints:

let constraints = try XCTUnwrap(withoutArticle.constraints)
XCTAssertEqual(3, constraints.count)
var constraint = constraints[0]
XCTAssertEqual(SymbolGraph.Symbol.Swift.GenericConstraint.Kind.sameType, constraint.kind)
Copy link
Contributor

Choose a reason for hiding this comment

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

FYI: The compiler should be able to infer the types here allowing these assertions to be shorter and more readable

Suggested change
XCTAssertEqual(SymbolGraph.Symbol.Swift.GenericConstraint.Kind.sameType, constraint.kind)
XCTAssertEqual(.sameType, constraint.kind)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

That's great - it must be that since I wrote the expected value first, the compiler/Xcode showed me an error until I finished typing the line. I wonder if it makes more sense to write the actual value first inside of XCTAssertEqual for this reason...

Comment on lines 1190 to 1192
XCTAssertEqual(SymbolGraph.Symbol.Swift.GenericConstraint.Kind.superclass, constraint.kind)
XCTAssertEqual("Observer", constraint.leftTypeName)
XCTAssertEqual("NSObject", constraint.rightTypeName)
Copy link
Contributor

Choose a reason for hiding this comment

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

FYI: The constrain is equatable so you can compare the entire value on one XCTAsserEqual(...) if you want to:

Suggested change
XCTAssertEqual(SymbolGraph.Symbol.Swift.GenericConstraint.Kind.superclass, constraint.kind)
XCTAssertEqual("Observer", constraint.leftTypeName)
XCTAssertEqual("NSObject", constraint.rightTypeName)
XCTAssertEqual(.init(kind: .superclass, leftTypeName: "Observer", rightTypeName: "NSObject"), constraint)

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.

This looks good to me.

I will just point out that this is a small source breaking change because the setters for
SwiftDocC/Symbol/constraintsVariants and SwiftDocC/Symbol/constraints are removed.

However, because assigning new values to those properties didn't really work before I think it's acceptable to remove them.

If we wanted we could add them back, mark them as deprecated, and assert in debug builds.

- Correct doc comment
- Clean up/refactor unit test
@patshaughnessy
Copy link
Contributor Author

@swift-ci please test

@patshaughnessy
Copy link
Contributor Author

@swift-ci please test

@patshaughnessy
Copy link
Contributor Author

@swift-ci please test

@patshaughnessy patshaughnessy merged commit 04be889 into swiftlang:main Oct 17, 2023
2 checks passed
@patshaughnessy patshaughnessy deleted the display-extended-protocol-names branch October 17, 2023 23:40
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants