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
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
- **Warning notes**: Display notes for warnings when those are available
- **Dynamic config**: The extension supports running a script to generate arguments to pass to cppcheck. This can be done by including the command in the argument field wrapped with \@(), e.g. `--suppress=memleak:src/file1.cpp @(bash path/to/script.sh)`. The script is expected to output the argument(s) wrapped with \@(). If the script e.g. creates a project file it should print out as `@(--project=path/to/projectfile.json)`. This output will be spliced into the argument string as such: `--suppress=memleak:src/file1.cpp --project=path/to/projectfile.json`.

- **Warning suppression**: Warnings of a specific type can be supressed with the --supress flag in the argument field in the extension settings. The extension also supports inline suppression for specific lines of code, simply write `// cppcheck-supress >warning id<` (see image below).
- **Warning suppression**: Warnings of a specific type can be supressed with the --suppress flag in the argument field in the extension settings. The extension also supports inline suppression for specific lines of code, simply write `// cppcheck-suppress >warning id<` (see image below).
![Image showing how to suppress warnings](./images/suppression.png)
## Requirements

Expand Down
38 changes: 38 additions & 0 deletions src/extension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ import { documentationLinkMap, getPremiumCertLink } from './util/documentation';
import { runCommand } from './util/scripts';
import { looksLikePath, resolvePath, findWorkspaceRoot } from './util/path';
import { diagnosticsUnion } from './util/diagnostics';
import { CodeActionProvider } from './util/codeActions';
import { writeSuppressionToProjectFile } from './util/files';

// To keep track of document changes we save hashed versions of their content to this record
let documentHashMemory : Record<string, string> = {};
Expand All @@ -17,6 +19,7 @@ let previewAnalysisTimer: NodeJS.Timeout | undefined;
let previewedDocument: vscode.TextDocument | undefined;
let cppcheckProgressIndicator: vscode.StatusBarItem;
let checksRunning = false;
let cppcheckProjectFileUri: vscode.Uri | undefined;

enum SeverityNumber {
Info = 0,
Expand Down Expand Up @@ -95,6 +98,18 @@ function getDocumentSha1(document: vscode.TextDocument): string {
// This method is called when your extension is activated.
// Your extension is activated the very first time the command is executed.
export async function activate(context: vscode.ExtensionContext) {
// Set up code actions provider
context.subscriptions.push(
vscode.languages.registerCodeActionsProvider(
{ pattern: "**/*" },
new CodeActionProvider(),
{
providedCodeActionKinds: [
vscode.CodeActionKind.QuickFix
]
}
)
);

// Register a command to push user to workspace settings from walkthrough
context.subscriptions.push(
Expand All @@ -108,6 +123,20 @@ export async function activate(context: vscode.ExtensionContext) {
}
)
);

// Register a command for suppressing a warning type
context.subscriptions.push(
vscode.commands.registerCommand(
"cppcheck-official.suppressWarningAll",
async (warningType : String) => {
if (cppcheckProjectFileUri) {
await writeSuppressionToProjectFile(cppcheckProjectFileUri, warningType);
} else {
vscode.window.showInformationMessage(`Adding suppression is currently only supported for .cppcheck project files`);
}
}
)
);

// ProgressIndicator status bar item to show when checks are running
cppcheckProgressIndicator = vscode.window.createStatusBarItem(vscode.StatusBarAlignment.Left, 10);
Expand Down Expand Up @@ -299,6 +328,8 @@ async function runCppcheckOnFileXML(
});

let usingProjectFile = false;
cppcheckProjectFileUri = undefined;

const args = [
'--enable=all',
'--inline-suppr',
Expand All @@ -308,9 +339,16 @@ async function runCppcheckOnFileXML(
'--suppress=missingIncludeSystem',
...argsParsed,
].filter(Boolean);

if (processedArgs.includes("--project")) {
usingProjectFile = true;
args.push(`--file-filter=${filePath}`);
// If project file is of type .cppcheck we keep track of it
var projectFilePath = processedArgs.split('--project=')[1].split(' ')[0];
var projectFileType = projectFilePath.split('.')[1];
if (projectFileType.toLowerCase() === 'cppcheck') {
cppcheckProjectFileUri = vscode.Uri.file(projectFilePath);
}
} else {
args.push(filePath);
}
Expand Down
61 changes: 61 additions & 0 deletions src/util/codeActions.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
import * as vscode from 'vscode';

export class CodeActionProvider implements vscode.CodeActionProvider {
provideCodeActions(
document: vscode.TextDocument,
range: vscode.Range,
context: vscode.CodeActionContext,
token: vscode.CancellationToken
): vscode.CodeAction[] {

const actions: vscode.CodeAction[] = [];

for (const diagnostic of context.diagnostics) {
var diagnosticCode = diagnostic.code;
if (typeof(diagnosticCode) === "object" && typeof(diagnosticCode) !== null) {
diagnosticCode = diagnosticCode.value;
}

// Set up one action for suppressing the specific warning on the line targeted by the diagnostic
const suppressAction = new vscode.CodeAction(
`Suppress warning for ${diagnosticCode} here`,
vscode.CodeActionKind.QuickFix
);

// Copy indentation from line affected by diagnostic
const lineText = document.lineAt(diagnostic.range.start.line).text;
const indent = lineText.match(/^\s*/)?.[0] ?? "";

// Insert suppression comment above affected line
const suppressLineEdit = new vscode.WorkspaceEdit();
suppressLineEdit.insert(
document.uri,
new vscode.Position(
diagnostic.range.start.line,
0,
),
`${indent}// cppcheck-suppress ${diagnosticCode}\n`
);
suppressAction.edit = suppressLineEdit;
suppressAction.diagnostics = [diagnostic];
actions.push(suppressAction);

// Set up one action for suppressing warning of a given type universally
const suppressTypeAction = new vscode.CodeAction(
`Suppress warning type ${diagnosticCode} universally`,
vscode.CodeActionKind.QuickFix
);

suppressTypeAction.command = {
command: "cppcheck-official.suppressWarningAll",
title: "Suppress warning here",
arguments: [diagnostic]
};

suppressTypeAction.diagnostics = [diagnostic];
actions.push(suppressTypeAction);
}

return actions;
}
}
55 changes: 55 additions & 0 deletions src/util/files.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import * as vscode from 'vscode';

export async function writeSuppressionToProjectFile(projectFileUri : vscode.Uri, warningType : String) {
const fileType = projectFileUri.toString().split('.')[1];
if (fileType !== 'cppcheck') {
throw new Error(`Function writeSuppressionToProjectFile only supports writing to .cppcheck project files! Recieved file is of type .${fileType}`);
}

// Open project file with vscode workspace API
const document = await vscode.workspace.openTextDocument(projectFileUri);
const text = document.getText();
const edit = new vscode.WorkspaceEdit();
var textToInsert = '';
var positionToInsertAt = 0;

// Search for suppressions section
const match = /<suppressions\b[^>]*>([\s\S]*?)<\/suppressions>/m.exec(text);
if (match !== null) {
// match !== null -> Suppressions section exists
const closeIndex = text.indexOf("</suppressions>");

// Determine indentation and construct the new suppression line
const line = document.lineAt(document.positionAt(closeIndex).line - 1);
const indentation = line.text.match(/^\s*/)?.[0] ?? " ";
const newSuppressionLine = `\n${indentation}<suppression id="${warningType}"/>\n`;

// We splice in the new line just before the end of the suppressions block
textToInsert = newSuppressionLine;
positionToInsertAt = closeIndex;
} else {
// match === null -> Suppressions section does not exist
const closeIndex = text.indexOf("</project>");

// Determine indentation and construct the new suppressions block
const line = document.lineAt(document.positionAt(closeIndex).line - 1);
const indentation = line.text.match(/^\s*/)?.[0] ?? " ";
const suppressionsBlock =
`\n${indentation}<suppressions>
${indentation} <suppression id="${warningType}"/>
${indentation}</suppressions>\n`;

// Splice in the new suppressions block just before the end of the project-file
textToInsert = suppressionsBlock;
positionToInsertAt = closeIndex;
}

// Write file
edit.insert(
document.uri,
document.positionAt(positionToInsertAt),
textToInsert
);

await vscode.workspace.applyEdit(edit);
}
Loading