zadr / swift-module-diff

SwiftSyntax-based command line tool to analyze and compare Swift module interface through .swiftinterface files

Home Page:https://github.com/zadr/swift-module-diff

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

swift-module-diff

What should this do

Given a pair of Xcode apps (such as Xcode.app and Xcode-beta.app):

  1. Scan through known SDKs (iOS, macOS, watchOS, tvOS, visionOS)
  2. Compile a list of supported architectures and swiftinterface files for each architecture
  3. For each swiftinterface file, parse it with SwiftSyntax
  4. Repeat for the other copy of Xcode
  5. Compare the two results, noting what frameworks are introduced, along with modifications to types, and members (additions, deprecations, etc)
  6. Output some basic HTML to see all the frameworks with modifications on one page
  7. Be able to click into a framework to see all modifications to types, and members (additions, deprecations, etc)

Things Left To Do

Swift is a big and evolving language, so it is likely that edge-cases are broken, or lesser-used features are missing. This tool is still under development, and some known gaps are:

  • default values in function parameters
  • tracking headerdoc changes

And there are some other features that could be nice to have, such as:

  • Using a DSL to generate HTML
  • Making HTML output more readable, such as by:
  • Selectively hiding attributes, or parameters of attributes (i.e. macOS @availability parameter isn't helpful on iOS API diff)
  • Removing framework prefix from type names (such as UIKit. or Swift. in @available(swift) func UIDeviceOrientationIsLandscape(_ orientation: UIKit.UIDeviceOrientation) -> Swift.Bool)
  • Making HTML interactable, such as by linking to more docs, or adding an option to expand all details with one click
  • Filtering output by platform, architecture, or framework
  • Extending CLI to expose diffing swiftmodule directories outside of Xcode.app bundles

But Why?

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

Inspiration

Code Workshop's objc-diff

Notes on development

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.

About

SwiftSyntax-based command line tool to analyze and compare Swift module interface through .swiftinterface files

https://github.com/zadr/swift-module-diff

License:MIT License


Languages

Language:Swift 100.0%