PSA: Soundness checks and disambiguation of overloaded symbols

More of a public service announcement than anything else, but wanted to share - although hopefully this is of time limited value.

A number of the repositories in the swiftlang GitHub organization are now using “soundness checks” for documentation. (the relevant workflow configuration and its related script). The heart of this check runs a documentation build that treats warnings from the DocC compiler as errors, and is quite easy to validate locally.

The equivalent command, which uses the swift-docc-plugin:

swift package generate-documentation --analyze --warnings-as-errors

If you don’t have the swift-docc-plugin enabled in your package, you can add it quickly with:

swift package add-dependency https://github.com/swiftlang/swift-docc-plugin --from 1.0.0

The current default for the toolchain version that’s used for the documentation checks is, as I’m writing this, 6.0 - and this is configurable by any repo that uses the soundness check workflow.

The tricky part I ran into recently was when I used Xcode as my editor, and took advantage of the command-line completion for the symbols. The symbols used to disambiguate overloaded methods for DocC changed between 6.0 and 6.1, shifting from relatively-hard-to-find consistent hash values to a more verbose, but far easier to read mechanism that reflects the overloaded types.

So - if you’re working on documentation today and it’s being checked with 6.0, those new disambiguations will not pass the soundness checks - nor will they generate documentation properly with a 6.0 toolchain. As I’m sure you’ve already guessed, using the newer disambiguation techniques definitely fails a soundness check that’s run with Swift 6.0.

If you need to find the legacy disambiguation hashes, they’re available - but it’s a bit of digging. The technique I’ve been using is the following:

  • generate the documentation with the addition of the –-emit-digest parameter. For example:
swift package generate-documentation --emit-digest

Within the resulting documentation archive, there’s a file called linkable-entities.json that has the needed detail. I’ve taken to dumping out the whole list of all the symbols, sorted, to make them easier to reference:

cat .build/plugins/Swift-DocC/outputs/MyPackage.doccarchive/linkable-entities.json | jq '.[].referenceURL' | sort

It’s still a bit of guesswork as to which overrides are which hashes, so that’s a bit more trial and error when curating (organizing) any lists of symbols, but comparatively quick to work through.

For your information: the way that this is run as an automated check it doesn't make any difference to include the --analyze flag.

The --analyze flag is an older, and less clear, spelling of --diagnostic-level information which configures DocC to emit "information" level diagnostics in the output. However, the --warnings-as-errors flag only raises the severity of "warning" level diagnostics to "error" severity. It doesn't change the severity of "information" level diagnostics.

In other words, this combination of flags only causes DocC to emit some optional information messages that the linked soundness check script never looks at.

1 Like

Would it make sense to either update these workflows to use a Swift 6.1 toolchain or maybe even to build the docc executable from source and use it with the DOCC_EXEC environmental variable so that these soundness checks can use the latest features and have the latest bug fixes available?

I’d expect them to update at some point, but I don’t think there’s a current value that’s taken as a “N releases back” that as a community we try and support. I think that’s something the ecosystem team is talking about proposing, but nothing is out there yet. When that’s pinned down, I’d expect that perhaps one of the release tasks would be to update the various workflow defaults.

In the meantime, this is entirely configurable per repo - the swiftlang worklows are very nicely set up that way, so if any repo wants to use it, they can, but since it’s the current default, I at least wanted to call it out and provide a path to work around the transition.