Skip to content

Commit cdf8b1a

Browse files
authored
feature / #77 show warnings for included header files along with source files (#84)
* feature / #77 show warnings for included header files along with source files * keep track of header errors generated from source file to decide when to clear them from diagnostics * fixed cases where diagnostics were cleared when it should not have been
1 parent 4e479d3 commit cdf8b1a

1 file changed

Lines changed: 65 additions & 15 deletions

File tree

src/extension.ts

Lines changed: 65 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ import { looksLikePath, resolvePath, findWorkspaceRoot } from './util/path';
99

1010
// To keep track of document changes we save hashed versions of their content to this record
1111
let documentHashMemory : Record<string, string> = {};
12+
// To keep track of warnings for files created from analysis of other files we save their relations to fileRelationMap
13+
let fileRelationMap: Record<string, Set<string>> = {};
1214

1315
let previewAnalysisTimer: NodeJS.Timeout | undefined;
1416
let previewedDocument: vscode.TextDocument | undefined;
@@ -114,6 +116,22 @@ export async function activate(context: vscode.ExtensionContext) {
114116
const diagnosticCollection = vscode.languages.createDiagnosticCollection("Cppcheck");
115117
context.subscriptions.push(diagnosticCollection);
116118

119+
function clearDiagnosticForDoc(doc: vscode.TextDocument): void {
120+
// Any file who was warnings generated from (and only from) the closed doc have their diagnostics cleared
121+
// NOTE: This includes the closed doc - its diagnostics will only be cleared if its warnings only come from analysis of it itself
122+
for (const fileUri of Object.keys(fileRelationMap)) {
123+
if (fileRelationMap[fileUri].has(doc.uri.toString())) {
124+
if (fileRelationMap[fileUri].size <= 1) {
125+
diagnosticCollection.delete(vscode.Uri.parse(fileUri));
126+
fileRelationMap[fileUri].clear();
127+
} else {
128+
fileRelationMap[fileUri].delete(doc.uri.toString());
129+
}
130+
}
131+
}
132+
documentHashMemory[doc.fileName] = '';
133+
}
134+
117135
async function handleDocument(document: vscode.TextDocument) {
118136
// Only process C/C++ files.
119137
if (!["c", "cpp"].includes(document.languageId)) {
@@ -164,8 +182,7 @@ export async function activate(context: vscode.ExtensionContext) {
164182

165183
// If disabled, clear any existing diagnostics for this doc.
166184
if (!isEnabled) {
167-
diagnosticCollection.delete(document.uri);
168-
documentHashMemory[document.fileName] = '';
185+
clearDiagnosticForDoc(document);
169186
return;
170187
}
171188

@@ -217,13 +234,22 @@ export async function activate(context: vscode.ExtensionContext) {
217234
}
218235
}
219236
}
237+
for (const tab of e.closed) {
238+
if (tab.input instanceof vscode.TabInputText) {
239+
const uri = tab.input.uri;
240+
const document =
241+
vscode.workspace.textDocuments.find(
242+
doc => doc.uri.toString() === uri.toString()
243+
) ?? await vscode.workspace.openTextDocument(uri);
244+
clearDiagnosticForDoc(document);
245+
}
246+
}
220247
}, null, context.subscriptions);
221248

222249
// Clear diagnostics of previewed files when no longer viewed
223250
vscode.window.onDidChangeActiveTextEditor(() => {
224251
if (previewedDocument) {
225-
diagnosticCollection.delete(previewedDocument.uri);
226-
documentHashMemory[previewedDocument.fileName] = '';
252+
clearDiagnosticForDoc(previewedDocument);
227253
previewedDocument = undefined;
228254
}
229255
});
@@ -238,8 +264,7 @@ export async function activate(context: vscode.ExtensionContext) {
238264

239265
// Clean up diagnostics when a file is closed
240266
vscode.workspace.onDidCloseTextDocument((document: vscode.TextDocument) => {
241-
diagnosticCollection.delete(document.uri);
242-
documentHashMemory[document.fileName] = '';
267+
clearDiagnosticForDoc(document);
243268
}, null, context.subscriptions);
244269
}
245270

@@ -272,7 +297,7 @@ async function runCppcheckOnFileXML(
272297
return arg;
273298
});
274299

275-
let proc;
300+
let usingProjectFile = false;
276301
const args = [
277302
'--enable=all',
278303
'--inline-suppr',
@@ -283,10 +308,13 @@ async function runCppcheckOnFileXML(
283308
...argsParsed,
284309
].filter(Boolean);
285310
if (processedArgs.includes("--project")) {
311+
usingProjectFile = true;
286312
args.push(`--file-filter=${filePath}`);
287313
} else {
288314
args.push(filePath);
289315
}
316+
317+
let proc;
290318
const cwd = findWorkspaceRoot();
291319
proc = cp.spawn(commandPath, args, {
292320
cwd,
@@ -313,14 +341,14 @@ async function runCppcheckOnFileXML(
313341
vscode.window.showErrorMessage(errorMessage);
314342
}
315343
const parser = new xml2js.Parser({ explicitArray: true });
316-
parser.parseString(xmlOutput, (err, result) => {
344+
parser.parseString(xmlOutput, async (err, result) => {
317345
if (err) {
318346
console.error("XML parse error:", err);
319347
return;
320348
}
321349

322350
const errors = result.results?.errors?.[0]?.error || [];
323-
const diagnostics: vscode.Diagnostic[] = [];
351+
const diagnostics: Record<string, vscode.Diagnostic[]> = {};
324352

325353
for (const e of errors) {
326354
const isCriticalError = criticalWarningTypes.includes(e.$.id);
@@ -331,8 +359,8 @@ async function runCppcheckOnFileXML(
331359

332360
const mainLoc = locations[locations.length - 1].$;
333361

334-
// If main location is not current file, then skip displaying warning unless it is critical
335-
if (!isCriticalError && !filePath.endsWith(mainLoc.file)) {
362+
// If main location is not current file, we are not using a project file and warning is not critical then skip displaying warning
363+
if (!isCriticalError && usingProjectFile && !filePath.endsWith(mainLoc.file)) {
336364
continue;
337365
}
338366

@@ -387,20 +415,42 @@ async function runCppcheckOnFileXML(
387415
lLine, lCol,
388416
lLine, document.lineAt(lLine).text.length
389417
);
390-
418+
const relatedDocument = await vscode.workspace.openTextDocument(loc.file);
391419
relatedInfos.push(
392420
new vscode.DiagnosticRelatedInformation(
393-
new vscode.Location(document.uri, relatedRange),
421+
new vscode.Location(relatedDocument?.uri ?? '', relatedRange),
394422
msg
395423
)
396424
);
397425
}
398426
if (relatedInfos.length > 0) {
399427
diagnostic.relatedInformation = relatedInfos;
400428
}
401-
diagnostics.push(diagnostic);
429+
const diagnosticFile = mainLoc.file;
430+
if (diagnosticFile === document.fileName) {
431+
const uri = document.uri.toString();
432+
if (diagnostics[uri] === null || diagnostics[uri] === undefined) {
433+
diagnostics[uri] = [];
434+
}
435+
diagnostics[uri].push(diagnostic);
436+
} else {
437+
const relatedDocument = await vscode.workspace.openTextDocument(mainLoc.file);
438+
const uri = relatedDocument.uri.toString();
439+
if (diagnostics[uri] === null || diagnostics[uri] === undefined) {
440+
diagnostics[uri] = [];
441+
}
442+
diagnostics[uri].push(diagnostic);
443+
}
444+
}
445+
const sourceDocumentUri = document.uri.toString();
446+
for (const uri of Object.keys(diagnostics)) {
447+
diagnosticCollection.set(vscode.Uri.parse(uri), diagnostics[uri]);
448+
if (fileRelationMap[uri] === null ||fileRelationMap[uri] === undefined) {
449+
fileRelationMap[uri] = new Set;
450+
}
451+
// NOTE: uri can be the same as sourceDocumentUri
452+
fileRelationMap[uri].add(sourceDocumentUri);
402453
}
403-
diagnosticCollection.set(document.uri, diagnostics);
404454
});
405455

406456
// If checks have run without error, save hashed document content to memory

0 commit comments

Comments
 (0)