From 3a5dcfda1b0f77a44901a3b536d0f724c19aa7c1 Mon Sep 17 00:00:00 2001 From: Magyarimiki <249292183+Magyarimiki@users.noreply.github.com> Date: Thu, 18 Jun 2026 17:46:43 +0200 Subject: [PATCH 1/5] Do not analyze unsupported files on save Signed-off-by: Magyarimiki <249292183+Magyarimiki@users.noreply.github.com> --- src/backend/executor/bridge.ts | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/src/backend/executor/bridge.ts b/src/backend/executor/bridge.ts index 9bbc236..3b0800f 100644 --- a/src/backend/executor/bridge.ts +++ b/src/backend/executor/bridge.ts @@ -387,7 +387,7 @@ export class ExecutorBridge implements Disposable { public async analyzeCurrentFile() { const currentFile = window.activeTextEditor?.document.uri; - if (currentFile !== undefined) { + if (currentFile !== undefined && this.isSupportedFile(currentFile)) { await this.analyzeFile(currentFile); } } @@ -928,4 +928,19 @@ export class ExecutorBridge implements Disposable { this._databaseLocationChanged.fire(); } + + private isSupportedFile(uri: Uri | undefined): boolean { + if (uri === undefined) { + return false; + } + + const extensions = [ + '.c', + '.cc', + '.cpp', + '.cxx' + ]; + + return extensions.find(ext => uri.path.endsWith(ext)) !== undefined; + } } \ No newline at end of file From f92fde4a20e66bd6144cf748c25a3a9da0e9b97b Mon Sep 17 00:00:00 2001 From: Magyarimiki <249292183+Magyarimiki@users.noreply.github.com> Date: Fri, 19 Jun 2026 13:15:08 +0200 Subject: [PATCH 2/5] Prevent actions when no supported files present Signed-off-by: Magyarimiki <249292183+Magyarimiki@users.noreply.github.com> --- package.json | 4 +--- src/backend/executor/bridge.ts | 38 ++++++++++++------------------ src/editor/initialize.ts | 3 ++- src/extension.ts | 3 +++ src/utils/files.ts | 42 ++++++++++++++++++++++++++++++++++ src/utils/state.ts | 5 ++++ 6 files changed, 67 insertions(+), 28 deletions(-) create mode 100644 src/utils/files.ts create mode 100644 src/utils/state.ts diff --git a/package.json b/package.json index 8e8e5cb..c3f84b3 100644 --- a/package.json +++ b/package.json @@ -31,9 +31,7 @@ "code analysis" ], "activationEvents": [ - "onCommand:codechecker.backend.reloadMetadata", - "onView:codechecker.views.overview", - "onFileSystem:file" + "onStartupFinished" ], "main": "./dist/extension.js", "contributes": { diff --git a/src/backend/executor/bridge.ts b/src/backend/executor/bridge.ts index 3b0800f..65ed938 100644 --- a/src/backend/executor/bridge.ts +++ b/src/backend/executor/bridge.ts @@ -25,6 +25,8 @@ import { NotificationType } from '../../editor/notifications'; import { Editor } from '../../editor'; import { SidebarContainer } from '../../sidebar'; import { ReportTreeItem } from '../../sidebar/views'; +import { isSupportedFile } from '../../utils/files'; +import { state } from '../../utils/state'; // Structure: // CodeChecker analyzer version: \n {"base_package_version": "M.m.p", ...} @@ -116,8 +118,10 @@ export class ExecutorBridge implements Disposable { ExtensionApi.executorManager )); - this.updateCompilationDatabasePaths(); - this.checkVersion(); + if (state.workspaceSupported) { + this.updateCompilationDatabasePaths(); + this.checkVersion(); + } } dispose() { @@ -387,13 +391,13 @@ export class ExecutorBridge implements Disposable { public async analyzeCurrentFile() { const currentFile = window.activeTextEditor?.document.uri; - if (currentFile !== undefined && this.isSupportedFile(currentFile)) { + if (currentFile !== undefined && isSupportedFile(currentFile)) { await this.analyzeFile(currentFile); } } public async analyzeFile(file: Uri) { - if (!await this.checkVersion()) { + if (!state.workspaceSupported || !await this.checkVersion()) { return; } @@ -411,7 +415,7 @@ export class ExecutorBridge implements Disposable { } public async analyzeProject() { - if (!await this.checkVersion()) { + if (!state.workspaceSupported || !await this.checkVersion()) { return; } @@ -431,9 +435,10 @@ export class ExecutorBridge implements Disposable { } public async getFileAnalysisStatus() { - if (!await this.checkVersion()) { + if (!state.workspaceSupported || !await this.checkVersion()) { return; } + if (this.checkedVersion < [ 6, 27, 0 ]) { const statusNode = SidebarContainer.reportsView.getNodeById('statusItem'); statusNode?.setLabelAndIcon('Status report requires CodeChecker 6.27.0 or higher.'); @@ -579,7 +584,7 @@ export class ExecutorBridge implements Disposable { } public async runLog(buildCommand?: string) { - if (!await this.checkVersion()) { + if (!state.workspaceSupported || !await this.checkVersion()) { return; } @@ -599,7 +604,7 @@ export class ExecutorBridge implements Disposable { } public async reloadCheckerData() { - if (!await this.checkVersion()) { + if (!state.workspaceSupported || !await this.checkVersion()) { return; } @@ -642,7 +647,7 @@ export class ExecutorBridge implements Disposable { } public async parseMetadata(...files: Uri[]) { - if (!await this.checkVersion()) { + if (!state.workspaceSupported || !await this.checkVersion()) { return; } @@ -928,19 +933,4 @@ export class ExecutorBridge implements Disposable { this._databaseLocationChanged.fire(); } - - private isSupportedFile(uri: Uri | undefined): boolean { - if (uri === undefined) { - return false; - } - - const extensions = [ - '.c', - '.cc', - '.cpp', - '.cxx' - ]; - - return extensions.find(ext => uri.path.endsWith(ext)) !== undefined; - } } \ No newline at end of file diff --git a/src/editor/initialize.ts b/src/editor/initialize.ts index 033563e..9e2de08 100644 --- a/src/editor/initialize.ts +++ b/src/editor/initialize.ts @@ -2,6 +2,7 @@ import { ExtensionContext, Uri, commands, window, workspace } from 'vscode'; import { ExtensionApi } from '../backend'; import { Editor } from './editor'; import { NotificationType } from './notifications'; +import { state } from '../utils/state'; export class FolderInitializer { constructor(_ctx: ExtensionContext) { @@ -27,7 +28,7 @@ export class FolderInitializer { async showDialog() { const workspaceFolder = workspace.workspaceFolders?.length && workspace.workspaceFolders[0].uri; - if (!workspaceFolder) { + if (!workspaceFolder || !state.workspaceSupported) { return; } diff --git a/src/extension.ts b/src/extension.ts index 1f92754..5e38cd0 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -2,6 +2,7 @@ import * as vscode from 'vscode'; import { ExtensionApi } from './backend'; import { Editor } from './editor'; import { SidebarContainer } from './sidebar'; +import { checkWorkspace } from './utils/files'; export interface CodeCheckerExtension { extensionApi: typeof ExtensionApi, @@ -15,6 +16,8 @@ export function activate(context: vscode.ExtensionContext): CodeCheckerExtension Editor.init(context); SidebarContainer.init(context); + checkWorkspace(); + console.log('Extension "codechecker" activated'); return { diff --git a/src/utils/files.ts b/src/utils/files.ts new file mode 100644 index 0000000..53e0920 --- /dev/null +++ b/src/utils/files.ts @@ -0,0 +1,42 @@ +import { Uri, workspace } from 'vscode'; +import { state } from './state'; + +const EXTENSIONS = [ + '.c', + '.cc', + '.cpp', + '.cxx' +]; + +export function isSupportedFile(uri: Uri | undefined): boolean { + if (uri === undefined) { + return false; + } + + return EXTENSIONS.find(ext => uri.path.endsWith(ext)) !== undefined; +} + +export async function isSupportedWorkspace() { + const supported = await workspace.findFiles( + `**/*.{${EXTENSIONS.join(',')}}`, + '**/{node_modules,.git}/**', + 1 + ); + + return supported.length > 1; +} + +export async function checkWorkspace() { + if (workspace.name === undefined) { + state.workspaceSupported = false; + console.log('No open workspace.'); + return; + } + const supported = await isSupportedWorkspace(); + if (supported) { + state.workspaceSupported = true; + } else { + state.workspaceSupported = false; + console.log(`workspace ${workspace.name} does not contain supported files.`); + } +} \ No newline at end of file diff --git a/src/utils/state.ts b/src/utils/state.ts new file mode 100644 index 0000000..f389cef --- /dev/null +++ b/src/utils/state.ts @@ -0,0 +1,5 @@ +export class ExtensionState { + public workspaceSupported = false; +} + +export const state = new ExtensionState(); \ No newline at end of file From 99dcfefed4753d0fecec48eb59112e3ce9cb4d45 Mon Sep 17 00:00:00 2001 From: Magyarimiki <249292183+Magyarimiki@users.noreply.github.com> Date: Tue, 23 Jun 2026 10:26:45 +0200 Subject: [PATCH 3/5] Fix compile commands path for project analysis Signed-off-by: Magyarimiki <249292183+Magyarimiki@users.noreply.github.com> --- src/backend/executor/bridge.ts | 10 +++++++++- src/utils/files.ts | 4 ++-- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/src/backend/executor/bridge.ts b/src/backend/executor/bridge.ts index 65ed938..6c6b809 100644 --- a/src/backend/executor/bridge.ts +++ b/src/backend/executor/bridge.ts @@ -254,7 +254,15 @@ export class ExecutorBridge implements Disposable { } else if (files.length === 0) { // FIXME: Add a way to analyze all open workspaces, or a selected one this._bridgeMessages.fire('>>> Using CodeChecker\'s built-in compilation database resolver\n'); - args.push(workspace.workspaceFolders[0].uri.fsPath); + let analysisPath; + const ccFolder = getConfigAndReplaceVariables('codechecker.backend', 'outputFolder'); + if (ccFolder !== undefined) { + analysisPath = path.join(ccFolder, 'compile_commands.json'); + } else { + const workspaceFolder = workspace.workspaceFolders[0].uri.fsPath; + analysisPath = path.join(workspaceFolder, '.codechecker'); + } + args.push(analysisPath); } else if (files.length === 1) { this._bridgeMessages.fire('>>> Using CodeChecker\'s built-in compilation database resolver\n'); args.push(files[0].fsPath); diff --git a/src/utils/files.ts b/src/utils/files.ts index 53e0920..d752f34 100644 --- a/src/utils/files.ts +++ b/src/utils/files.ts @@ -18,12 +18,12 @@ export function isSupportedFile(uri: Uri | undefined): boolean { export async function isSupportedWorkspace() { const supported = await workspace.findFiles( - `**/*.{${EXTENSIONS.join(',')}}`, + `**/*{${EXTENSIONS.join(',')}}`, '**/{node_modules,.git}/**', 1 ); - return supported.length > 1; + return supported.length > 0; } export async function checkWorkspace() { From 876cb3e19835165c16174d9984b462f7bf491d7a Mon Sep 17 00:00:00 2001 From: Magyarimiki <249292183+Magyarimiki@users.noreply.github.com> Date: Wed, 24 Jun 2026 11:22:10 +0200 Subject: [PATCH 4/5] If set, use custom db path for project analysis Signed-off-by: Magyarimiki <249292183+Magyarimiki@users.noreply.github.com> --- src/backend/executor/bridge.ts | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/backend/executor/bridge.ts b/src/backend/executor/bridge.ts index 6c6b809..5fe6426 100644 --- a/src/backend/executor/bridge.ts +++ b/src/backend/executor/bridge.ts @@ -255,9 +255,12 @@ export class ExecutorBridge implements Disposable { // FIXME: Add a way to analyze all open workspaces, or a selected one this._bridgeMessages.fire('>>> Using CodeChecker\'s built-in compilation database resolver\n'); let analysisPath; - const ccFolder = getConfigAndReplaceVariables('codechecker.backend', 'outputFolder'); - if (ccFolder !== undefined) { - analysisPath = path.join(ccFolder, 'compile_commands.json'); + const ccDbFolder = getConfigAndReplaceVariables('codechecker.backend', 'compilationDatabasePath'); + const outputFolder = getConfigAndReplaceVariables('codechecker.backend', 'outputFolder'); + if (ccDbFolder !== undefined) { + analysisPath = path.join(ccDbFolder, 'compile_commands.json'); + } else if (outputFolder !== undefined) { + analysisPath = path.join(outputFolder, 'compile_commands.json'); } else { const workspaceFolder = workspace.workspaceFolders[0].uri.fsPath; analysisPath = path.join(workspaceFolder, '.codechecker'); From 6649185adb9fb8fabebf2718682bd028b9394a81 Mon Sep 17 00:00:00 2001 From: Magyarimiki <249292183+Magyarimiki@users.noreply.github.com> Date: Tue, 30 Jun 2026 10:13:58 +0200 Subject: [PATCH 5/5] UI improvements to support file filtering Signed-off-by: Magyarimiki <249292183+Magyarimiki@users.noreply.github.com> --- src/backend/executor/bridge.ts | 31 +++++++++++++++++++++------- src/backend/processor/diagnostics.ts | 7 ++++++- src/editor/diagnostics.ts | 2 +- src/extension.ts | 11 +++++----- src/sidebar/views/reports.ts | 19 ++++++++++++++--- src/utils/files.ts | 14 ++++++++++++- 6 files changed, 66 insertions(+), 18 deletions(-) diff --git a/src/backend/executor/bridge.ts b/src/backend/executor/bridge.ts index 5fe6426..21969db 100644 --- a/src/backend/executor/bridge.ts +++ b/src/backend/executor/bridge.ts @@ -25,7 +25,11 @@ import { NotificationType } from '../../editor/notifications'; import { Editor } from '../../editor'; import { SidebarContainer } from '../../sidebar'; import { ReportTreeItem } from '../../sidebar/views'; -import { isSupportedFile } from '../../utils/files'; +import { + COMPILE_COMMANDS_JSON, + getCompilationDatabasePath, + getOutputFolder, + isSupportedFile } from '../../utils/files'; import { state } from '../../utils/state'; // Structure: @@ -255,12 +259,12 @@ export class ExecutorBridge implements Disposable { // FIXME: Add a way to analyze all open workspaces, or a selected one this._bridgeMessages.fire('>>> Using CodeChecker\'s built-in compilation database resolver\n'); let analysisPath; - const ccDbFolder = getConfigAndReplaceVariables('codechecker.backend', 'compilationDatabasePath'); - const outputFolder = getConfigAndReplaceVariables('codechecker.backend', 'outputFolder'); + const ccDbFolder = getCompilationDatabasePath(); + const outputFolder = getOutputFolder(); if (ccDbFolder !== undefined) { - analysisPath = path.join(ccDbFolder, 'compile_commands.json'); + analysisPath = path.join(ccDbFolder, COMPILE_COMMANDS_JSON); } else if (outputFolder !== undefined) { - analysisPath = path.join(outputFolder, 'compile_commands.json'); + analysisPath = path.join(outputFolder, COMPILE_COMMANDS_JSON); } else { const workspaceFolder = workspace.workspaceFolders[0].uri.fsPath; analysisPath = path.join(workspaceFolder, '.codechecker'); @@ -450,6 +454,11 @@ export class ExecutorBridge implements Disposable { return; } + const fileUri = window.activeTextEditor?.document.uri; + if (!isSupportedFile(fileUri)) { + return; + } + if (this.checkedVersion < [ 6, 27, 0 ]) { const statusNode = SidebarContainer.reportsView.getNodeById('statusItem'); statusNode?.setLabelAndIcon('Status report requires CodeChecker 6.27.0 or higher.'); @@ -458,7 +467,6 @@ export class ExecutorBridge implements Disposable { const ccPath = getConfigAndReplaceVariables('codechecker.executor', 'executablePath') || 'CodeChecker'; const reportsFolder = this.getReportsFolder(); - const fileUri = window.activeTextEditor?.document.uri; const fsPath = fileUri?.fsPath; const statusArgs = [ @@ -532,6 +540,7 @@ export class ExecutorBridge implements Disposable { analyzerStatuses.push(existingStatusNode); } else { existingStatusNode.iconPath = new ThemeIcon(iconname); + analyzerStatuses.push(existingStatusNode); } } } @@ -545,6 +554,9 @@ export class ExecutorBridge implements Disposable { } else if (failed > 0) { statusNode?.setLabelAndIcon('Analysis failed', new ThemeIcon('error', new ThemeColor('charts.red'))); + } else if (missing > 0 && outdated === 0 && uptodate === 0) { + statusNode?.setLabelAndIcon('Analysis is missing', + new ThemeIcon('error', new ThemeColor('charts.red'))); } else if (outdated === 0 && failed === 0) { statusNode?.setLabelAndIcon('Analysis is up-to-date', new ThemeIcon('check', new ThemeColor('charts.green'))); @@ -658,7 +670,12 @@ export class ExecutorBridge implements Disposable { } public async parseMetadata(...files: Uri[]) { - if (!state.workspaceSupported || !await this.checkVersion()) { + if (!state.workspaceSupported || files.length === 0 || !await this.checkVersion()) { + return; + } + + if (!files.find(uri => isSupportedFile(uri))) { + ExtensionApi.diagnostics.fireDiagnosticsUpdate(); return; } diff --git a/src/backend/processor/diagnostics.ts b/src/backend/processor/diagnostics.ts index 925f463..5f2e3dd 100644 --- a/src/backend/processor/diagnostics.ts +++ b/src/backend/processor/diagnostics.ts @@ -41,6 +41,10 @@ export class DiagnosticsApi { return undefined; } + public fireDiagnosticsUpdate() { + this._diagnosticsUpdated.fire(); + } + public setSelectedEntry(position?: {file: string, idx: number}) { if (position) { const activeFile = this.getFileDiagnostics(Uri.file(position.file)) ?? []; @@ -88,7 +92,8 @@ export class DiagnosticsApi { return; } - ExtensionApi.executorBridge.parseMetadata(...filesToLoad.map(file => Uri.file(file))) + ExtensionApi.executorBridge.parseMetadata(...filesToLoad + .map(file => Uri.file(file))) .catch((err: any) => console.log(`Internal error in reloadDiagnostics: ${err}`)); } diff --git a/src/editor/diagnostics.ts b/src/editor/diagnostics.ts index d657812..5ef509f 100644 --- a/src/editor/diagnostics.ts +++ b/src/editor/diagnostics.ts @@ -157,7 +157,7 @@ export class DiagnosticRenderer { if (this.customSeverities && this.customSeverities[severity]) { const severityString = this.customSeverities[severity]; - if (typeof severityString === 'string' && this._severityMap[severityString.toLowerCase()]) { + if (typeof severityString === 'string' && this._severityMap[severityString.toLowerCase()] !== undefined) { return this._severityMap[severityString.toLowerCase()]; } else { Editor.loggerPanel.window.appendLine( diff --git a/src/extension.ts b/src/extension.ts index 5e38cd0..7dbcd94 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -10,13 +10,14 @@ export interface CodeCheckerExtension { editor: typeof Editor } -export function activate(context: vscode.ExtensionContext): CodeCheckerExtension { +export async function activate(context: vscode.ExtensionContext): Promise { // Backend must be initialized before the frontend - ExtensionApi.init(context); - Editor.init(context); - SidebarContainer.init(context); - checkWorkspace(); + if (await checkWorkspace()) { + ExtensionApi.init(context); + Editor.init(context); + SidebarContainer.init(context); + } console.log('Extension "codechecker" activated'); diff --git a/src/sidebar/views/reports.ts b/src/sidebar/views/reports.ts index e9cb739..08cfcad 100644 --- a/src/sidebar/views/reports.ts +++ b/src/sidebar/views/reports.ts @@ -19,6 +19,7 @@ import { import { ExtensionApi } from '../../backend'; import { DiagnosticReport } from '../../backend/types'; import { SidebarContainer } from '../sidebar_container'; +import { isSupportedFile } from '../../utils/files'; export class ReportTreeItem extends TreeItem { parent: ReportTreeItem | undefined; @@ -378,13 +379,25 @@ export class ReportsView implements TreeDataProvider { // Get root level items. getRootItems(): ReportTreeItem[] | undefined { - if (!this.currentEntryList?.length) { + if (!isSupportedFile(this.currentFile)) { const statusNode = SidebarContainer.reportsView.getNodeById('statusItem'); - statusNode?.setLabelAndIcon('Not in compilation database', - new ThemeIcon('question', new ThemeColor('charts.orange'))); + if (statusNode !== undefined) { + statusNode.setLabelAndIcon('Not supported by codechecker', + new ThemeIcon('question', new ThemeColor('charts.orange'))); + // statusNode.children = []; + statusNode.collapsibleState = TreeItemCollapsibleState.None; + const rootNode = statusNode.parent; + if (rootNode !== undefined) { + rootNode.children = [ statusNode ]; + } + } return statusNode ? [ statusNode ] : undefined; } + if (this.currentEntryList === undefined) { + return undefined; + } + const severityItems: { [key: string]: TreeDiagnosticReport[] } = {}; for (const [idx, entry] of this.currentEntryList.entries()) { const severity = entry.severity || 'UNSPECIFIED'; diff --git a/src/utils/files.ts b/src/utils/files.ts index d752f34..c7d2fdf 100644 --- a/src/utils/files.ts +++ b/src/utils/files.ts @@ -1,5 +1,6 @@ import { Uri, workspace } from 'vscode'; import { state } from './state'; +import { getConfigAndReplaceVariables } from './config'; const EXTENSIONS = [ '.c', @@ -8,6 +9,8 @@ const EXTENSIONS = [ '.cxx' ]; +export const COMPILE_COMMANDS_JSON = 'compile_commands.json'; + export function isSupportedFile(uri: Uri | undefined): boolean { if (uri === undefined) { return false; @@ -30,7 +33,7 @@ export async function checkWorkspace() { if (workspace.name === undefined) { state.workspaceSupported = false; console.log('No open workspace.'); - return; + return false; } const supported = await isSupportedWorkspace(); if (supported) { @@ -39,4 +42,13 @@ export async function checkWorkspace() { state.workspaceSupported = false; console.log(`workspace ${workspace.name} does not contain supported files.`); } + return state.workspaceSupported; +} + +export function getOutputFolder() { + return getConfigAndReplaceVariables('codechecker.backend', 'outputFolder'); +} + +export function getCompilationDatabasePath() { + return getConfigAndReplaceVariables('codechecker.backend', 'compilationDatabasePath'); } \ No newline at end of file