diff --git a/compiler/src/dmd/errors.d b/compiler/src/dmd/errors.d index 3c3f5e6733ae..e3b1ca51bd8f 100644 --- a/compiler/src/dmd/errors.d +++ b/compiler/src/dmd/errors.d @@ -37,6 +37,23 @@ enum ErrorKind message, } +/** +Represents a diagnostic message generated during compilation, such as errors, warnings, or other messages. + +Params: + loc = The location in the source code where the diagnostic was generated (includes file, line, and column). + message = The text of the diagnostic message, describing the issue. + kind = The type of diagnostic, indicating whether it is an error, warning, deprecation, etc. +*/ +struct Diagnostic +{ + SourceLoc loc; + string message; + ErrorKind kind; +} + +Diagnostic[] diagnostics; // Global array to store diagnostics + /*************************** * Error message sink for D compiler. */ @@ -101,6 +118,153 @@ class ErrorSinkCompiler : ErrorSink verrorReport(loc, format, ap, ErrorKind.message); va_end(ap); } + + void plugSink() + { + // Only proceed if there are collected diagnostics + if (diagnostics.length > 0) + { + OutBuffer ob; + ob.doindent = true; + + // Extract and clean the version string + string toolVersion = global.versionString(); + // Remove 'v' prefix if it exists + if (toolVersion.length > 0 && toolVersion[0] == 'v') { + toolVersion = toolVersion[1 .. $]; + } + // Find the first non-numeric character after the version number + size_t length = toolVersion.length; + const(char)* nonNumeric = strchr(toolVersion.ptr, '-'); + if (nonNumeric) { + length = cast(size_t)(nonNumeric - toolVersion.ptr); + } + string cleanedVersion = toolVersion[0 .. length]; + + // Build SARIF report + ob.writestringln("{"); + ob.level = 1; + ob.writestringln(`"version": "2.1.0",`); + ob.writestringln(`"$schema": "https://schemastore.azurewebsites.net/schemas/json/sarif-2.1.0.json",`); + ob.writestringln(`"runs": [{`); + + // Tool Information + ob.level += 1; + ob.writestringln(`"tool": {`); + ob.level += 1; + ob.writestringln(`"driver": {`); + ob.level += 1; + + // Write "name" field + ob.writestring(`"name": "`); + ob.writestring(global.compileEnv.vendor.ptr); + ob.writestringln(`",`); + + // Write "version" field + ob.writestring(`"version": "`); + ob.writestring(cleanedVersion); + ob.writestringln(`",`); + ob.writestring(`"informationUri": "https://dlang.org/dmd.html"`); + ob.writestringln(""); + ob.level -= 1; + ob.writestringln("}"); + ob.level -= 1; + ob.writestringln("},"); + + // Invocation Information + ob.writestringln(`"invocations": [{`); + ob.level += 1; + ob.writestring(`"executionSuccessful": false`); + ob.writestringln(""); + ob.level -= 1; + ob.writestringln("}],"); + + // Results Array + ob.writestringln(`"results": [`); + ob.level += 1; + + foreach (idx, diag; diagnostics) + { + ob.writestringln("{"); + ob.level += 1; + + // Rule ID + ob.writestring(`"ruleId": "DMD-ERROR",`); + ob.writestringln(""); + + // Message Information + ob.writestringln(`"message": {`); + ob.level += 1; + ob.writestring(`"text": "`); + ob.writestring(diag.message); + ob.writestringln(`"`); + ob.level -= 1; + ob.writestringln("},"); + + // Error Severity Level + ob.writestring(`"level": "error",`); + ob.writestringln(""); + + // Location Information + ob.writestringln(`"locations": [{`); + ob.level += 1; + ob.writestringln(`"physicalLocation": {`); + ob.level += 1; + + // Artifact Location + ob.writestringln(`"artifactLocation": {`); + ob.level += 1; + ob.writestring(`"uri": "`); + ob.writestring(diag.loc.filename); + ob.writestringln(`"`); + ob.level -= 1; + ob.writestringln("},"); + + + // Region Information + ob.writestringln(`"region": {`); + ob.level += 1; + ob.writestring(`"startLine": `); + ob.printf(`%d,`, diag.loc.linnum); + ob.writestringln(""); + ob.writestring(`"startColumn": `); + ob.printf(`%d`, diag.loc.charnum); + ob.writestringln(""); + ob.level -= 1; + ob.writestringln("}"); + + // Close physicalLocation and locations + ob.level -= 1; + ob.writestringln("}"); + ob.level -= 1; + ob.writestringln("}]"); + + // Closing brace for each diagnostic item + ob.level -= 1; + if (idx < diagnostics.length - 1) { + ob.writestringln("},"); + } else { + ob.writestringln("}"); + } + } + + // Close results, run, and JSON document + ob.level -= 1; + ob.writestringln("]"); + ob.level -= 1; + ob.writestringln("}]"); + ob.level -= 1; + ob.writestringln("}"); + + // Print the SARIF output to stdout + const(char)* sarifOutput = ob.extractChars(); + fputs(sarifOutput, stdout); + fflush(stdout); + + // Clear diagnostics after generating the report + diagnostics.length = 0; + } + } } @@ -467,6 +631,14 @@ private extern(C++) void verrorReport(const Loc loc, const(char)* format, va_lis private extern(C++) void verrorReport(const SourceLoc loc, const(char)* format, va_list ap, ErrorKind kind, const(char)* p1 = null, const(char)* p2 = null) { auto info = ErrorInfo(loc, kind, p1, p2); + + // Buffer to hold the fully formatted message + char[1024] buffer; + int written = vsnprintf(buffer.ptr, buffer.length, format, ap); + + // Handle any truncation that may have occurred + string formattedMessage = cast(string) buffer[0 .. (written < 0 ? 0 : written)].dup; + final switch (info.kind) { case ErrorKind.error: @@ -476,7 +648,7 @@ private extern(C++) void verrorReport(const SourceLoc loc, const(char)* format, info.headerColor = Classification.error; if (global.params.v.messageStyle == MessageStyle.sarif) { - generateSarifReport(loc, format, ap, info.kind, false); + diagnostics ~= Diagnostic(loc, formattedMessage, kind); return; } verrorPrint(format, ap, info); @@ -510,7 +682,7 @@ private extern(C++) void verrorReport(const SourceLoc loc, const(char)* format, info.headerColor = Classification.deprecation; if (global.params.v.messageStyle == MessageStyle.sarif) { - generateSarifReport(loc, format, ap, info.kind, false); + diagnostics ~= Diagnostic(loc, formattedMessage, kind); return; } verrorPrint(format, ap, info); @@ -531,7 +703,7 @@ private extern(C++) void verrorReport(const SourceLoc loc, const(char)* format, info.headerColor = Classification.warning; if (global.params.v.messageStyle == MessageStyle.sarif) { - generateSarifReport(loc, format, ap, info.kind, false); + diagnostics ~= Diagnostic(loc, formattedMessage, kind); return; } verrorPrint(format, ap, info); @@ -551,7 +723,7 @@ private extern(C++) void verrorReport(const SourceLoc loc, const(char)* format, info.headerColor = Classification.tip; if (global.params.v.messageStyle == MessageStyle.sarif) { - generateSarifReport(loc, format, ap, info.kind, false); + diagnostics ~= Diagnostic(loc, formattedMessage, kind); return; } verrorPrint(format, ap, info); @@ -571,7 +743,7 @@ private extern(C++) void verrorReport(const SourceLoc loc, const(char)* format, fflush(stdout); // ensure it gets written out in case of compiler aborts if (global.params.v.messageStyle == MessageStyle.sarif) { - generateSarifReport(loc, format, ap, info.kind, false); + diagnostics ~= Diagnostic(loc, formattedMessage, kind); return; } return; diff --git a/compiler/test/fail_compilation/sarif_test.d b/compiler/test/fail_compilation/sarif_test.d index 541ff1fa4eac..c199dd214bc7 100644 --- a/compiler/test/fail_compilation/sarif_test.d +++ b/compiler/test/fail_compilation/sarif_test.d @@ -16,24 +16,26 @@ TEST_OUTPUT: "invocations": [{ "executionSuccessful": false }], - "results": [{ - "ruleId": "DMD-ERROR", - "message": { - "text": "undefined identifier `x`" - }, - "level": "error", - "locations": [{ - "physicalLocation": { - "artifactLocation": { - "uri": "fail_compilation/sarif_test.d" - }, - "region": { - "startLine": 43, - "startColumn": 5 + "results": [ + { + "ruleId": "DMD-ERROR", + "message": { + "text": "undefined identifier `x`" + }, + "level": "error", + "locations": [{ + "physicalLocation": { + "artifactLocation": { + "uri": "fail_compilation/sarif_test.d" + }, + "region": { + "startLine": 45, + "startColumn": 5 + } } - } - }] - }] + }] + } + ] }] } --- diff --git a/compiler/test/fail_compilation/sarifmultiple_test.d b/compiler/test/fail_compilation/sarifmultiple_test.d new file mode 100644 index 000000000000..57291271e62b --- /dev/null +++ b/compiler/test/fail_compilation/sarifmultiple_test.d @@ -0,0 +1,65 @@ +// REQUIRED_ARGS: -verror-style=sarif +/* +TEST_OUTPUT: +--- +{ + "version": "2.1.0", + "$schema": "https://schemastore.azurewebsites.net/schemas/json/sarif-2.1.0.json", + "runs": [{ + "tool": { + "driver": { + "name": "Digital Mars D", + "version": "$r:\d+\.\d+\.\d+$", + "informationUri": "https://dlang.org/dmd.html" + } + }, + "invocations": [{ + "executionSuccessful": false + }], + "results": [ + { + "ruleId": "DMD-ERROR", + "message": { + "text": "undefined identifier `x`" + }, + "level": "error", + "locations": [{ + "physicalLocation": { + "artifactLocation": { + "uri": "fail_compilation/sarifmultiple_test.d" + }, + "region": { + "startLine": 63, + "startColumn": 5 + } + } + }] + }, + { + "ruleId": "DMD-ERROR", + "message": { + "text": "undefined identifier `y`" + }, + "level": "error", + "locations": [{ + "physicalLocation": { + "artifactLocation": { + "uri": "fail_compilation/sarifmultiple_test.d" + }, + "region": { + "startLine": 64, + "startColumn": 5 + } + } + }] + } + ] + }] +} +--- +*/ + +void main() { + x = 5; // Undefined variable to trigger the error + y = 5; // Undefined variable to trigger the error +}