Given a pair of Xcode apps (such as Xcode.app and Xcode-beta.app):
- Scan through known SDKs (iOS, macOS, watchOS, tvOS, visionOS)
- Compile a list of supported architectures and
swiftinterfacefiles for each architecture - For each
swiftinterfacefile, parse it with SwiftSyntax - Repeat for the other copy of Xcode
- Compare the two results, noting what frameworks are introduced, along with modifications to types, and members (additions, deprecations, etc)
- Output some basic HTML to see all the frameworks with modifications on one page
- Be able to click into a framework to see all modifications to types, and members (additions, deprecations, etc)
Swift is a big and evolving language, so it is likely that some edge-cases are broken, or some less popular features are missing.
There are some other features that could be nice to have, such as:
- Tracking headerdoc changes
- Making HTML interactable, such as by linking to more docs, or adding an option to expand all details with one click
- Extending CLI to expose diffing swiftmodule directories outside of Xcode.app bundles, e.g. to support other frameworks
Having everything that changed one one page can be nice
If more information than base additions / deprecations / removals is needed, official documentation is always available
Code Workshop's objc-diff
As a hypothetical, consider adding support for a new magic keyword that can appear before func declarations in class contexts.
Starting with a new Magic.swiftmodule containing the following:
class C {
magic func x(y: Int = 1) -> Int { 0 }
}
Running with --single-file flag set: swift-module-diff --single-file=/path/to/Magic.swiftmodule will parse a single file.
From there, breakpoints make for a quick way to the AST. For example, in FunctionTracker.visit(_ node: FunctionDeclSyntax) -> SyntaxVisitorContinueKind,
running vo node will produce something that looks like this:
(lldb) vo node
(SwiftSyntax.FunctionDeclSyntax) node = FunctionDeclSyntax
├─attributes: AttributeListSyntax
├─modifiers: DeclModifierListSyntax
│ ╰─[0]: DeclModifierSyntax
│ ╰─name: keyword(SwiftSyntax.Keyword.magic)
├─funcKeyword: keyword(SwiftSyntax.Keyword.func)
├─name: identifier("x")
├─signature: FunctionSignatureSyntax
│ ├─parameterClause: FunctionParameterClauseSyntax
│ │ ├─leftParen: leftParen
│ │ ├─parameters: FunctionParameterListSyntax
│ │ │ ╰─[0]: FunctionParameterSyntax
│ │ │ ├─attributes: AttributeListSyntax
│ │ │ ├─modifiers: DeclModifierListSyntax
│ │ │ ├─firstName: identifier("y")
│ │ │ ├─colon: colon
│ │ │ ├─type: IdentifierTypeSyntax
│ │ │ │ ╰─name: identifier("Int")
│ │ │ ╰─defaultValue: InitializerClauseSyntax
│ │ │ ├─equal: equal
│ │ │ ╰─value: IntegerLiteralExprSyntax
│ │ │ ╰─literal: integerLiteral("1")
│ │ ╰─rightParen: rightParen
│ ╰─returnClause: ReturnClauseSyntax
│ ├─arrow: arrow
│ ╰─type: IdentifierTypeSyntax
│ ╰─name: identifier("Int")
╰─body: CodeBlockSyntax
├─leftBrace: leftBrace
├─statements: CodeBlockItemListSyntax
│ ╰─[0]: CodeBlockItemSyntax
│ ╰─item: IntegerLiteralExprSyntax
│ ╰─literal: integerLiteral("0")
╰─rightBrace: rightBrace
which shows that magic is a declaration modifier.
swift-module-diff tracks member types (functions, vars, typealiases, and so on) with the Member type, and Member includes a Decorator enum with results stored in a decorators set.
All together, the results will look something like this commit that added support for tracking optional conformances of members within protocols.