Skip to content

Conversation

@davbeck
Copy link

@davbeck davbeck commented Dec 11, 2025

This PR adds a new feature swift.emit_diagnostics that enables the Swift compiler to output serialized diagnostics files (.dia). These machine-readable files can be consumed by IDEs, migration tools, and other static analysis utilities.

What I'm using this for at the moment is to replicate the behavior of swift package migrate (see https://github.com/swiftlang/swift-package-manager/blob/main/Sources/Commands/PackageCommands/Migrate.swift). The .dia files contain fixits and categories so you can use them to automatically apply fixits for a specific category of warnings.

I think these files have the potential to be useful in other ways too. For instance these could be connected to Xcode (or another IDE), which was mentioned in #304. Or they could be used to give AI better context similar to what is discussed in https://tuist.dev/blog/2025/11/27/teaching-ai-to-read-xcode-builds.

Full disclosure: most of these changes were made using Cursor and only verified on 1 project. I pieced together the input for the compiler based on source code in the swift package manager.

@google-cla
Copy link

google-cla bot commented Dec 11, 2025

Thanks for your pull request! It looks like this may be your first contribution to a Google open source project. Before we can look at your pull request, you'll need to sign a Contributor License Agreement (CLA).

View this failed invocation of the CLA check for more information.

For the most up to date status, view the checks section at the bottom of the pull request.

@adincebic
Copy link
Contributor

@davbeck Do these files contain absolute paths or something user / host specific?

@davbeck
Copy link
Author

davbeck commented Dec 11, 2025

They are relative to the project root. My understanding is that swift uses the path from the file map and since rules_swift uses paths relative to the project, the diagnostic files contain relative paths.

Here's an example:

[Target].output_file_map.json:

  "Apps/Dasher/Layers/Foundation/Core/Sources/Retry/RetryAsync.swift": {
    "ast-dump": "bazel-out/darwin_arm64-fastbuild/bin/Apps/Dasher/Layers/Foundation/Core/Core_objs/Sources/Retry/RetryAsync.swift.ast",
    "const-values": "bazel-out/darwin_arm64-fastbuild/bin/Apps/Dasher/Layers/Foundation/Core/Core_objs/Sources/Retry/RetryAsync.swift.swiftconstvalues",
    "diagnostics": "bazel-out/darwin_arm64-fastbuild/bin/Apps/Dasher/Layers/Foundation/Core/Core_objs/Sources/Retry/RetryAsync.swift.dia",
    "object": "bazel-out/darwin_arm64-fastbuild/bin/Apps/Dasher/Layers/Foundation/Core/Core_objs/Sources/Retry/RetryAsync.swift.o"
  },

Will produce a diagnostic file like this:

(lldb) po diagnostics
▿ FlattenSequence<Array<Array<Diagnostic>>>
  ▿ _base : 1 element
    ▿ 0 : 3 elements
      ▿ 0 : Diagnostic
        - text : "feature \'NonisolatedNonsendingByDefault\' will cause nonisolated async function type to be treated as specified to run on the caller\'s actor; use \'@concurrent\' to preserve behavior"
        - level : TSCUtility.SerializedDiagnostics.Diagnostic.Level.warning
        ▿ location : Optional<SourceLocation>
          ▿ some : Apps/Dasher/Layers/Foundation/Core/Sources/Retry/RetryAsync.swift:141:33
            - filename : "Apps/Dasher/Layers/Foundation/Core/Sources/Retry/RetryAsync.swift"
            - line : 141
            - column : 33
            - offset : 6094
        ▿ category : Optional<String>
          - some : "NonisolatedNonsendingByDefault"
        ▿ categoryURL : Optional<String>
          - some : "https://docs.swift.org/compiler/documentation/diagnostics/nonisolated-nonsending-by-default"
        ▿ flag : Optional<String>
          - some : "https://docs.swift.org/compiler/documentation/diagnostics/nonisolated-nonsending-by-default"
        - ranges : 0 elements
        ▿ fixIts : 1 element
          ▿ 0 : FixIt
            ▿ start : Apps/Dasher/Layers/Foundation/Core/Sources/Retry/RetryAsync.swift:141:33
              - filename : "Apps/Dasher/Layers/Foundation/Core/Sources/Retry/RetryAsync.swift"
              - line : 141
              - column : 33
              - offset : 6094
            ▿ end : Apps/Dasher/Layers/Foundation/Core/Sources/Retry/RetryAsync.swift:141:33
              - filename : "Apps/Dasher/Layers/Foundation/Core/Sources/Retry/RetryAsync.swift"
              - line : 141
              - column : 33
              - offset : 6094
            - text : "@concurrent "
      ▿ 1 : Diagnostic
        - text : "feature \'NonisolatedNonsendingByDefault\' will cause nonisolated async function type to be treated as specified to run on the caller\'s actor; use \'@concurrent\' to preserve behavior"
        - level : TSCUtility.SerializedDiagnostics.Diagnostic.Level.warning
        ▿ location : Optional<SourceLocation>
          ▿ some : Apps/Dasher/Layers/Foundation/Core/Sources/Retry/RetryAsync.swift:142:38
            - filename : "Apps/Dasher/Layers/Foundation/Core/Sources/Retry/RetryAsync.swift"
            - line : 142
            - column : 38
            - offset : 6153
        ▿ category : Optional<String>
          - some : "NonisolatedNonsendingByDefault"
        ▿ categoryURL : Optional<String>
          - some : "https://docs.swift.org/compiler/documentation/diagnostics/nonisolated-nonsending-by-default"
        ▿ flag : Optional<String>
          - some : "https://docs.swift.org/compiler/documentation/diagnostics/nonisolated-nonsending-by-default"
        - ranges : 0 elements
        ▿ fixIts : 1 element
          ▿ 0 : FixIt
            ▿ start : Apps/Dasher/Layers/Foundation/Core/Sources/Retry/RetryAsync.swift:142:38
              - filename : "Apps/Dasher/Layers/Foundation/Core/Sources/Retry/RetryAsync.swift"
              - line : 142
              - column : 38
              - offset : 6153
            ▿ end : Apps/Dasher/Layers/Foundation/Core/Sources/Retry/RetryAsync.swift:142:38
              - filename : "Apps/Dasher/Layers/Foundation/Core/Sources/Retry/RetryAsync.swift"
              - line : 142
              - column : 38
              - offset : 6153
            - text : "@concurrent "
      ▿ 2 : Diagnostic
        - text : "feature \'NonisolatedNonsendingByDefault\' will cause nonisolated async global function \'retry\' to run on the caller\'s actor; use \'@concurrent\' to preserve behavior"
        - level : TSCUtility.SerializedDiagnostics.Diagnostic.Level.warning
        ▿ location : Optional<SourceLocation>
          ▿ some : Apps/Dasher/Layers/Foundation/Core/Sources/Retry/RetryAsync.swift:140:13
            - filename : "Apps/Dasher/Layers/Foundation/Core/Sources/Retry/RetryAsync.swift"
            - line : 140
            - column : 13
            - offset : 5994
        ▿ category : Optional<String>
          - some : "NonisolatedNonsendingByDefault"
        ▿ categoryURL : Optional<String>
          - some : "https://docs.swift.org/compiler/documentation/diagnostics/nonisolated-nonsending-by-default"
        ▿ flag : Optional<String>
          - some : "https://docs.swift.org/compiler/documentation/diagnostics/nonisolated-nonsending-by-default"
        - ranges : 0 elements
        ▿ fixIts : 1 element
          ▿ 0 : FixIt
            ▿ start : Apps/Dasher/Layers/Foundation/Core/Sources/Retry/RetryAsync.swift:139:1
              - filename : "Apps/Dasher/Layers/Foundation/Core/Sources/Retry/RetryAsync.swift"
              - line : 139
              - column : 1
              - offset : 5963
            ▿ end : Apps/Dasher/Layers/Foundation/Core/Sources/Retry/RetryAsync.swift:139:1
              - filename : "Apps/Dasher/Layers/Foundation/Core/Sources/Retry/RetryAsync.swift"
              - line : 139
              - column : 1
              - offset : 5963
            - text : "@concurrent "

This is different from the files that swift package manager creates because it uses absolute paths in it output file map.

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.

2 participants