Fix clang-format/clang-tidy version check failing on Windows (and paths with spaces) by sean-mcmanus · Pull Request #14552 · microsoft/vscode-cpptools · GitHub
Skip to content

Fix clang-format/clang-tidy version check failing on Windows (and paths with spaces)#14552

Open
sean-mcmanus wants to merge 2 commits into
mainfrom
seanmcm/fixClangVersionChecking
Open

Fix clang-format/clang-tidy version check failing on Windows (and paths with spaces)#14552
sean-mcmanus wants to merge 2 commits into
mainfrom
seanmcm/fixClangVersionChecking

Conversation

@sean-mcmanus

@sean-mcmanus sean-mcmanus commented Jun 27, 2026

Copy link
Copy Markdown
Contributor

Fixed using Copilot with Claude Opus 4.8 in VS Code (after reproing/debugging it).

Summary: It was using the older Program Files clang-format instead of the newer bundled one.

Problem

When resolving the clang-format / clang-tidy path, the extension shells out to the bundled binary to read its version (getClangPath in settings.ts). On Windows this could throw:

Error: Command failed: 'c:\...\Extension\LLVM\bin\clang-format.exe' --version
The filename, directory name, or volume label syntax is incorrect.

The failure happened in the bundled-binary try block, silently falling through to the catch ("Unable to invoke our own clang-*") and skipping the bundled-vs-system version comparison entirely.

Root cause

The bundled invocation used shell-quote's quote() to build the command string:

execSync(quote([bundledPath, '--version']))

quote() produces POSIX-style single-quoted output ('c:\...\clang-format.exe' --version). On Windows, execSync runs the command through cmd.exe, which does not accept single quotes for quoting, so the command failed. The binary itself was fine — only the quoting was wrong.

quote() was originally added in #11841 ("Address issues reported by compliance tooling") to stop building a shell command from an unescaped variable. So we cannot simply revert to raw string interpolation.

Fix

Switch both version-probe invocations from a shell command (execSync + quoting) to execFileSync, which invokes the executable directly with an argument array and no shell:

execFileSync(bundledPath, ['--version'])
execFileSync(path, ['--version'])

Because no shell is involved:

  • No quoting is required, so the Windows single-quote bug cannot occur.
  • Paths containing spaces (and other special characters) work correctly on Windows, Linux, and macOS.
  • The original compliance concern that motivated quote() is addressed more directly — there is no shell command constructed from a variable, which is the canonical remediation for that class of finding.

The shell-quote import is removed since it is no longer used.

What is preserved

  • The flexible regex-based version extraction from clang-format/tidy version comparisons fail for some builds #12813 ("clang-format/tidy version comparisons fail for some builds") is unchanged: output.match(/(\d+\.\d+\.\d+)/)?.[1] on both paths. That scenario does not regress.
  • The try/catch structure is unchanged. getExtensionFilePath() stays inside the try, so if the extension path is not yet initialized and it throws, the error is still caught and we fall back to the system clang-* gracefully.
  • Behavior is otherwise identical: parse the bundled version, compare against the system version, and prefer the newer one; fall back to the system binary if the bundled one can't be invoked or parsed.

Testing

  • Verified the bundled and system --version calls succeed on Windows where the previous single-quoted command failed.
  • Confirmed paths containing spaces no longer break the invocation.
  • No functional change to the version-comparison logic.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

This PR fixes Windows (and spaces-in-path) failures when probing clang-format/clang-tidy versions by switching from shell-based execution (execSync + quoting) to direct execution (execFileSync), ensuring the bundled LLVM binaries are correctly version-compared against system installs.

Changes:

  • Replaces execSync command strings with execFileSync(..., ['--version']) for both bundled and system version probes.
  • Removes the shell-quote dependency from settings.ts since shell quoting is no longer needed.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread Extension/src/LanguageServer/settings.ts
@sean-mcmanus sean-mcmanus added this to the 1.33.3 milestone Jun 27, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: Pull Request

Development

Successfully merging this pull request may close these issues.

2 participants