Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,18 @@ This extension contributes the following settings under `cppcheck-official.*`:
- **`cppcheck-official.arguments`**: (string) Additional [command line arguments](https://cppcheck.sourceforge.io/manual.pdf?#page=5) to pass to `cppcheck`.
- **`cppcheck-official.path`**: (string) Path to the `cppcheck` executable (i.e. `/path/to/cppcheck.exe`). If left empty, `cppcheck` from the system PATH is used. Supports paths relative to workspace folder on the formats `./RELATIVE_PATH`, `../RELATIVE_PATH` or `${workspaceFolder}/RELATIVE_PATH`.

## Shared Settings File

To give all developers in a project the same setup, create a `cppcheck.json` file in the root of the workspace folder and commit it to version control:

```json
{
"arguments": "--project=build/compile_commands.json --enable=style --suppress=missingInclude"
}
```

`arguments` can be either a string or an array of strings. If the file specifies `arguments`, it takes precedence over the `cppcheck-official.arguments` setting. Relative paths are resolved relative to the workspace folder. All other settings are still read from the VS Code settings, so each developer can adjust them individually.

## Reporting Issues
Please submit any issues or feature requests via the [GitHub Issues page](https://github.com/cppchecksolutions/vscode-cppcheck-official/issues).

Expand Down
8 changes: 7 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -67,12 +67,18 @@
"cppcheck-official.arguments": {
"type": "string",
"default": "",
"markdownDescription": "Additional command line arguments for cppcheck. Use spaces to separate multiple arguments. For documentation see: [Cppcheck documentation](https://files.cppchecksolutions.com/manual.pdf)",
"markdownDescription": "Additional command line arguments for cppcheck. Use spaces to separate multiple arguments. For documentation see: [Cppcheck documentation](https://files.cppchecksolutions.com/manual.pdf). Overridden by `arguments` in `cppcheck.json` in the workspace folder, if present.",
"description": "Additional command line arguments for cppcheck. Use spaces to separate multiple arguments."
}
}
}
],
"jsonValidation": [
{
"fileMatch": "/cppcheck.json",
"url": "./schemas/cppcheck.schema.json"
}
],
"walkthroughs": [
{
"id": "cppcheck-official.gettingStarted",
Expand Down
16 changes: 16 additions & 0 deletions schemas/cppcheck.schema.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"title": "Cppcheck Official settings file",
"description": "Shared settings for the Cppcheck Official VS Code extension. Settings in this file take precedence over the VS Code settings.",
"type": "object",
"properties": {
"arguments": {
"description": "Additional command line arguments for cppcheck. Overrides the 'cppcheck-official.arguments' setting. Paths are resolved relative to the workspace folder.",
"oneOf": [
{ "type": "string" },
{ "type": "array", "items": { "type": "string" } }
]
}
},
"additionalProperties": false
}
5 changes: 3 additions & 2 deletions src/extension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import { DiagnosticMetadataStore, diagnosticsUnion } from './util/diagnostics';
import { CodeActionProvider } from './util/codeActions';
import { ProjectFileStore, writeSuppressionToProjectFile } from './util/files';
import { extractRelatedInformation, setUpDiagnostic } from './helpers/diagnosticHelpers';
import { getArguments } from './util/settingsFile';

// To keep track of document changes we save hashed versions of their content to this record
let documentHashMemory : Record<string, string> = {};
Expand Down Expand Up @@ -441,7 +442,7 @@ export async function activate(context: vscode.ExtensionContext) {
const userPath = config.get<string>("cppcheck-official.path")?.trim() || "";
const commandPath = userPath ? resolvePath(userPath) : "cppcheck";

var args = config.get<string>("cppcheck-official.arguments", "");
var args = getArguments(config);
const processedArgs = await processArguments(args);

// Check if cppcheck is available
Expand Down Expand Up @@ -590,7 +591,7 @@ export async function activate(context: vscode.ExtensionContext) {
const userPath = config.get<string>("cppcheck-official.path")?.trim() || "";
const commandPath = userPath ? resolvePath(userPath) : "cppcheck";

var args = config.get<string>("cppcheck-official.arguments", "");
var args = getArguments(config);
const processedArgs = await processArguments(args);

// If disabled, clear any existing diagnostics for this doc.
Expand Down
104 changes: 104 additions & 0 deletions src/test/settingsFile.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
import * as assert from 'assert';
import * as fs from 'fs';
import * as os from 'os';
import * as path from 'path';
import * as vscode from 'vscode';
import { getArguments, getSettingsFilePath } from '../util/settingsFile';

suite('Settings file', () => {
let workspaceRoot: string;
let errorMessages: string[];
const originalShowErrorMessage = vscode.window.showErrorMessage;

// Fake VS Code configuration that returns the given value for the arguments setting
const config = {
get: (key: string, defaultValue?: unknown) =>
key === 'cppcheck-official.arguments' ? '--from-vscode-settings' : defaultValue,
} as vscode.WorkspaceConfiguration;

function writeSettingsFile(content: string) {
fs.writeFileSync(getSettingsFilePath(workspaceRoot), content);
}

setup(() => {
workspaceRoot = fs.mkdtempSync(path.join(os.tmpdir(), 'cppcheck-settings-'));
errorMessages = [];
(vscode.window as { showErrorMessage: unknown }).showErrorMessage = (message: string) => {
errorMessages.push(message);
return Promise.resolve(undefined);
};
// Clear error memory from previous tests
getArguments(config, workspaceRoot);
});

teardown(() => {
(vscode.window as { showErrorMessage: unknown }).showErrorMessage = originalShowErrorMessage;
fs.rmSync(workspaceRoot, { recursive: true, force: true });
});

test('Uses VS Code setting when there is no settings file', () => {
assert.strictEqual(getArguments(config, workspaceRoot), '--from-vscode-settings');
assert.deepStrictEqual(errorMessages, []);
});

test('Arguments as string', () => {
writeSettingsFile('{"arguments": "--enable=style --project=build/compile_commands.json"}');
assert.strictEqual(getArguments(config, workspaceRoot), '--enable=style --project=build/compile_commands.json');
assert.deepStrictEqual(errorMessages, []);
});

test('Arguments as array', () => {
writeSettingsFile('{"arguments": ["--enable=style", "--project=build/compile_commands.json"]}');
assert.strictEqual(getArguments(config, workspaceRoot), '--enable=style --project=build/compile_commands.json');
assert.deepStrictEqual(errorMessages, []);
});

test('Empty arguments in settings file override VS Code setting', () => {
writeSettingsFile('{"arguments": ""}');
assert.strictEqual(getArguments(config, workspaceRoot), '');
});

test('Uses VS Code setting when settings file has no arguments', () => {
writeSettingsFile('{}');
assert.strictEqual(getArguments(config, workspaceRoot), '--from-vscode-settings');
assert.deepStrictEqual(errorMessages, []);
});

test('Changes to settings file are picked up', () => {
writeSettingsFile('{"arguments": "--first"}');
assert.strictEqual(getArguments(config, workspaceRoot), '--first');
writeSettingsFile('{"arguments": "--second"}');
assert.strictEqual(getArguments(config, workspaceRoot), '--second');
});

test('Invalid JSON reports error once and falls back to VS Code setting', () => {
writeSettingsFile('{"arguments": ');
assert.strictEqual(getArguments(config, workspaceRoot), '--from-vscode-settings');
assert.strictEqual(getArguments(config, workspaceRoot), '--from-vscode-settings');
assert.strictEqual(errorMessages.length, 1);
assert.ok(errorMessages[0].includes('cppcheck.json'));
});

test('Invalid arguments type reports error', () => {
writeSettingsFile('{"arguments": 5}');
assert.strictEqual(getArguments(config, workspaceRoot), '--from-vscode-settings');
assert.strictEqual(errorMessages.length, 1);
assert.ok(errorMessages[0].includes('"arguments" must be a string or an array of strings'));
});

test('Settings file that is not an object reports error', () => {
writeSettingsFile('["--enable=style"]');
assert.strictEqual(getArguments(config, workspaceRoot), '--from-vscode-settings');
assert.strictEqual(errorMessages.length, 1);
});

test('Error is reported again after settings file has been fixed and broken again', () => {
writeSettingsFile('{"arguments": 5}');
getArguments(config, workspaceRoot);
writeSettingsFile('{"arguments": "--fixed"}');
assert.strictEqual(getArguments(config, workspaceRoot), '--fixed');
writeSettingsFile('{"arguments": 5}');
getArguments(config, workspaceRoot);
assert.strictEqual(errorMessages.length, 2);
});
});
64 changes: 64 additions & 0 deletions src/util/settingsFile.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
import * as fs from 'fs';
import * as path from 'path';
import * as vscode from 'vscode';
import { findWorkspaceRoot } from './path';

// Shared settings file that can be committed to the repository so that all developers get the same settings.
// Settings in this file take precedence over the VS Code settings.
export const settingsFileName = 'cppcheck.json';

interface SettingsFile {
arguments?: string;
}

// Remember the last reported error so that the user is not spammed with the same message on every analysis
let lastReportedError = '';

export function getSettingsFilePath(workspaceRoot: string = findWorkspaceRoot()): string {
return path.join(workspaceRoot, settingsFileName);
}

function readSettingsFile(workspaceRoot: string): SettingsFile | undefined {
const filePath = getSettingsFilePath(workspaceRoot);
if (!fs.existsSync(filePath)) {
lastReportedError = '';
return undefined;
}

try {
const content = JSON.parse(fs.readFileSync(filePath, 'utf8'));
if (typeof content !== 'object' || content === null || Array.isArray(content)) {
throw new Error('expected a JSON object');
}

const settings: SettingsFile = {};
if (content.arguments !== undefined) {
// Arguments may be given either as a string or as an array of strings
if (typeof content.arguments === 'string') {
settings.arguments = content.arguments;
} else if (Array.isArray(content.arguments) && content.arguments.every((a: unknown) => typeof a === 'string')) {
settings.arguments = content.arguments.join(' ');
} else {
throw new Error('"arguments" must be a string or an array of strings');
}
}
lastReportedError = '';
return settings;
} catch (error) {
const message = `Cppcheck: Failed to read ${filePath}: ${error instanceof Error ? error.message : error}`;
if (message !== lastReportedError) {
lastReportedError = message;
vscode.window.showErrorMessage(message);
}
return undefined;
}
}

// Get cppcheck arguments, from the settings file if it specifies them, otherwise from the VS Code settings
export function getArguments(config: vscode.WorkspaceConfiguration, workspaceRoot: string = findWorkspaceRoot()): string {
const settings = readSettingsFile(workspaceRoot);
if (settings?.arguments !== undefined) {
return settings.arguments;
}
return config.get<string>("cppcheck-official.arguments", "");
}
2 changes: 1 addition & 1 deletion walkthrough/arguments.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

Cppcheck works best for you if you set it up according to your needs. You may e.g. want to disable certain kinds of warnings, and if you have cppcheck premium you will need to set this up for it to work in this extension. All of this is done through the extension property 'arguments', with the different flags or arguments available being detailed in the [official cppcheck documentation](https://files.cppchecksolutions.com/manual.pdf#page=13).

It is recommended to set up these settings in the workspace settings so that you and your team easily can work with the same set up. Furthermore you are likely to want to have the same settings between VS Code and your CI workflows. This is most easily done through project files, which are referenced from the argument setting (see [documentation](https://files.cppchecksolutions.com/manual.pdf#page=4) for how to create a project file).
To make sure that you and your team work with the same set up, it is recommended to put the arguments in a `cppcheck.json` file in the root of your workspace and commit it to version control, e.g. `{ "arguments": "--project=build/compile_commands.json" }`. Arguments in this file take precedence over the arguments setting. Furthermore you are likely to want to have the same settings between VS Code and your CI workflows. This is most easily done through project files, which are referenced from the argument setting (see [documentation](https://files.cppchecksolutions.com/manual.pdf#page=4) for how to create a project file).

![image showing project file path being set in arguments property](../images/project_file.png)

Expand Down
Loading