From 06ad05a68b1b6c2d515ef496728e8a780fed0a0e Mon Sep 17 00:00:00 2001 From: Sean McManus Date: Fri, 28 Aug 2026 13:29:51 -0700 Subject: [PATCH 1/3] Update clang-tidy checks to 23.1.0 (#14713) --- Extension/package.json | 138 +++++++++---------- Extension/src/LanguageServer/codeAnalysis.ts | 2 +- 2 files changed, 67 insertions(+), 73 deletions(-) diff --git a/Extension/package.json b/Extension/package.json index 004ee8436..8e4db4ca7 100644 --- a/Extension/package.json +++ b/Extension/package.json @@ -2357,7 +2357,9 @@ "bugprone-*", "bugprone-argument-comment", "bugprone-assert-side-effect", + "bugprone-assignment-*", "bugprone-assignment-in-if-condition", + "bugprone-assignment-in-selection-statement", "bugprone-bad-signal-to-kill-thread", "bugprone-bitwise-pointer-cast", "bugprone-bool-pointer-implicit-conversion", @@ -2402,6 +2404,7 @@ "bugprone-misplaced-operator-in-strlen-in-alloc", "bugprone-misplaced-pointer-arithmetic-in-alloc", "bugprone-misplaced-widening-cast", + "bugprone-missing-end-comparison", "bugprone-move-forwarding-reference", "bugprone-multi-level-implicit-pointer-conversion", "bugprone-multiple-*", @@ -2423,12 +2426,16 @@ "bugprone-return-const-ref-from-parameter", "bugprone-shared-ptr-array-mismatch", "bugprone-signal-handler", + "bugprone-signed-*", + "bugprone-signed-bitwise", "bugprone-signed-char-misuse", "bugprone-sizeof-*", "bugprone-sizeof-container", "bugprone-sizeof-expression", "bugprone-spuriously-wake-up-functions", "bugprone-standalone-empty", + "bugprone-std-*", + "bugprone-std-exception-baseclass", "bugprone-std-namespace-modification", "bugprone-string-*", "bugprone-string-constructor", @@ -2458,11 +2465,14 @@ "bugprone-undefined-memory-manipulation", "bugprone-undelegated-constructor", "bugprone-unhandled-*", + "bugprone-unhandled-code-paths", "bugprone-unhandled-exception-at-new", "bugprone-unhandled-self-assignment", "bugprone-unintended-char-ostream-output", "bugprone-unique-ptr-array-mismatch", + "bugprone-unsafe-*", "bugprone-unsafe-functions", + "bugprone-unsafe-to-allow-exceptions", "bugprone-unused-*", "bugprone-unused-local-non-trivial-variable", "bugprone-unused-raii", @@ -2492,7 +2502,9 @@ "cert-err58-cpp", "cert-err60-cpp", "cert-err61-cpp", + "cert-exp*", "cert-exp42-c", + "cert-exp45-c", "cert-fio38-c", "cert-flp*", "cert-flp30-c", @@ -2654,6 +2666,8 @@ "cppcoreguidelines-avoid-non-const-global-variables", "cppcoreguidelines-avoid-reference-coroutine-parameters", "cppcoreguidelines-c-copy-assignment-signature", + "cppcoreguidelines-explicit-*", + "cppcoreguidelines-explicit-constructor", "cppcoreguidelines-explicit-virtual-functions", "cppcoreguidelines-init-variables", "cppcoreguidelines-interfaces-global-init", @@ -2732,45 +2746,10 @@ "google-runtime-int", "google-runtime-operator", "google-upgrade-googletest-case", - "hicpp-*", - "hicpp-avoid-*", - "hicpp-avoid-c-arrays", - "hicpp-avoid-goto", - "hicpp-braces-around-statements", - "hicpp-deprecated-headers", - "hicpp-exception-baseclass", - "hicpp-explicit-conversions", - "hicpp-function-size", - "hicpp-ignored-remove-result", - "hicpp-invalid-access-moved", - "hicpp-member-init", - "hicpp-move-const-arg", - "hicpp-multiway-paths-covered", - "hicpp-named-parameter", - "hicpp-new-delete-operators", - "hicpp-no-*", - "hicpp-no-array-decay", - "hicpp-no-assembler", - "hicpp-no-malloc", - "hicpp-noexcept-move", - "hicpp-signed-bitwise", - "hicpp-special-member-functions", - "hicpp-static-assert", - "hicpp-undelegated-constructor", - "hicpp-uppercase-literal-suffix", - "hicpp-use-*", - "hicpp-use-auto", - "hicpp-use-emplace", - "hicpp-use-equals-*", - "hicpp-use-equals-default", - "hicpp-use-equals-delete", - "hicpp-use-noexcept", - "hicpp-use-nullptr", - "hicpp-use-override", - "hicpp-vararg", "linuxkernel-must-check-errs", "llvm-*", "llvm-else-after-return", + "llvm-formatv-string", "llvm-header-guard", "llvm-include-order", "llvm-namespace-comment", @@ -2779,10 +2758,13 @@ "llvm-prefer-register-over-unsigned", "llvm-prefer-static-over-anonymous-namespace", "llvm-qualified-auto", + "llvm-redundant-casting", "llvm-twine-local", + "llvm-type-switch-case-types", "llvm-use-*", "llvm-use-new-mlir-op-builder", "llvm-use-ranges", + "llvm-use-vector-utils", "llvmlibc-*", "llvmlibc-callee-namespace", "llvmlibc-implementation-in-namespace", @@ -2794,6 +2776,7 @@ "misc-const-correctness", "misc-coroutine-hostile-raii", "misc-definitions-in-headers", + "misc-explicit-constructor", "misc-header-include-cycle", "misc-include-cleaner", "misc-misleading-*", @@ -2809,7 +2792,9 @@ "misc-override-with-different-visibility", "misc-predictable-rand", "misc-redundant-expression", + "misc-static-*", "misc-static-assert", + "misc-static-initialization-cycle", "misc-throw-by-value-catch-by-reference", "misc-unconventional-assign-operator", "misc-uniqueptr-reset-release", @@ -2869,9 +2854,12 @@ "modernize-use-scoped-lock", "modernize-use-starts-ends-with", "modernize-use-std-*", + "modernize-use-std-bit", "modernize-use-std-format", "modernize-use-std-numbers", "modernize-use-std-print", + "modernize-use-string-view", + "modernize-use-structured-binding", "modernize-use-trailing-return-type", "modernize-use-transparent-functors", "modernize-use-uncaught-exceptions", @@ -2912,13 +2900,17 @@ "performance-noexcept-destructor", "performance-noexcept-move-constructor", "performance-noexcept-swap", + "performance-prefer-single-char-overloads", + "performance-string-view-conversions", "performance-trivially-destructible", "performance-type-promotion-in-math-fn", "performance-unnecessary-*", "performance-unnecessary-copy-initialization", "performance-unnecessary-value-param", + "performance-use-std-move", "portability-*", "portability-avoid-pragma-once", + "portability-no-assembler", "portability-restrict-system-includes", "portability-simd-intrinsics", "portability-std-allocator-const", @@ -2968,9 +2960,12 @@ "readability-redundant-declaration", "readability-redundant-function-ptr-dereference", "readability-redundant-inline-specifier", + "readability-redundant-lambda-parameter-list", "readability-redundant-member-init", + "readability-redundant-nested-if", "readability-redundant-parentheses", "readability-redundant-preprocessor", + "readability-redundant-qualified-alias", "readability-redundant-smartptr-get", "readability-redundant-string-*", "readability-redundant-string-cstr", @@ -2985,6 +2980,8 @@ "readability-static-definition-in-anonymous-namespace", "readability-string-compare", "readability-suspicious-call-argument", + "readability-trailing-comma", + "readability-trivial-switch", "readability-uniqueptr-delete-release", "readability-uppercase-literal-suffix", "readability-use-*", @@ -3061,7 +3058,9 @@ "bugprone-*", "bugprone-argument-comment", "bugprone-assert-side-effect", + "bugprone-assignment-*", "bugprone-assignment-in-if-condition", + "bugprone-assignment-in-selection-statement", "bugprone-bad-signal-to-kill-thread", "bugprone-bitwise-pointer-cast", "bugprone-bool-pointer-implicit-conversion", @@ -3106,6 +3105,7 @@ "bugprone-misplaced-operator-in-strlen-in-alloc", "bugprone-misplaced-pointer-arithmetic-in-alloc", "bugprone-misplaced-widening-cast", + "bugprone-missing-end-comparison", "bugprone-move-forwarding-reference", "bugprone-multi-level-implicit-pointer-conversion", "bugprone-multiple-*", @@ -3127,12 +3127,16 @@ "bugprone-return-const-ref-from-parameter", "bugprone-shared-ptr-array-mismatch", "bugprone-signal-handler", + "bugprone-signed-*", + "bugprone-signed-bitwise", "bugprone-signed-char-misuse", "bugprone-sizeof-*", "bugprone-sizeof-container", "bugprone-sizeof-expression", "bugprone-spuriously-wake-up-functions", "bugprone-standalone-empty", + "bugprone-std-*", + "bugprone-std-exception-baseclass", "bugprone-std-namespace-modification", "bugprone-string-*", "bugprone-string-constructor", @@ -3162,11 +3166,14 @@ "bugprone-undefined-memory-manipulation", "bugprone-undelegated-constructor", "bugprone-unhandled-*", + "bugprone-unhandled-code-paths", "bugprone-unhandled-exception-at-new", "bugprone-unhandled-self-assignment", "bugprone-unintended-char-ostream-output", "bugprone-unique-ptr-array-mismatch", + "bugprone-unsafe-*", "bugprone-unsafe-functions", + "bugprone-unsafe-to-allow-exceptions", "bugprone-unused-*", "bugprone-unused-local-non-trivial-variable", "bugprone-unused-raii", @@ -3196,7 +3203,9 @@ "cert-err58-cpp", "cert-err60-cpp", "cert-err61-cpp", + "cert-exp*", "cert-exp42-c", + "cert-exp45-c", "cert-fio38-c", "cert-flp*", "cert-flp30-c", @@ -3358,6 +3367,8 @@ "cppcoreguidelines-avoid-non-const-global-variables", "cppcoreguidelines-avoid-reference-coroutine-parameters", "cppcoreguidelines-c-copy-assignment-signature", + "cppcoreguidelines-explicit-*", + "cppcoreguidelines-explicit-constructor", "cppcoreguidelines-explicit-virtual-functions", "cppcoreguidelines-init-variables", "cppcoreguidelines-interfaces-global-init", @@ -3436,45 +3447,10 @@ "google-runtime-int", "google-runtime-operator", "google-upgrade-googletest-case", - "hicpp-*", - "hicpp-avoid-*", - "hicpp-avoid-c-arrays", - "hicpp-avoid-goto", - "hicpp-braces-around-statements", - "hicpp-deprecated-headers", - "hicpp-exception-baseclass", - "hicpp-explicit-conversions", - "hicpp-function-size", - "hicpp-ignored-remove-result", - "hicpp-invalid-access-moved", - "hicpp-member-init", - "hicpp-move-const-arg", - "hicpp-multiway-paths-covered", - "hicpp-named-parameter", - "hicpp-new-delete-operators", - "hicpp-no-*", - "hicpp-no-array-decay", - "hicpp-no-assembler", - "hicpp-no-malloc", - "hicpp-noexcept-move", - "hicpp-signed-bitwise", - "hicpp-special-member-functions", - "hicpp-static-assert", - "hicpp-undelegated-constructor", - "hicpp-uppercase-literal-suffix", - "hicpp-use-*", - "hicpp-use-auto", - "hicpp-use-emplace", - "hicpp-use-equals-*", - "hicpp-use-equals-default", - "hicpp-use-equals-delete", - "hicpp-use-noexcept", - "hicpp-use-nullptr", - "hicpp-use-override", - "hicpp-vararg", "linuxkernel-must-check-errs", "llvm-*", "llvm-else-after-return", + "llvm-formatv-string", "llvm-header-guard", "llvm-include-order", "llvm-namespace-comment", @@ -3483,10 +3459,13 @@ "llvm-prefer-register-over-unsigned", "llvm-prefer-static-over-anonymous-namespace", "llvm-qualified-auto", + "llvm-redundant-casting", "llvm-twine-local", + "llvm-type-switch-case-types", "llvm-use-*", "llvm-use-new-mlir-op-builder", "llvm-use-ranges", + "llvm-use-vector-utils", "llvmlibc-*", "llvmlibc-callee-namespace", "llvmlibc-implementation-in-namespace", @@ -3498,6 +3477,7 @@ "misc-const-correctness", "misc-coroutine-hostile-raii", "misc-definitions-in-headers", + "misc-explicit-constructor", "misc-header-include-cycle", "misc-include-cleaner", "misc-misleading-*", @@ -3513,7 +3493,9 @@ "misc-override-with-different-visibility", "misc-predictable-rand", "misc-redundant-expression", + "misc-static-*", "misc-static-assert", + "misc-static-initialization-cycle", "misc-throw-by-value-catch-by-reference", "misc-unconventional-assign-operator", "misc-uniqueptr-reset-release", @@ -3573,9 +3555,12 @@ "modernize-use-scoped-lock", "modernize-use-starts-ends-with", "modernize-use-std-*", + "modernize-use-std-bit", "modernize-use-std-format", "modernize-use-std-numbers", "modernize-use-std-print", + "modernize-use-string-view", + "modernize-use-structured-binding", "modernize-use-trailing-return-type", "modernize-use-transparent-functors", "modernize-use-uncaught-exceptions", @@ -3616,13 +3601,17 @@ "performance-noexcept-destructor", "performance-noexcept-move-constructor", "performance-noexcept-swap", + "performance-prefer-single-char-overloads", + "performance-string-view-conversions", "performance-trivially-destructible", "performance-type-promotion-in-math-fn", "performance-unnecessary-*", "performance-unnecessary-copy-initialization", "performance-unnecessary-value-param", + "performance-use-std-move", "portability-*", "portability-avoid-pragma-once", + "portability-no-assembler", "portability-restrict-system-includes", "portability-simd-intrinsics", "portability-std-allocator-const", @@ -3672,9 +3661,12 @@ "readability-redundant-declaration", "readability-redundant-function-ptr-dereference", "readability-redundant-inline-specifier", + "readability-redundant-lambda-parameter-list", "readability-redundant-member-init", + "readability-redundant-nested-if", "readability-redundant-parentheses", "readability-redundant-preprocessor", + "readability-redundant-qualified-alias", "readability-redundant-smartptr-get", "readability-redundant-string-*", "readability-redundant-string-cstr", @@ -3689,6 +3681,8 @@ "readability-static-definition-in-anonymous-namespace", "readability-string-compare", "readability-suspicious-call-argument", + "readability-trailing-comma", + "readability-trivial-switch", "readability-uniqueptr-delete-release", "readability-uppercase-literal-suffix", "readability-use-*", diff --git a/Extension/src/LanguageServer/codeAnalysis.ts b/Extension/src/LanguageServer/codeAnalysis.ts index 8a71a3d00..61cc8cf06 100644 --- a/Extension/src/LanguageServer/codeAnalysis.ts +++ b/Extension/src/LanguageServer/codeAnalysis.ts @@ -380,7 +380,7 @@ export function publishCodeAnalysisDiagnostics(params: PublishCodeAnalysisDiagno docPage = `checks${checksGroup}/${checksPage}.html`; } // TODO: This should be checking the clang-tidy version used to better support usage of older versions. - const primaryDocUri: vscode.Uri = vscode.Uri.parse(`https://releases.llvm.org/22.1.0/tools/clang/tools/extra/docs/clang-tidy/${docPage}`); + const primaryDocUri: vscode.Uri = vscode.Uri.parse(`https://releases.llvm.org/23.1.0/tools/clang/tools/extra/docs/clang-tidy/${docPage}`); diagnostic.code = { value: identifier.code, target: primaryDocUri }; if (new CppSettings().clangTidyCodeActionShowDocumentation) { From b8a7644bc31f19f283d18c66536f5fe258ca1a64 Mon Sep 17 00:00:00 2001 From: Colen Garoutte-Carson <49173979+Colengms@users.noreply.github.com> Date: Fri, 28 Aug 2026 17:43:27 -0700 Subject: [PATCH 2/3] Use native file type mappings for TypeScript-side classification (#14711) --- Extension/package.json | 7 ++ .../src/Debugger/configurationProvider.ts | 16 +-- Extension/src/LanguageServer/client.ts | 48 +++++--- .../LanguageServer/cppBuildTaskProvider.ts | 17 ++- Extension/src/common.ts | 29 ++--- Extension/src/fileType.ts | 110 ++++++++++++++++++ Extension/test/unit/fileType.test.ts | 76 ++++++++++++ 7 files changed, 249 insertions(+), 54 deletions(-) create mode 100644 Extension/src/fileType.ts create mode 100644 Extension/test/unit/fileType.test.ts diff --git a/Extension/package.json b/Extension/package.json index 8e4db4ca7..fd39e0a54 100644 --- a/Extension/package.json +++ b/Extension/package.json @@ -68,6 +68,13 @@ "languages": [ { "id": "cpp", + "extensions": [ + ".ccm", + ".cppm", + ".hip", + ".ixx", + ".sycl" + ], "filenames": [ "algorithm", "any", diff --git a/Extension/src/Debugger/configurationProvider.ts b/Extension/src/Debugger/configurationProvider.ts index 5bc427759..e675516f8 100644 --- a/Extension/src/Debugger/configurationProvider.ts +++ b/Extension/src/Debugger/configurationProvider.ts @@ -81,7 +81,7 @@ export class DebugConfigurationProvider implements vscode.DebugConfigurationProv throw new Error("Default config not found in provideDebugConfigurations()"); } const editor: vscode.TextEditor | undefined = vscode.window.activeTextEditor; - if (!editor || !util.isCppOrCFile(editor.document.uri) || configs.length <= 1) { + if (!editor || !util.isCppOrCFile(editor.document.uri, editor.document.languageId) || configs.length <= 1) { return [defaultTemplateConfig]; } @@ -546,12 +546,6 @@ export class DebugConfigurationProvider implements vscode.DebugConfigurationProv return; } - const fileExt: string = path.extname(editor.document.fileName); - if (!fileExt) { - DebugConfigurationProvider.detectedBuildTasks = emptyTasks; - return; - } - // Don't offer tasks for header files. const isHeader: boolean = util.isHeaderFile(editor.document.uri); if (isHeader) { @@ -559,9 +553,9 @@ export class DebugConfigurationProvider implements vscode.DebugConfigurationProv return; } - // Don't offer tasks if the active file's extension is not a recognized C/C++ extension. - const fileIsCpp: boolean = util.isCppFile(editor.document.uri); - const fileIsC: boolean = util.isCFile(editor.document.uri); + // Don't offer tasks if the active file is not a recognized C/C++ source file. + const fileIsCpp: boolean = util.isCppFile(editor.document.uri, editor.document.languageId); + const fileIsC: boolean = util.isCFile(editor.document.uri, editor.document.languageId); if (!(fileIsCpp || fileIsC)) { DebugConfigurationProvider.detectedBuildTasks = emptyTasks; return; @@ -982,7 +976,7 @@ export class DebugConfigurationProvider implements vscode.DebugConfigurationProv private async selectConfiguration(textEditor: vscode.TextEditor, pickDefault: boolean = true, onlyWorkspaceFolder: boolean = false): Promise { const folder: vscode.WorkspaceFolder | undefined = vscode.workspace.getWorkspaceFolder(textEditor.document.uri); - if (!util.isCppOrCFile(textEditor.document.uri)) { + if (!util.isCppOrCFile(textEditor.document.uri, textEditor.document.languageId)) { void vscode.window.showErrorMessage(localize("cannot.build.non.cpp", 'Cannot build and debug because the active file is not a C or C++ source file.')); return; } diff --git a/Extension/src/LanguageServer/client.ts b/Extension/src/LanguageServer/client.ts index e76eaa4bf..67836f6f3 100644 --- a/Extension/src/LanguageServer/client.ts +++ b/Extension/src/LanguageServer/client.ts @@ -35,6 +35,7 @@ import { ManualPromise } from '../Utility/Async/manualPromise'; import { logAndReturn } from '../Utility/Async/returns'; import * as util from '../common'; import { isWindows } from '../constants'; +import { FileTypeMappings, hasNativeFileTypeMappings, isTagParsableFile, resetFileTypeMappings, updateFileTypeMappings } from '../fileType'; import { instrument, isInstrumentationEnabled } from '../instrumentation'; import { DebugProtocolParams, Logger, ShowWarningParams, getDiagnosticsChannel, getOutputChannelLogger, logDebugProtocol, logLocalized, showWarning } from '../logger'; import { localizedStringCount, lookupString } from '../nativeStrings'; @@ -528,6 +529,7 @@ interface CppInitializationParams { interface CppInitializationResult { shouldShutdown: boolean; + fileTypeMappings?: FileTypeMappings; } interface TagParseStatus { @@ -683,6 +685,7 @@ const ReportStatusNotification: NotificationType = const DebugProtocolNotification: NotificationType = new NotificationType('cpptools/debugProtocol'); const DebugLogNotification: NotificationType = new NotificationType('cpptools/debugLog'); const CompileCommandsPathsNotification: NotificationType = new NotificationType('cpptools/compileCommandsPaths'); +const FileTypeMappingsNotification: NotificationType = new NotificationType('cpptools/fileTypeMappings'); const ReferencesNotification: NotificationType = new NotificationType('cpptools/references'); const ReportReferencesProgressNotification: NotificationType = new NotificationType('cpptools/reportReferencesProgress'); const RequestCustomConfigs: NotificationType = new NotificationType('cpptools/requestCustomConfigs'); @@ -1406,6 +1409,7 @@ export class DefaultClient implements Client { // Ideally this would be set earlier, but the task provider expects it to also mean that `this.innerConfiguration` is set. this.languageClient.isStarted = true; + clients.ActiveClient.updateActiveDocumentTextOptions(); telemetry.logLanguageServerEvent("NonDefaultInitialCppSettings", this.settingsTracker.getUserModifiedSettings()); failureMessageShown = false; @@ -1732,6 +1736,7 @@ export class DefaultClient implements Client { localizedStrings: localizedStrings, settings: this.getAllSettings() }; + resetFileTypeMappings(); this.loggingLevel = util.getNumericLoggingLevel(cppInitializationParams.settings.loggingLevel); const lspInitializationOptions: LspInitializationOptions = { @@ -1843,6 +1848,12 @@ export class DefaultClient implements Client { // A request is used in order to wait for completion and ensure that no subsequent // higher priority message may be processed before the Initialization request. const initializeResult = await client.sendRequest(InitializationRequest, cppInitializationParams); + if (initializeResult.fileTypeMappings) { + updateFileTypeMappings(initializeResult.fileTypeMappings); + } else { + resetFileTypeMappings(); + } + DebugConfigurationProvider.ClearDetectedBuildTasks(); // If the server requested shutdown, then reload with the failsafe (null) client. if (initializeResult.shouldShutdown) { @@ -2589,6 +2600,11 @@ export class DefaultClient implements Client { this.languageClient.onNotification(ReportStatusNotification, (e) => void this.updateStatus(e)); this.languageClient.onNotification(ReportTagParseStatusNotification, (e) => this.updateTagParseStatus(e)); this.languageClient.onNotification(CompileCommandsPathsNotification, (e) => void this.promptCompileCommands(e)); + this.languageClient.onNotification(FileTypeMappingsNotification, (mappings) => { + updateFileTypeMappings(mappings); + DebugConfigurationProvider.ClearDetectedBuildTasks(); + clients.ActiveClient.updateActiveDocumentTextOptions(); + }); this.languageClient.onNotification(ReferencesNotification, (e) => this.processReferencesPreview(e)); this.languageClient.onNotification(ReportReferencesProgressNotification, (e) => this.handleReferencesProgress(e)); this.languageClient.onNotification(RequestCustomConfigs, (e) => this.handleRequestCustomConfigs(e)); @@ -2718,14 +2734,14 @@ export class DefaultClient implements Client { void this.languageClient.sendNotification(FileCreatedNotification, { uri: uri.toString() }).catch(logAndReturn.undefined); }); - // TODO: Handle new associations without a reload. - this.associations_for_did_change = new Set(["cu", "cuh", "c", "i", "cpp", "cc", "cxx", "c++", "cp", "hpp", "hh", "hxx", "h++", "hp", "h", "ii", "ino", "inl", "ipp", "tcc", "txx", "tpp", "idl"]); + // Fallback for custom associations when native binaries do not publish effective file type mappings. + this.associations_for_did_change = new Set(); const assocs: any = new OtherSettings().filesAssociations; for (const assoc in assocs) { const dotIndex: number = assoc.lastIndexOf('.'); if (dotIndex !== -1) { const ext: string = assoc.substring(dotIndex + 1); - this.associations_for_did_change.add(ext); + this.associations_for_did_change.add(ext.toLowerCase()); } } this.rootPathFileWatcher.onDidChange(async (uri) => { @@ -2739,17 +2755,19 @@ export class DefaultClient implements Client { cachedEditorConfigLookups.clear(); this.updateActiveDocumentTextOptions(); } - if (dotIndex !== -1) { - const ext: string = uri.fsPath.substring(dotIndex + 1); - if (this.associations_for_did_change?.has(ext)) { - // VS Code has a bug that causes onDidChange events to happen to files that aren't changed, - // which causes a large backlog of "files to parse" to accumulate. - // We workaround this via only sending the change message if the modified time is within 10 seconds. - const mtime: Date = fs.statSync(uri.fsPath).mtime; - const duration: number = Date.now() - mtime.getTime(); - if (duration < 10000) { - void this.languageClient.sendNotification(FileChangedNotification, { uri: uri.toString() }).catch(logAndReturn.undefined); - } + const ext: string | undefined = dotIndex !== -1 ? uri.fsPath.substring(dotIndex + 1) : undefined; + const isTrackedFile: boolean = hasNativeFileTypeMappings() + ? isTagParsableFile(uri.fsPath) + : isTagParsableFile(uri.fsPath) || + (ext !== undefined && this.associations_for_did_change?.has(ext.toLowerCase()) === true); + if (isTrackedFile) { + // VS Code has a bug that causes onDidChange events to happen to files that aren't changed, + // which causes a large backlog of "files to parse" to accumulate. + // We workaround this via only sending the change message if the modified time is within 10 seconds. + const mtime: Date = fs.statSync(uri.fsPath).mtime; + const duration: number = Date.now() - mtime.getTime(); + if (duration < 10000) { + void this.languageClient.sendNotification(FileChangedNotification, { uri: uri.toString() }).catch(logAndReturn.undefined); } } }); @@ -3079,7 +3097,7 @@ export class DefaultClient implements Client { public updateActiveDocumentTextOptions(): void { const editor: vscode.TextEditor | undefined = vscode.window.activeTextEditor; if (editor && util.isCpp(editor.document)) { - void SessionState.buildAndDebugIsSourceFile.set(util.isCppOrCFile(editor.document.uri)); + void SessionState.buildAndDebugIsSourceFile.set(util.isCppOrCFile(editor.document.uri, editor.document.languageId)); void SessionState.buildAndDebugIsFolderOpen.set(util.isFolderOpen(editor.document.uri)); // If using vcFormat, check for a ".editorconfig" file, and apply those text options to the active document. const settings: CppSettings = new CppSettings(this.RootUri); diff --git a/Extension/src/LanguageServer/cppBuildTaskProvider.ts b/Extension/src/LanguageServer/cppBuildTaskProvider.ts index 486e3df64..8112602f9 100644 --- a/Extension/src/LanguageServer/cppBuildTaskProvider.ts +++ b/Extension/src/LanguageServer/cppBuildTaskProvider.ts @@ -73,7 +73,7 @@ export class CppBuildTaskProvider implements TaskProvider { return _task; } - // Generate tasks to build the current file based on the user's detected compilers, the user's compilerPath setting, and the current file's extension. + // Generate tasks to build the current file based on the user's detected compilers, compilerPath setting, and file type. public async getTasks(appendSourceToName: boolean = false): Promise { const editor: TextEditor | undefined = window.activeTextEditor; const emptyTasks: CppBuildTask[] = []; @@ -81,20 +81,15 @@ export class CppBuildTaskProvider implements TaskProvider { return emptyTasks; } - const fileExt: string = path.extname(editor.document.fileName); - if (!fileExt) { - return emptyTasks; - } - // Don't offer tasks for header files. const isHeader: boolean = util.isHeaderFile(editor.document.uri); if (isHeader) { return emptyTasks; } - // Don't offer tasks if the active file's extension is not a recognized C/C++ extension. - const fileIsCpp: boolean = util.isCppFile(editor.document.uri); - const fileIsC: boolean = util.isCFile(editor.document.uri); + // Don't offer tasks if the active file is not a recognized C/C++ source file. + const fileIsCpp: boolean = util.isCppFile(editor.document.uri, editor.document.languageId); + const fileIsC: boolean = util.isCFile(editor.document.uri, editor.document.languageId); if (!(fileIsCpp || fileIsC)) { return emptyTasks; } @@ -421,7 +416,9 @@ class CustomBuildTaskTerminal implements Pseudoterminal { } async openAsync(_initialDimensions: TerminalDimensions | undefined): Promise { - if (this.buildOptions.taskUsesActiveFile && !util.isCppOrCFile(window.activeTextEditor?.document.uri)) { + if (this.buildOptions.taskUsesActiveFile && !util.isCppOrCFile( + window.activeTextEditor?.document.uri, + window.activeTextEditor?.document.languageId)) { this.writeEmitter.fire(localize("cannot.build.non.cpp", 'Cannot build and debug because the active file is not a C or C++ source file.') + this.endOfLine); this.closeEmitter.fire(-1); return; diff --git a/Extension/src/common.ts b/Extension/src/common.ts index 7bfda0ec8..700d8ab44 100644 --- a/Extension/src/common.ts +++ b/Extension/src/common.ts @@ -16,6 +16,7 @@ import * as nls from 'vscode-nls'; import { TargetPopulation } from 'vscode-tas-client'; import { ManualPromise } from './Utility/Async/manualPromise'; import { isWindows } from './constants'; +import { classifyFilePath, FileTypeMapping } from './fileType'; import { getOutputChannelLogger, showOutputChannel } from './logger'; import { PlatformInformation } from './platform'; import * as Telemetry from './telemetry'; @@ -157,34 +158,26 @@ export function getVcpkgRoot(): string { return vcpkgRoot; } -/** - * This is a fuzzy determination of whether a uri represents a header file. - * For the purposes of this function, a header file has no extension, or an extension that begins with the letter 'h'. - * @param document The document to check. - */ export function isHeaderFile(uri: vscode.Uri): boolean { - const fileExt: string = path.extname(uri.fsPath); - const fileExtLower: string = fileExt.toLowerCase(); - return !fileExt || [".cuh", ".hpp", ".hh", ".hxx", ".h++", ".hp", ".h", ".inl", ".ipp", ".tcc", ".txx", ".tpp", ".tlh", ".tli", ""].some(ext => fileExtLower === ext); + return classifyFilePath(uri.fsPath)?.kind === 'header'; } -export function isCppFile(uri: vscode.Uri): boolean { - const fileExt: string = path.extname(uri.fsPath); - const fileExtLower: string = fileExt.toLowerCase(); - return (fileExt === ".C") || [".cu", ".cpp", ".cc", ".cxx", ".c++", ".cp", ".ii", ".ino"].some(ext => fileExtLower === ext); +export function isCppFile(uri: vscode.Uri, languageId?: string): boolean { + const fileType: FileTypeMapping | undefined = classifyFilePath(uri.fsPath, languageId); + return fileType?.kind === 'source' && (fileType.language === 'cpp' || fileType.language === 'cuda'); } -export function isCFile(uri: vscode.Uri): boolean { - const fileExt: string = path.extname(uri.fsPath); - const fileExtLower: string = fileExt.toLowerCase(); - return fileExt === ".c" || fileExtLower === ".i"; +export function isCFile(uri: vscode.Uri, languageId?: string): boolean { + const fileType: FileTypeMapping | undefined = classifyFilePath(uri.fsPath, languageId); + return fileType?.kind === 'source' && fileType.language === 'c'; } -export function isCppOrCFile(uri: vscode.Uri | undefined): boolean { +export function isCppOrCFile(uri: vscode.Uri | undefined, languageId?: string): boolean { if (!uri) { return false; } - return isCppFile(uri) || isCFile(uri); + const fileType: FileTypeMapping | undefined = classifyFilePath(uri.fsPath, languageId); + return fileType?.kind === 'source' && fileType.language !== undefined; } export function isFolderOpen(uri: vscode.Uri): boolean { diff --git a/Extension/src/fileType.ts b/Extension/src/fileType.ts new file mode 100644 index 000000000..4712511ed --- /dev/null +++ b/Extension/src/fileType.ts @@ -0,0 +1,110 @@ +/* -------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All Rights Reserved. + * See 'LICENSE' in the project root for license information. + * ------------------------------------------------------------------------------------------ */ + +import * as path from 'path'; + +export type FileTypeKind = 'source' | 'header' | 'idl' | 'resource' | 'other'; +export type FileTypeLanguage = 'c' | 'cpp' | 'cuda'; + +export interface FileTypeMapping { + name: string; + kind: FileTypeKind; + language?: FileTypeLanguage; +} + +export interface FileTypeMappings { + extensions: FileTypeMapping[]; + filenames: FileTypeMapping[]; +} + +const bootstrapMappings: FileTypeMappings = { + extensions: [ + ...['.cuh', '.hpp', '.hh', '.hxx', '.h++', '.hp', '.h', '.inl', '.ipp', '.tcc', '.txx', '.tpp', '.tlh', '.tli'] + .map(name => ({ name, kind: 'header' as const, language: name === '.cuh' ? 'cuda' as const : 'cpp' as const })), + ...['.cu', '.cpp', '.cc', '.ccm', '.cxx', '.c++', '.cp', '.cppm', '.hip', '.ii', '.ino', '.ixx', '.sycl'] + .map(name => ({ name, kind: 'source' as const, language: name === '.cu' ? 'cuda' as const : 'cpp' as const })), + ...['.c', '.i'].map(name => ({ name, kind: 'source' as const, language: 'c' as const })), + { name: '.idl', kind: 'idl' } + ], + filenames: [] +}; + +let extensionMappings: ReadonlyMap; +let filenameMappings: ReadonlyMap; +let nativeMappingsAvailable: boolean = false; + +function createMappingMap(mappings: FileTypeMapping[]): ReadonlyMap { + const result: Map = new Map(); + for (const mapping of mappings) { + // VS Code matches file associations case-insensitively on every platform. + result.set(mapping.name.toLowerCase(), { ...mapping }); + } + return result; +} + +export function resetFileTypeMappings(): void { + extensionMappings = createMappingMap(bootstrapMappings.extensions); + filenameMappings = createMappingMap(bootstrapMappings.filenames); + nativeMappingsAvailable = false; +} + +export function updateFileTypeMappings(mappings: FileTypeMappings | undefined): void { + if (!mappings) { + resetFileTypeMappings(); + return; + } + + extensionMappings = createMappingMap(mappings.extensions); + filenameMappings = createMappingMap(mappings.filenames); + nativeMappingsAvailable = true; +} + +export function hasNativeFileTypeMappings(): boolean { + return nativeMappingsAvailable; +} + +function getRegisteredFileType(filePath: string): FileTypeMapping | undefined { + const filename: string = path.basename(filePath); + const filenameMapping: FileTypeMapping | undefined = filenameMappings.get(filename.toLowerCase()); + if (filenameMapping) { + return filenameMapping; + } + + const extension: string = path.extname(filename); + // Uppercase .C is an exact, case-sensitive C++ association by convention. + if (extension === '.C') { + return { name: extension, kind: 'source', language: 'cpp' }; + } + return extensionMappings.get(extension.toLowerCase()); +} + +export function classifyFilePath(filePath: string, languageId?: string): FileTypeMapping | undefined { + const registeredType: FileTypeMapping | undefined = getRegisteredFileType(filePath); + if (registeredType) { + return registeredType; + } + + if (!nativeMappingsAvailable && !path.extname(filePath)) { + return { name: '', kind: 'header' }; + } + + switch (languageId) { + case 'c': + return { name: '', kind: 'source', language: 'c' }; + case 'cpp': + return { name: '', kind: 'source', language: 'cpp' }; + case 'cuda-cpp': + return { name: '', kind: 'source', language: 'cuda' }; + default: + return undefined; + } +} + +export function isTagParsableFile(filePath: string): boolean { + const type: FileTypeMapping | undefined = getRegisteredFileType(filePath); + return type?.kind === 'source' || type?.kind === 'header' || type?.kind === 'idl'; +} + +resetFileTypeMappings(); diff --git a/Extension/test/unit/fileType.test.ts b/Extension/test/unit/fileType.test.ts new file mode 100644 index 000000000..84c122b83 --- /dev/null +++ b/Extension/test/unit/fileType.test.ts @@ -0,0 +1,76 @@ +/* -------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All Rights Reserved. + * See 'LICENSE' in the project root for license information. + * ------------------------------------------------------------------------------------------ */ + +import { deepStrictEqual, equal } from 'node:assert'; +import { afterEach, describe, it } from 'mocha'; +import { + classifyFilePath, + hasNativeFileTypeMappings, + isTagParsableFile, + resetFileTypeMappings, + updateFileTypeMappings +} from '../../src/fileType'; + +describe('file type mappings', () => { + afterEach(() => resetFileTypeMappings()); + + it('uses case-insensitive legacy classifications before native initialization', () => { + equal(hasNativeFileTypeMappings(), false); + deepStrictEqual(classifyFilePath('file.HPP'), { name: '.hpp', kind: 'header', language: 'cpp' }); + deepStrictEqual(classifyFilePath('file.C'), { name: '.C', kind: 'source', language: 'cpp' }); + deepStrictEqual(classifyFilePath('file.c'), { name: '.c', kind: 'source', language: 'c' }); + for (const extension of ['CCM', 'CPPM', 'HIP', 'IXX', 'SYCL']) { + deepStrictEqual(classifyFilePath(`file.${extension}`), { name: `.${extension.toLowerCase()}`, kind: 'source', language: 'cpp' }); + equal(isTagParsableFile(`file.${extension}`), true); + } + deepStrictEqual(classifyFilePath('Makefile'), { name: '', kind: 'header' }); + }); + + it('atomically replaces bootstrap mappings with native mappings', () => { + updateFileTypeMappings({ + extensions: [ + { name: '.c', kind: 'source', language: 'c' }, + { name: '.cppm', kind: 'source', language: 'cpp' }, + { name: '.h', kind: 'header', language: 'cpp' }, + { name: '.hpp', kind: 'header', language: 'cpp' }, + { name: '.idl', kind: 'idl' } + ], + filenames: [ + { name: 'foo.h', kind: 'source', language: 'c' }, + { name: 'build', kind: 'source', language: 'cpp' }, + { name: 'vector', kind: 'header' }, + { name: 'kernel.custom', kind: 'source', language: 'cuda' } + ] + }); + + equal(hasNativeFileTypeMappings(), true); + deepStrictEqual(classifyFilePath('module.CPPM', 'cpp'), { name: '.cppm', kind: 'source', language: 'cpp' }); + deepStrictEqual(classifyFilePath('header.HPP', 'cpp'), { name: '.hpp', kind: 'header', language: 'cpp' }); + deepStrictEqual(classifyFilePath('file.C'), { name: '.C', kind: 'source', language: 'cpp' }); + deepStrictEqual(classifyFilePath('file.c'), { name: '.c', kind: 'source', language: 'c' }); + deepStrictEqual(classifyFilePath('FOO.H', 'cpp'), { name: 'foo.h', kind: 'source', language: 'c' }); + deepStrictEqual(classifyFilePath('build'), { name: 'build', kind: 'source', language: 'cpp' }); + deepStrictEqual(classifyFilePath('VECTOR', 'cpp'), { name: 'vector', kind: 'header' }); + deepStrictEqual(classifyFilePath('kernel.custom'), { name: 'kernel.custom', kind: 'source', language: 'cuda' }); + equal(classifyFilePath('Makefile'), undefined); + equal(isTagParsableFile('schema.idl'), true); + equal(isTagParsableFile('kernel.custom'), true); + equal(isTagParsableFile('build'), true); + equal(isTagParsableFile('vector'), true); + equal(isTagParsableFile('unknown.txt'), false); + }); + + it('uses the editor language only for unregistered paths', () => { + updateFileTypeMappings({ + extensions: [{ name: '.h', kind: 'header', language: 'cpp' }], + filenames: [] + }); + + deepStrictEqual(classifyFilePath('file.h', 'c'), { name: '.h', kind: 'header', language: 'cpp' }); + deepStrictEqual(classifyFilePath('file.special', 'c'), { name: '', kind: 'source', language: 'c' }); + deepStrictEqual(classifyFilePath('file.special', 'cuda-cpp'), { name: '', kind: 'source', language: 'cuda' }); + deepStrictEqual(classifyFilePath('extensionless', 'cpp'), { name: '', kind: 'source', language: 'cpp' }); + }); +}); From 2f25880cb584fab29b5b7d473a3bf73825058731 Mon Sep 17 00:00:00 2001 From: Sean McManus Date: Mon, 31 Aug 2026 15:25:57 -0700 Subject: [PATCH 3/3] Add 1.34.2 changelog (#14722) * Add 1.34.2 changelog --- Extension/CHANGELOG.md | 10 ++++++++++ Extension/package.json | 2 +- 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/Extension/CHANGELOG.md b/Extension/CHANGELOG.md index 3d50c118c..c1232b567 100644 --- a/Extension/CHANGELOG.md +++ b/Extension/CHANGELOG.md @@ -1,5 +1,15 @@ # C/C++ for Visual Studio Code Changelog +## Version 1.34.2: August 31, 2026 +### Enhancements +* Improve C/C++ file classification by supporting extensionless headers and additional extensions such as `.hip` throughout extension features. [PR #14711](https://github.com/microsoft/vscode-cpptools/pull/14711) +* Update the bundled `clang-tidy` and `clang-format` from 22.1.3 to 23.1.0, including available `clang-tidy` check suggestions and documentation. [PR #14713](https://github.com/microsoft/vscode-cpptools/pull/14713) + +### Bug Fixes +* Fix high CPU and memory usage in workspaces with multiple symbolic links that point to ancestor directories. [#14689](https://github.com/microsoft/vscode-cpptools/issues/14689) +* Fix include completion and symbol navigation using stale recursive include data after files are replaced by directories or symbolic link roots change. +* Fix 'Find All References' misclassifying references in inactive preprocessor regions after falling back to header-only IntelliSense. + ## Version 1.34.1: August 28, 2026 ### Bug Fixes * Fix formatting not inserting the final newline requested by `InsertNewlineAtEOF`. [#12680](https://github.com/microsoft/vscode-cpptools/issues/12680) diff --git a/Extension/package.json b/Extension/package.json index fd39e0a54..5c42a5ecd 100644 --- a/Extension/package.json +++ b/Extension/package.json @@ -2,7 +2,7 @@ "name": "cpptools", "displayName": "C/C++", "description": "C/C++ IntelliSense, debugging, and code browsing.", - "version": "1.34.1-main", + "version": "1.34.2-main", "publisher": "ms-vscode", "icon": "LanguageCCPP_color_128x.png", "readme": "README.md",