diff --git a/src/extension.ts b/src/extension.ts index c8436a8..ab87963 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -6,6 +6,7 @@ import * as crypto from 'crypto'; import { documentationLinkMap, getPremiumCertLink } from './util/documentation'; import { runCommand } from './util/scripts'; import { looksLikePath, resolvePath, findWorkspaceRoot } from './util/path'; +import { diagnosticsUnion } from './util/diagnostics'; // To keep track of document changes we save hashed versions of their content to this record let documentHashMemory : Record = {}; @@ -349,7 +350,6 @@ async function runCppcheckOnFileXML( const errors = result.results?.errors?.[0]?.error || []; const diagnostics: Record = {}; - for (const e of errors) { const isCriticalError = criticalWarningTypes.includes(e.$.id); const locations = e.location || []; @@ -358,12 +358,13 @@ async function runCppcheckOnFileXML( } const mainLoc = locations[locations.length - 1].$; - // If main location is not current file, we are not using a project file and warning is not critical then skip displaying warning if (!isCriticalError && usingProjectFile && !filePath.endsWith(mainLoc.file)) { continue; } + const mainLocDocument = await vscode.workspace.openTextDocument(mainLoc.file); + // Cppcheck line number is 1-indexed, while VS Code uses 0-indexing let line = Number(mainLoc.line) - 1; // Invalid line number usually means non-analysis output @@ -377,7 +378,7 @@ async function runCppcheckOnFileXML( // Cppcheck col number is 1-indexed, while VS Code uses 0-indexing let col = Number(mainLoc.column) - 1; - if (isNaN(col) || col < 0 || col > document.lineAt(line).text.length) { + if (isNaN(col) || col < 0 || col > mainLocDocument.lineAt(line).text.length) { col = 0; } @@ -386,7 +387,7 @@ async function runCppcheckOnFileXML( continue; } - const range = new vscode.Range(line, col, line, document.lineAt(line).text.length); + const range = new vscode.Range(line, col, line, mainLocDocument.lineAt(line).text.length); const diagnostic = new vscode.Diagnostic(range, e.$.msg, severity); diagnostic.source = "cppcheck"; // If we have a link to documentation, include it @@ -411,11 +412,11 @@ async function runCppcheckOnFileXML( continue; } + const relatedDocument = await vscode.workspace.openTextDocument(loc.file); const relatedRange = new vscode.Range( lLine, lCol, - lLine, document.lineAt(lLine).text.length + lLine, relatedDocument.lineAt(lLine).text.length ); - const relatedDocument = await vscode.workspace.openTextDocument(loc.file); relatedInfos.push( new vscode.DiagnosticRelatedInformation( new vscode.Location(relatedDocument?.uri ?? '', relatedRange), @@ -423,6 +424,7 @@ async function runCppcheckOnFileXML( ) ); } + if (relatedInfos.length > 0) { diagnostic.relatedInformation = relatedInfos; } @@ -444,7 +446,13 @@ async function runCppcheckOnFileXML( } const sourceDocumentUri = document.uri.toString(); for (const uri of Object.keys(diagnostics)) { - diagnosticCollection.set(vscode.Uri.parse(uri), diagnostics[uri]); + var newDiagnostics = diagnostics[uri]; + // If file has existing diagnostics from analyzing other files we do not want to overwrite those + const existingDiagnostics = diagnosticCollection.get(vscode.Uri.parse(uri)); + if (existingDiagnostics) { + newDiagnostics = diagnosticsUnion(newDiagnostics, existingDiagnostics.flat()); + } + diagnosticCollection.set(vscode.Uri.parse(uri), newDiagnostics); if (fileRelationMap[uri] === null ||fileRelationMap[uri] === undefined) { fileRelationMap[uri] = new Set; } diff --git a/src/util/diagnostics.ts b/src/util/diagnostics.ts new file mode 100644 index 0000000..0054b1a --- /dev/null +++ b/src/util/diagnostics.ts @@ -0,0 +1,23 @@ +import * as vscode from 'vscode'; + +export function diagnosticsUnion(diagnosticsA : vscode.Diagnostic[], diagnosticB : vscode.Diagnostic[]) : vscode.Diagnostic[] { + const diagnosticsUnion = new Array; + // Add all elements from diagnosticsA to result array + diagnosticsUnion.push(...diagnosticsA); + + // Add all elements present in diagnosticsB but not in diagnosticsA to result array + for (const diagnostic of diagnosticB) { + if (!diagnosticsA.some((d) => { + if (typeof(diagnostic?.code) === "object" && typeof(diagnostic?.code) !== null && typeof(d?.code) === "object" && typeof(d?.code) !== null) { + return diagnostic.code.value === d.code.value && diagnostic.range.isEqual(d.range); + } else { + return diagnostic.code === d.code && diagnostic.range.isEqual(d.range); + } + })) { + diagnosticsUnion.push(diagnostic); + } + } + + // Return result + return diagnosticsUnion; +} \ No newline at end of file