Skip to content

Show warning for Swiftly/Xcode toolchain mismatch failures#2109

Open
Flamki wants to merge 3 commits intoswiftlang:mainfrom
Flamki:fix/swiftly-xcode-toolchain-mismatch-warning-2075
Open

Show warning for Swiftly/Xcode toolchain mismatch failures#2109
Flamki wants to merge 3 commits intoswiftlang:mainfrom
Flamki:fix/swiftly-xcode-toolchain-mismatch-warning-2075

Conversation

@Flamki
Copy link

@Flamki Flamki commented Feb 18, 2026

Description

This PR fixes issue #2075 by detecting likely version mismatches between Swiftly-managed Swift toolchains and the selected Xcode toolchain, then showing a targeted warning with clear next actions.

In mismatch scenarios users can currently see cryptic build/package errors. With this change, the extension now surfaces a warning that explains the likely cause and suggests resolution paths:

  • Select a different toolchain
  • Open supported toolchains documentation

Issue: #2075

What Changed

  • Added toolchainMismatch detection/warning module:
    • detectSwiftlyXcodeToolchainMismatch(...)
    • maybeShowSwiftlyXcodeToolchainMismatchWarning(...)
  • Added mismatch warning trigger in Swift package loading error path.
  • Added mismatch warning trigger for Swift task output (build/test/run) via diagnostics parsing flow.
  • Added one-time-per-folder/session warning behavior to avoid repeated prompts.
  • Added unit tests for:
    • positive/negative mismatch detection cases
    • warning actions (Select Toolchain, Open Documentation)
    • additional failure patterns (swift-frontend command failed, differing swiftlang-* versions)

Tasks

  • Required tests have been written
  • Documentation has been updated (Not applicable for this internal behavior change)
  • Added an entry to CHANGELOG.md if applicable (Not applicable; can be included at release aggregation time)

Testing

  • npm run lint
  • npm run unit-test

Both commands pass locally.

@Flamki
Copy link
Author

Flamki commented Feb 18, 2026

Implemented a fix for this in PR #2109.

Approach taken:

  1. Detect likely Swiftly/Xcode mismatch failures
  • Added a best-effort detector that looks for:
    • mixed Swiftly + Xcode toolchain evidence in output (for example .swiftly/toolchains and XcodeDefault.xctoolchain)
    • mismatch/failure signals (for example “different version of the compiler”, “failed to build module”, swift-frontend command failed)
    • differing swiftlang-* version strings in failure output
  1. Show a targeted warning with actionable guidance
  • When detected, the extension shows a warning explaining likely toolchain version mismatch and suggests:
    • Select Toolchain
    • Open Documentation (supported toolchains docs)
  1. Cover both package loading and build/test/run flows
  • Hooked warning into:
    • Swift package state loading failures (swift package describe path)
    • Swift task output parsing flow used by build/test/run diagnostics
  1. Avoid notification spam
  • Warning is deduplicated to show at most once per folder per extension session.
  1. Added tests
  • Unit tests for positive/negative detection cases and warning action behavior.

This should replace many currently cryptic mismatch errors with a clear, guided warning for users.

Copy link
Contributor

@rbenegal rbenegal left a comment

Choose a reason for hiding this comment

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

Thanks very much for this @Flamki! I think having something like this would be very helpful for folks who have multiple toolchains installed.

Left some comments for your thoughts, and I think it would be great if @matthewbastien also took a look at this and share some thoughts.

lowerOutput.includes("xcodedefault.xctoolchain") ||
lowerOutput.includes("/applications/xcode") ||
lowerOutput.includes("contents/developer/toolchains");
const hasVersionMismatchSignal = [
Copy link
Contributor

Choose a reason for hiding this comment

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

Curious to know how you came up with this list. Is this based on actual diagnostics that are emitted by the compiler? Wondering if it would be best to always rely on different version numbers as a signal.

Copy link
Author

Choose a reason for hiding this comment

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

Great question. The list came from real mismatch failure logs we saw (module/compiler version errors, swift-frontend failures, mixed toolchain paths), plus conservative fallbacks. I kept swiftlang-* version mismatch as a strong signal, but not the only signal, because many real logs don’t always include two parseable version strings. I can tighten this further toward version-first if maintainers prefer.

let lastDiagnosticNeedsSaving = false;
disposables.push(
swiftExecution.onDidWrite(data => {
const sanitizedData = (remainingData || "") + stripAnsi(data);
Copy link
Contributor

Choose a reason for hiding this comment

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

Is there a reason this portion had to change?

Copy link
Author

Choose a reason for hiding this comment

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

Yes, this change is intentional. I needed the raw task output chunk (stripAnsi(data)) in a bounded buffer so we can run mismatch detection at task completion for build/test/run failures. Without this, we only catch package-load errors and miss the most common runtime/task failure path.

});
}

private folderContextForTask(task: vscode.Task): FolderContext | undefined {
Copy link
Contributor

Choose a reason for hiding this comment

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

Also not sure why this folderContextForTask portion is required here. Is this being used to ensure that the warning is not shown again for the same folder somehow?

Copy link
Author

Choose a reason for hiding this comment

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

Exactly. folderContextForTask ensures we map a task failure to the correct folder in multi-root workspaces, so warning dedupe is per-folder and the detector uses the right toolchain manager (swiftly vs others). Relying on currentFolder can point to the wrong workspace folder during background task runs.

let outputBuffer = "";
const done = () => {
if (folderContext && outputBuffer.length > 0) {
maybeShowSwiftlyXcodeToolchainMismatchWarning(outputBuffer, folderContext);
Copy link
Contributor

Choose a reason for hiding this comment

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

This is very interesting thanks! I was not thinking of parsing diagnostics, initially was just thinking we could base it on detecting different version numbers, but I think a problem that was brought up with that is sometimes the builds will work fine even if there is a slight mismatch (i.e. modules built with an older version that works fine with newer versions) so perhaps this approach is better.

@Flamki
Copy link
Author

Flamki commented Feb 20, 2026

@rbenegal Pushed a follow-up commit to this PR: 99ca1e3.

What changed:

  • Ran Prettier on touched files (src/DiagnosticsManager.ts, src/SwiftPackage.ts, src/toolchain/toolchainMismatch.ts) so CI format checks in the Linux test workflow should no longer fail on style.

Responses to review questions:

  • On the detection signal list in toolchainMismatch.ts: this list is based on common real-world compiler/module failure text seen in mismatch reports (eg different version of the compiler, failed to build module, swift-frontend command failed). I kept version-string mismatch as a strong signal too, but not the only one, because some failing logs do not always include both parseable swiftlang-* versions.
  • On the DiagnosticsManager chunk/output buffering changes: this is required to detect mismatch patterns during build/test/run task failures (not only package-load failures). The buffering is bounded (256KB) and only used to trigger the warning detector.
  • On folderContextForTask: this is needed so warnings are scoped/deduped per folder and use the correct toolchain manager in multi-root workspaces; relying only on currentFolder can be wrong when tasks run from a different workspace folder.

I’ll keep an eye on the re-run checks and adjust further if anything else pops up.

@Flamki
Copy link
Author

Flamki commented Feb 24, 2026

Merged swiftlang/main into this branch and resolved the conflict in src/DiagnosticsManager.ts in commit f987e88.

For the conflict resolution, I kept both important parts:

  • the Swiftly/Xcode mismatch warning path (buffering task output and mapping with folderContextForTask(...))
  • the upstream diagnostics parsing refactor (handleRelatedInformation, isDuplicateRelatedInfo, handleParsedDiagnostic)

I also ran Prettier on the merged file.

Local checks after resolving:

  • npm run format
  • npm run lint

Both pass locally.

@Flamki
Copy link
Author

Flamki commented Feb 24, 2026

@rbenegal merged latest swiftlang/main and resolved the DiagnosticsManager conflict in f987e88 while preserving both the mismatch-warning flow and the upstream diagnostics refactor. Could you please re-review when convenient?

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