From c38dc7419c8abca00c84e8e4f318e3fae6640571 Mon Sep 17 00:00:00 2001 From: Iskaban10 Date: Tue, 12 May 2026 23:52:20 +0530 Subject: [PATCH 1/2] Diagnostic reporting style for dfa engine --- compiler/include/dmd/globals.h | 3 +- compiler/src/build.d | 3 +- compiler/src/dmd/cli.d | 3 +- compiler/src/dmd/diagreport/defs.d | 59 +++ compiler/src/dmd/diagreport/geometry.d | 426 ++++++++++++++++++++++ compiler/src/dmd/diagreport/glue.d | 302 ++++++++++++++++ compiler/src/dmd/diagreport/renderer.d | 482 +++++++++++++++++++++++++ compiler/src/dmd/errors.d | 112 +++++- compiler/src/dmd/frontend.h | 1 + compiler/src/dmd/location.d | 5 +- compiler/src/dmd/mars.d | 5 +- 11 files changed, 1395 insertions(+), 6 deletions(-) create mode 100644 compiler/src/dmd/diagreport/defs.d create mode 100644 compiler/src/dmd/diagreport/geometry.d create mode 100644 compiler/src/dmd/diagreport/glue.d create mode 100644 compiler/src/dmd/diagreport/renderer.d diff --git a/compiler/include/dmd/globals.h b/compiler/include/dmd/globals.h index 2eb7779f6279..b65f1b12dd66 100644 --- a/compiler/include/dmd/globals.h +++ b/compiler/include/dmd/globals.h @@ -35,7 +35,8 @@ enum class MessageStyle : unsigned char { digitalmars, // file(line,column): message gnu, // file:line:column: message - sarif // JSON SARIF output, see https://docs.oasis-open.org/sarif/sarif/v2.1.0/sarif-v2.1.0.html + sarif, // JSON SARIF output, see https://docs.oasis-open.org/sarif/sarif/v2.1.0/sarif-v2.1.0.html + diagreport // diagnostics reporting messagestyle }; // The state of array bounds checking diff --git a/compiler/src/build.d b/compiler/src/build.d index f8c608ff1d75..f6f715a38dcf 100755 --- a/compiler/src/build.d +++ b/compiler/src/build.d @@ -1571,7 +1571,7 @@ auto sourceFiles() glue/s2ir.d glue/tocsym.d glue/toctype.d glue/tocvdebug.d glue/todt.d glue/toir.d glue/toobj.d "), driver: fileArray(env["D"], "dinifile.d dmdparams.d lib/package.d lib/elf.d lib/mach.d lib/mscoff.d - link.d mars.d main.d sarif.d lib/scanelf.d lib/scanmach.d lib/scanmscoff.d timetrace.d vsoptions.d + link.d mars.d main.d sarif.d diagreport/glue.d lib/scanelf.d lib/scanmach.d lib/scanmscoff.d timetrace.d vsoptions.d "), frontend: fileArray(env["D"], " access.d aggregate.d aliasthis.d argtypes_x86.d argtypes_sysv_x64.d argtypes_aarch64.d arrayop.d @@ -1592,6 +1592,7 @@ auto sourceFiles() visitor/strict.d visitor/transitive.d cparse.d dfa/entry.d dfa/utils.d dfa/fast/structure.d dfa/fast/analysis.d dfa/fast/report.d dfa/fast/expression.d dfa/fast/statement.d + diagreport/defs.d diagreport/geometry.d diagreport/renderer.d "), backendHeaders: fileArray(env["C"], " cc.d cdef.d cgcv.d code.d dt.d el.d global.d diff --git a/compiler/src/dmd/cli.d b/compiler/src/dmd/cli.d index 798966df24a3..0ba210c05271 100644 --- a/compiler/src/dmd/cli.d +++ b/compiler/src/dmd/cli.d @@ -935,7 +935,7 @@ dmd -cov -unittest myprog.d Option("vcolumns", "print character (column) numbers in diagnostics" ), - Option("verror-style=[digitalmars|gnu|sarif]", + Option("verror-style=[digitalmars|gnu|sarif|diagreport]", "set the style for file/line number annotations on compiler messages", `Set the style for file/line number annotations on compiler messages, where: @@ -943,6 +943,7 @@ dmd -cov -unittest myprog.d $(DT digitalmars)$(DD 'file(line[,column]): message'. This is the default.) $(DT gnu)$(DD 'file:line[:column]: message', conforming to the GNU standard used by gcc and clang.) $(DT sarif)$(DD 'Generates JSON output conforming to the SARIF (Static Analysis Results Interchange Format) standard, useful for integration with tools like GitHub and other SARIF readers.') + $(DT diagreport)$(DD 'Generates diagnostic report of errors, warnings, deprecations and tips.') )`, ), Option("verror-supplements=", diff --git a/compiler/src/dmd/diagreport/defs.d b/compiler/src/dmd/diagreport/defs.d new file mode 100644 index 000000000000..7c5648f1717a --- /dev/null +++ b/compiler/src/dmd/diagreport/defs.d @@ -0,0 +1,59 @@ +/** + Defines the vertical state of a diagnostic on a given line for drawing purposes. + This enumeration captures the vertical state machine logic for a single diagnostic span. +*/ +module dmd.diagreport.defs; + +enum LineClassification +{ + /// The line is not part of the diagnostic span. + Inactive, + /// The line is the start of the span and continues below. Implies the previous line was Inactive. + SpanStart, + /// The line is between the start and end. Implies the previous line and next line are Active. + SpanContinue, + /// The line is the end of the span and no other active diagnostics follow it. Implies the next line is Inactive. + SpanEnd, + /// The span starts and ends on the same line (a one-line diagnostic). + SpanStartEnd +} + +struct Diagnostic +{ + /// The line number where the diagnostic span begins (inclusive). + int start; + /// The line number where the diagnostic span ends (inclusive). + int end; + + Message startMessage; + Message endMessage; + + size_t id; + + size_t offset() + { + return originalOffset; + } + +package: + size_t originalOffset; + int column; +} + +struct Message +{ + int startColumn, endColumn; + bool isMultiline; + + size_t id; +} + +struct Help +{ + Diagnostic[] diagnostics; + + Message startMessage; + Message endMessage; + + size_t id; +} diff --git a/compiler/src/dmd/diagreport/geometry.d b/compiler/src/dmd/diagreport/geometry.d new file mode 100644 index 000000000000..681b29c1bbca --- /dev/null +++ b/compiler/src/dmd/diagreport/geometry.d @@ -0,0 +1,426 @@ +/** + Defines the elements of the geometry of the diagnostic reporting mechanism +**/ +module dmd.diagreport.geometry; +import dmd.diagreport.defs; + +struct TimeLineGeometry +{ + Diagnostic[] diagnostics; + + int minToSkip; + int toSkipBuffer; + + void delegate(int line, ref Diagnostic, LineClassification classification) nothrow columnDrawHandler; + void delegate(int line, bool haveStartOrEndColumnsToLeft, bool previousLineColumnIsActive) nothrow columnEmptyHandler; + void delegate(int line) nothrow onLineStart; + void delegate(int line) nothrow onLineEnd; + void delegate(int line) nothrow onLineSource; + void delegate(int startLine, int endLine) nothrow onLinesSkippedBeforeMargin; + void delegate(int startLine, int endLine) nothrow onLinesSkippedAfterMargin; + uint delegate(int line, int startColumn, int endColumn, ref Diagnostic diag, ref Message message) nothrow graphemesBetweenPositions; + void delegate(int line, int offsetToSquiggles, int numberOfSquiggles, + ref Diagnostic diag, ref Message message, bool spansMultipleLines) nothrow lineHighlight; + void delegate(int line, ref Diagnostic diag, ref Message message) nothrow printSingleLine; + void delegate(scope void delegate() nothrow printMargin, uint offsetToMessage, int line, + int offsetToSquiggles, int numberOfSquiggles, ref Diagnostic diag, ref Message message) nothrow printMultiLine; + + void calculate() nothrow + { + assignColumns; + + int lineNumber = diagnostics[0].start; + int allowBeforeSkipTo; + int skipTo; + + for (;;) + { + int lastColumnEmitted = -numberOfTokenColumns; + int minimumActiveColumn = -numberOfTokenColumns; + int numberOfEventsForThisLine; + int nextActiveLine = int.max; + + if (allowBeforeSkipTo > 0) + { + allowBeforeSkipTo--; + + if (allowBeforeSkipTo == 0) + { + onLinesSkippedBeforeMargin(lineNumber, skipTo); + processDiagnosticLineEvents(lineNumber, 0, + lastColumnEmitted, true, false, false); + onLinesSkippedAfterMargin(lineNumber, skipTo); + this.onLineEnd(lineNumber); + + lineNumber = skipTo; + continue; + } + } + else + { + foreach (ref diag; diagnostics) + { + bool startEndOnlyRange; + const classification = calculateLineClassification(diag, + lineNumber, startEndOnlyRange); + + if (classification != LineClassification.Inactive) + { + if (diag.column < minimumActiveColumn) + minimumActiveColumn = diag.column; + + if (classification != LineClassification.SpanContinue) + numberOfEventsForThisLine++; + } + + if (diag.end >= lineNumber) + { + const nextLine = diag.start >= lineNumber ? diag.start : diag.end; + if (nextLine < nextActiveLine) + nextActiveLine = nextLine; + } + } + + if (nextActiveLine == int.max) + break; + else if (nextActiveLine - lineNumber > minToSkip) + { + allowBeforeSkipTo = toSkipBuffer; + skipTo = nextActiveLine - toSkipBuffer; + } + } + + { + onLineStart(lineNumber); + + processDiagnosticLineEvents(lineNumber, minimumActiveColumn, + lastColumnEmitted, true, false, false); + + onLineSource(lineNumber); + onLineEnd(lineNumber); + + lastColumnEmitted = -numberOfTokenColumns; + } + + while ((numberOfColumns == 0 || lastColumnEmitted < numberOfColumns) + && numberOfEventsForThisLine > 0) + { + onLineStart(lineNumber); + processDiagnosticLineEvents(lineNumber, minimumActiveColumn, + lastColumnEmitted, false, false, true); + + numberOfEventsForThisLine--; + } + + lineNumber++; + } + } + +private: + int numberOfColumns; + int numberOfTokenColumns; + + void assignColumns() nothrow + { + { + foreach (diag1; diagnostics) + { + if (diag1.start == diag1.end) + { + uint overlappingDiagCount; + + foreach (diag2; diagnostics) + { + if (diag2.start != diag2.end || diag1.start != diag2.start) + continue; + overlappingDiagCount++; + } + + if (overlappingDiagCount > numberOfTokenColumns) + numberOfTokenColumns = overlappingDiagCount; + } + else + { + uint overlappingDiagCount; + + foreach (diag2; diagnostics) + { + if (diag2.start == diag2.end) + continue; + else if (diag1.end < diag2.start) + break; + + if (diag1.start <= diag2.end && diag1.end > diag2.start) + overlappingDiagCount++; + } + + if (overlappingDiagCount > numberOfColumns) + numberOfColumns = overlappingDiagCount; + } + } + } + + if (numberOfTokenColumns + numberOfColumns > 0) + { + int[] columnReleaseLine = new int[numberOfTokenColumns + numberOfColumns]; + + foreach (ref diag; diagnostics) + { + bool assigned; + + if (diag.start == diag.end) + { + if (numberOfTokenColumns > 1) + { + foreach (column; 0 .. numberOfTokenColumns) + { + if (diag.start <= columnReleaseLine[column]) + continue; + + diag.column = column - numberOfTokenColumns; + columnReleaseLine[column] = diag.end; + assigned = true; + break; + } + } + else + { + diag.column = -1; + assigned = true; + } + } + else if (numberOfColumns > 1) + { + foreach (column; numberOfTokenColumns .. numberOfColumns + 1) + { + if (diag.start <= columnReleaseLine[column]) + continue; + + diag.column = column - numberOfTokenColumns; + columnReleaseLine[column] = diag.end; + assigned = true; + break; + } + } + else + assigned = true; + + assert(assigned); + } + } + + if (numberOfColumns > 0) + numberOfColumns++; + } + + void processDiagnosticLineEvents(int lineNumber, int minimumActiveColumn, + ref int lastColumnEmitted, bool noStartEndAsInactive, + bool noStartEndAsContinue, bool withUser) nothrow + { + int emittedDiags = lastColumnEmitted; + const ifCalledMoreThanOnceForLine = lastColumnEmitted > numberOfTokenColumns; + scope (exit) + lastColumnEmitted = emittedDiags; + + Diagnostic* findActiveDiagInColumn(int lineNumber, int col, + out LineClassification classification, out bool startEndOnlyRange) nothrow + { + if (lineNumber == 0) + return null; + + foreach (ref diag; diagnostics) + { + if (diag.column != col) + continue; + + classification = calculateLineClassification(diag, lineNumber, startEndOnlyRange); + if (classification != LineClassification.Inactive) + return &diag; + } + + return null; + } + + void emptyColumn(int onLine, int columnNumber, bool haveStartOrEndColumnsToLeft) nothrow + { + if (columnNumber < 0) + return; + + LineClassification classification; + bool startEndOnlyRange; + Diagnostic* diag = findActiveDiagInColumn(onLine, columnNumber, + classification, startEndOnlyRange); + const isActive = classification == LineClassification.SpanStart + || classification == LineClassification.SpanContinue; + + columnEmptyHandler(lineNumber, haveStartOrEndColumnsToLeft, isActive); + } + + void printMargin() nothrow + { + int tempMaxDiagsEmitted = emittedDiags + 1; + processDiagnosticLineEvents(lineNumber, minimumActiveColumn, + tempMaxDiagsEmitted, false, true, false); + } + + void userMessage(ref Diagnostic diag, ref Message message, bool spansMultipleLines) nothrow + { + if (!withUser) + return; + + const offsetToSquiggles = this.graphemesBetweenPositions(lineNumber, + 0, message.startColumn, diag, message); + const lengthOfSquiggles = this.graphemesBetweenPositions(lineNumber, + message.startColumn, message.endColumn, diag, message); + + lineHighlight(lineNumber, offsetToSquiggles, lengthOfSquiggles, + diag, message, spansMultipleLines); + + if (message.isMultiline) + { + this.onLineEnd(lineNumber); + + printMultiLine(&printMargin, offsetToSquiggles, lineNumber, + offsetToSquiggles, lengthOfSquiggles, diag, message); + } + else + { + this.printSingleLine(lineNumber, diag, message); + this.onLineEnd(lineNumber); + } + } + + foreach (int column; 0 .. minimumActiveColumn) + columnEmptyHandler(lineNumber, false, false); + + for (int column = minimumActiveColumn; column < numberOfColumns; column++) + { + LineClassification classification; + bool startEndOnlyRange; + Diagnostic* currentDiag = findActiveDiagInColumn(lineNumber, + column, classification, startEndOnlyRange); + + if (column < lastColumnEmitted) + classification = LineClassification.Inactive; + else if (noStartEndAsInactive) + { + final switch (classification) + { + case LineClassification.SpanStart: + case LineClassification.SpanStartEnd: + case LineClassification.SpanEnd: + classification = LineClassification.Inactive; + break; + case LineClassification.SpanContinue: + case LineClassification.Inactive: + break; + } + } + else if (noStartEndAsContinue) + { + final switch (classification) + { + case LineClassification.SpanStart: + classification = LineClassification.SpanContinue; + break; + case LineClassification.SpanStartEnd: + classification = LineClassification.Inactive; + break; + case LineClassification.SpanEnd: + classification = (column < lastColumnEmitted) + ? LineClassification.Inactive : LineClassification.SpanContinue; + break; + case LineClassification.SpanContinue: + case LineClassification.Inactive: + break; + } + } + + final switch (classification) + { + case LineClassification.SpanStart: + case LineClassification.SpanEnd: + case LineClassification.SpanStartEnd: + + if (column >= 0) + columnDrawHandler(lineNumber, *currentDiag, classification); + emittedDiags++; + + const haveBefore = classification != LineClassification.SpanStartEnd; + + foreach (column2; column + 1 .. numberOfColumns) + { + LineClassification classification2; + bool startEndOnlyRange2; + Diagnostic* currentDiag2 = findActiveDiagInColumn(lineNumber, + column2, classification2, startEndOnlyRange2); + + if (startEndOnlyRange2) + columnDrawHandler(lineNumber, *currentDiag2, classification2); + else + emptyColumn(lineNumber - 1, column2, haveBefore); + } + + userMessage(*currentDiag, classification == LineClassification.SpanEnd + ? currentDiag.endMessage : currentDiag.startMessage, haveBefore); + return; + + case LineClassification.SpanContinue: + columnDrawHandler(lineNumber, *currentDiag, classification); + emittedDiags++; + break; + + case LineClassification.Inactive: + emptyColumn(lineNumber - (!ifCalledMoreThanOnceForLine + && !noStartEndAsContinue), column, false); + emittedDiags++; + break; + } + } + } +} + +private: + +LineClassification calculateLineClassification(ref Diagnostic diagnostic, + int lineNumber, out bool startEndOnlyRange) nothrow +{ + if (lineNumber < diagnostic.start || lineNumber > diagnostic.end) + return LineClassification.Inactive; + + const isStart = (lineNumber == diagnostic.start), isEnd = (lineNumber == diagnostic.end); + LineClassification ret; + + if (isStart && isEnd) + ret = LineClassification.SpanStartEnd; + else if (isStart) + ret = LineClassification.SpanStart; + else if (isEnd) + ret = LineClassification.SpanEnd; + else + ret = LineClassification.SpanContinue; + + final switch (ret) + { + case LineClassification.SpanStart: + if (diagnostic.startMessage.startColumn == 0 + && diagnostic.startMessage.endColumn == 0 && diagnostic.startMessage.id == 0) + startEndOnlyRange = true; + break; + case LineClassification.SpanEnd: + if (diagnostic.endMessage.startColumn == 0 + && diagnostic.endMessage.endColumn == 0 && diagnostic.endMessage.id == 0) + startEndOnlyRange = true; + break; + + case LineClassification.SpanStartEnd: + case LineClassification.SpanContinue: + case LineClassification.Inactive: + break; + } + + if (startEndOnlyRange) + ret = LineClassification.SpanContinue; + + return ret; +} diff --git a/compiler/src/dmd/diagreport/glue.d b/compiler/src/dmd/diagreport/glue.d new file mode 100644 index 000000000000..1fb438f278a6 --- /dev/null +++ b/compiler/src/dmd/diagreport/glue.d @@ -0,0 +1,302 @@ +module dmd.diagreport.glue; + +import core.stdc.stdio : vsnprintf, fwrite, fflush, stderr; +import core.stdc.stdarg; +import core.stdc.string : strlen; +import dmd.common.outbuffer; +import dmd.diagreport.defs; +import dmd.diagreport.geometry; +import dmd.diagreport.renderer; +import dmd.errors; +import dmd.globals; +import dmd.location; + +dmd.diagreport.defs.Diagnostic convert(dmd.errors.Diagnostic d) nothrow +{ + dmd.diagreport.defs.Diagnostic obj; + obj.start = d.loc.line; + obj.end = d.loc.line; + + const startCol = cast(int) getMessageStartColumn(d.loc.fileContent, d.loc.fileOffset); + obj.startMessage.startColumn = startCol; + obj.startMessage.endColumn = startCol + getTokenLength(d.loc.fileContent, d.loc.fileOffset); + obj.startMessage.isMultiline = false; + return obj; +} + +void callEvent(ref dmd.errors.Diagnostic[] group) nothrow +{ + if (group.length == 0) return; + + auto primary = group[0]; + + dmd.diagreport.defs.Diagnostic[] diags; + string[] messages; + + try + { + foreach (i, ref d; group) + { + auto diag = convert(d); + diag.startMessage.id = i + 1; + diags ~= diag; + messages ~= d.message; + } + } + catch (Exception) { return; } + + event(cast(string) primary.loc.filename, cast(string) primary.loc.fileContent, diags, messages, null); +} + +void event(string filename, string source, dmd.diagreport.defs.Diagnostic[] diagnostics, string[] messagesText, dmd.diagreport.defs.Help[] help) nothrow +{ + OutBuffer buf; + + string[] lines = splitLines(source); + + Renderer renderer; + renderer.filename = filename; + renderer.diagnostics = diagnostics; + renderer.help = help; + + int firstLineNumber = 1; // for 1 based indexing + + renderer.emitRaw = (string text) nothrow + => buf.printDiagnostic(text); + + renderer.emitRawFormat = (const(char)* fmt, ...) nothrow + { + va_list args; + va_start(args, fmt); + char[64] tmp = void; + int n = vsnprintf(tmp.ptr, tmp.length, fmt, args); + va_end(args); + if (n > 0) + buf.printDiagnostic(cast(string) tmp[0 .. n]); + }; + + if(global.params.v.color) + { + renderer.emitMargin = (string text) nothrow + => buf.printDiagnostic("\x1b[33m", text, "\x1b[0m"); + + renderer.emitHeader = () nothrow + => buf.printDiagnostic("\x1b[31merror\x1b[0m: "); + + renderer.emitHeaderMultiLinePrefix = () nothrow + => buf.printDiagnostic(" "); + + renderer.emitFooter = () nothrow + => buf.printDiagnostic("\x1b[34mnote:\x1b[0m "); + + renderer.emitFooterMultiLinePrefix = () nothrow + => buf.printDiagnostic(" "); + + renderer.emitHelp = () nothrow + => buf.printDiagnostic("\x1b[34mhelp:\x1b[0m "); + + renderer.emitHelpMultiLinePrefix = () nothrow + => buf.printDiagnostic(" "); + + renderer.emitGutter = (string text) nothrow + => buf.printDiagnostic("\x1b[34m", text, "\x1b[0m"); + + renderer.emitSquiggle = (string text) nothrow + => buf.printDiagnostic("\x1b[31m", text, "\x1b[0m"); + } + else + { + renderer.emitMargin = (string text) nothrow + => buf.printDiagnostic(text); + renderer.emitHeader = () nothrow + => buf.printDiagnostic("error: "); + renderer.emitHeaderMultiLinePrefix = () nothrow + => buf.printDiagnostic(" "); + renderer.emitFooter = () nothrow + => buf.printDiagnostic("note: "); + renderer.emitFooterMultiLinePrefix = () nothrow + => buf.printDiagnostic(" "); + renderer.emitHelp = () nothrow + => buf.printDiagnostic("help: "); + renderer.emitHelpMultiLinePrefix = () nothrow + => buf.printDiagnostic(" "); + renderer.emitGutter = (string text) nothrow + => buf.printDiagnostic(text); + renderer.emitSquiggle = (string text) nothrow + => buf.printDiagnostic(text); + } + + renderer.getSourceCode = (int lineNumber) nothrow @trusted + { + int idx = lineNumber - firstLineNumber; + if (idx < 0 || idx >= lines.length) + return ""; + return lines[idx]; + }; + + renderer.emitMessageSingleLine = (ref Message message) nothrow + { + if (message.id > 0 && message.id <= messagesText.length) + buf.printDiagnostic(messagesText[message.id - 1]); + }; + + renderer.emitMessageMultiLine = (scope void delegate(bool isLast) nothrow beforeTextOnLine, + ref Message message) nothrow @trusted + { + if (message.id == 0 || message.id > messagesText.length) + return; + + string text1 = messagesText[message.id - 1]; + size_t start = 0; + + while (start < text1.length) + { + size_t i = start; + while (i < text1.length && text1[i] != '\n' && text1[i] != '\r') + i++; + + if (i < text1.length) + { + if (text1[i] == '\r' && i + 1 < text1.length && text1[i + 1] == '\n') + i += 2; + else + i += 1; + } + + string text2 = text1[start .. i]; + start = i; + const isLast = (start == text1.length); + + beforeTextOnLine(isLast); + buf.printDiagnostic(text2); + + if (isLast) + buf.writeByte('\n'); + } + }; + + renderer.render(); + + const data = buf[]; + fwrite(data.ptr, 1, data.length, stderr); + fflush(stderr); +} + +// Split source into lines without Phobos +private string[] splitLines(string source) nothrow +{ + string[] result; + auto range = LineRange(source); + while (!range.empty) + { + try { result ~= range.front(); } + catch (Exception) { break; } + range.popFront(); + } + return result; +} + +void printDiagnostic(ref OutBuffer buf, string[] arr...) nothrow +{ + foreach (s; arr) + buf.write(s); +} + +size_t getMessageStartColumn(const(char)[] text, size_t offset) nothrow @safe +{ + import dmd.root.utf : utf_decodeChar; + + if (offset >= text.length) + return 0; + + size_t s = offset; + while (s > 0 && text[s - 1] != '\n') + s--; + + const line = text[s .. $]; + const byteColumn = offset - s; + enum tabWidth = 4; + + size_t currentColumn = 0; + size_t caretColumn = 0; + for (size_t i = 0; i < line.length; ) + { + dchar u; + const start = i; + const msg = utf_decodeChar(line, i, u); + assert(msg is null, msg); + if (u == '\t') + currentColumn += tabWidth - (currentColumn % tabWidth); + else if (u == '\r' || u == '\n') + break; + else + currentColumn++; + + if (start < byteColumn) + caretColumn = currentColumn; + } + return caretColumn; +} + +struct LineRange +{ + private const(char)[] content; + private size_t pos; + + this(const(char)[] source) nothrow { content = source; } + bool empty() const nothrow { return pos >= content.length; } + + string front() const nothrow + { + size_t end = pos; + while (end < content.length && content[end] != '\n' && content[end] != '\r') + end++; + return cast(string) content[pos .. end]; + } + + void popFront() nothrow + { + while (pos < content.length && content[pos] != '\n' && content[pos] != '\r') + pos++; + if (pos < content.length && content[pos] == '\r') + pos++; + if (pos < content.length && content[pos] == '\n') + pos++; + } +} + +private int getTokenLength(const(char)[] text, size_t offset) nothrow @safe +{ + import dmd.root.utf : utf_decodeChar; + + if (offset >= text.length) + return 1; + + // Find start of line + size_t s = offset; + while (s > 0 && text[s - 1] != '\n') + s--; + + // Scan forward from offset to end of token + // Simple heuristic: scan until whitespace, punctuation, or end of line + size_t i = offset; + int count = 0; + + while (i < text.length) + { + dchar c; + const prev = i; + if (utf_decodeChar(text, i, c) !is null) + break; + if (c == ' ' || c == '\t' || c == '\n' || c == '\r' || + c == ',' || c == ';' || c == ')' || c == '(' || + c == ']' || c == '[' || c == '{' || c == '}') + { + if (count == 0) count = 1; + break; + } + count++; + } + + return count == 0 ? 1 : count; +} diff --git a/compiler/src/dmd/diagreport/renderer.d b/compiler/src/dmd/diagreport/renderer.d new file mode 100644 index 000000000000..9df379298dd5 --- /dev/null +++ b/compiler/src/dmd/diagreport/renderer.d @@ -0,0 +1,482 @@ +module dmd.diagreport.renderer; +import dmd.diagreport.defs; +import dmd.diagreport.geometry; + +struct Renderer +{ + Config config; + + string filename; + + Message header; + Diagnostic[] diagnostics; + Message footer; + Help[] help; + + void delegate(string) nothrow emitRaw; + void delegate(const(char)* fmt, ...) nothrow emitRawFormat; + void delegate(string) nothrow emitMargin; + void delegate() nothrow emitHeader; + void delegate() nothrow emitHeaderMultiLinePrefix; + void delegate() nothrow emitFooter; + void delegate() nothrow emitFooterMultiLinePrefix; + void delegate() nothrow emitHelp; + void delegate() nothrow emitHelpMultiLinePrefix; + void delegate(string text) nothrow emitGutter; + void delegate(string text) nothrow emitSquiggle; + string delegate(int lineNumber) nothrow getSourceCode; + void delegate(ref Message message) nothrow emitMessageSingleLine; + void delegate(scope void delegate(bool isLast) nothrow beforeTextOnLine, + ref Message message) nothrow emitMessageMultiLine; + + private + { + int primaryLineNumber; + int minLineNumber; + string columnWithoutNumber; + string columnNumberFormat; + + int lastLineNumber; + } + + /// Assumption, all members of this are one grapheme in size. + struct Config + { + string margin = "│"; + string marginRight = "├"; + string marginToRight = "─"; + string marginUpLeft = "╮"; + string marginUpRight = "╭"; + string marginDownLeft = "╯"; + string marginDownRight = "╰"; + + string skippedLines = "╌"; + + string gutter = "│"; + string gutterUpRight = "┌"; + string gutterDownRight = "└"; + string gutterToLabel = "─"; + string gutterLeftRightUpDown = "┼"; + string gutterAsSquiggle = "┘"; + + string squiggle = "^"; + } + + void render() nothrow + { + if (diagnostics.length == 0) + return; + + calculate; + emitHeader2; + emitMainDiag; + emitFooter2; + emitHelp2; + } + +private: + + void calculate() nothrow + { + import core.stdc.stdio : snprintf; + import core.stdc.string : strlen; + + int maxLineNumber; + minLineNumber = int.max; + primaryLineNumber = diagnostics[0].start; + + foreach (i, ref diag; diagnostics) + { + diag.originalOffset = i; + + if (diag.start < minLineNumber) + minLineNumber = diag.start; + if (diag.end > maxLineNumber) + maxLineNumber = diag.end; + } + + // Selection sort — avoids Phobos + for (int i = 0; i < cast(int) diagnostics.length; i++) + { + for (int j = i + 1; j < cast(int) diagnostics.length; j++) + { + if (diagnostics[j].start < diagnostics[i].start || + (diagnostics[j].start == diagnostics[i].start && + diagnostics[j].end < diagnostics[i].end)) + { + Diagnostic tmp = diagnostics[i]; + diagnostics[i] = diagnostics[j]; + diagnostics[j] = tmp; + } + } + } + + // Build columnWithoutNumber — spaces matching width of maxLineNumber + { + int lineNumberLength = snprintf(null, 0, "%d", maxLineNumber); + + char[] temp; + try { temp.length = lineNumberLength; } + catch (Exception) { temp = [' ']; } + temp[] = ' '; + columnWithoutNumber = cast(string) temp; + } + + // Build columnNumberFormat — e.g. "%3d" for a 3-digit max line number + { + int lineNumberLength = snprintf(null, 0, "%d", maxLineNumber); + char[32] buf; + int n = snprintf(buf.ptr, buf.length, "%%%dd", lineNumberLength); + + char[] fmt; + try { fmt = new char[n + 1]; } + catch (Exception) { fmt = buf[0 .. n + 1]; } + fmt[0 .. n] = buf[0 .. n]; + fmt[n] = '\0'; + columnNumberFormat = cast(string) fmt[0 .. n]; + } + } + + void emitHeader2() nothrow + { + bool doneFirst; + + void beforeTextOnLine(bool isLast) nothrow + { + if (doneFirst) + emitHeaderMultiLinePrefix(); + else + emitHeader(); + + doneFirst = true; + } + + if (header.id > 0) + { + if (!header.isMultiline) + { + beforeTextOnLine(false); + emitMessageSingleLine(header); + } + else + emitMessageMultiLine(&beforeTextOnLine, header); + } + + { + emitRaw(columnWithoutNumber); + emitRaw(" "); + emitMargin(config.marginUpRight); + emitRaw(" "); + emitRaw(filename); + emitRawFormat("(%d)\n", primaryLineNumber); + } + } + + void emitMainDiag() nothrow + { + lastLineNumber = 0; + TimeLineGeometry(diagnostics, 3, 1, &columnDrawHandler, + &columnEmptyHandler, &onLineStart, &onLineEnd, &onLineSource, + &onLinesSkippedBeforeMargin, &onLinesSkippedAfterMargin, + &graphemesBetweenPositions, &lineHighlight, &printSingleLine, &printMultiLine) + .calculate; + } + + void emitFooter2() nothrow + { + bool doneFirst; + bool haveSomethingAfter; + + foreach (ref h; help) + { + if (h.startMessage.id != 0 || h.endMessage.id != 0) + { + haveSomethingAfter = true; + break; + } + } + + void beforeTextOnLine(bool isLast) nothrow + { + emitRaw(columnWithoutNumber); + + if (doneFirst) + { + if (isLast && haveSomethingAfter) + { + emitRaw(" "); + emitMargin(config.marginUpRight); + emitMargin(config.marginDownLeft); + } + else + { + emitRaw(" "); + emitMargin(config.margin); + } + + emitRaw(" "); + emitFooterMultiLinePrefix(); + } + else + { + if (isLast) + { + emitRaw(" "); + emitMargin(config.marginRight); + emitMargin(config.marginToRight); + } + else + { + emitRaw(" "); + emitMargin(config.marginDownRight); + emitMargin(config.marginUpLeft); + } + + emitRaw(" "); + emitFooter(); + } + + doneFirst = true; + } + + if (footer.id > 0) + { + if (!footer.isMultiline) + { + beforeTextOnLine(true); + emitMessageSingleLine(footer); + } + else + emitMessageMultiLine(&beforeTextOnLine, footer); + } + } + + void emitHelp2() nothrow + { + bool doneFirst; + + void beforeTextOnLine(bool isLast) nothrow + { + emitRaw(columnWithoutNumber); + + if (doneFirst) + { + if (isLast && help.length > 0) + { + emitRaw(" "); + emitMargin(config.marginUpRight); + emitMargin(config.marginDownLeft); + } + else + { + emitRaw(" "); + emitMargin(config.margin); + } + + emitRaw(" "); + emitHelpMultiLinePrefix(); + } + else + { + if (isLast) + { + emitRaw(" "); + emitMargin(config.marginRight); + emitMargin(config.marginToRight); + } + else + { + emitRaw(" "); + emitMargin(config.marginDownRight); + emitMargin(config.marginUpLeft); + } + + emitRaw(" "); + emitHelp(); + } + + doneFirst = true; + } + + void emitMessage(ref Message message) nothrow + { + if (message.id == 0) + return; + + doneFirst = false; + + if (message.isMultiline) + emitMessageMultiLine(&beforeTextOnLine, message); + else + { + beforeTextOnLine(true); + emitMessageSingleLine(message); + emitRaw("\n"); + } + } + + foreach (h; help) + { + if (h.startMessage.id == 0 && h.endMessage.id == 0) + continue; + + emitMessage(h.startMessage); + + lastLineNumber = 0; + TimeLineGeometry(h.diagnostics, 3, 1, &columnDrawHandler, + &columnEmptyHandler, &onLineStart, &onLineEnd, &onLineSource, + &onLinesSkippedBeforeMargin, &onLinesSkippedAfterMargin, + &graphemesBetweenPositions, &lineHighlight, + &printSingleLine, &printMultiLine).calculate; + + emitMessage(h.endMessage); + } + } + + void columnDrawHandler(int line, ref Diagnostic, LineClassification classification) nothrow + { + string glyph; + + final switch (classification) + { + case LineClassification.SpanStart: + glyph = config.gutterUpRight; + break; + case LineClassification.SpanContinue: + glyph = config.gutter; + break; + case LineClassification.SpanEnd: + glyph = config.gutterDownRight; + break; + case LineClassification.SpanStartEnd: + glyph = " "; + break; + case LineClassification.Inactive: + break; + } + + if (glyph.length > 0) + emitGutter(glyph); + } + + void columnEmptyHandler(int line, bool haveStartOrEndColumnsToLeft, + bool previousLineColumnIsActive) nothrow + { + string glyph; + + if (haveStartOrEndColumnsToLeft && previousLineColumnIsActive) + glyph = config.gutterLeftRightUpDown; + else if (previousLineColumnIsActive) + glyph = config.gutter; + else if (haveStartOrEndColumnsToLeft) + glyph = config.gutterToLabel; + else + glyph = " "; + + if (glyph.length > 0) + emitGutter(glyph); + } + + void onLineStart(int line) nothrow + { + if (lastLineNumber == line) + emitRaw(columnWithoutNumber); + else + emitRawFormat(columnNumberFormat.ptr, line); + + emitRaw(" "); + emitMargin(config.gutter); + emitRaw(" "); + + lastLineNumber = line; + } + + void onLineEnd(int line) nothrow + { + emitRaw("\n"); + } + + void onLineSource(int line) nothrow + { + emitRaw(getSourceCode(line)); + } + + void onLinesSkippedBeforeMargin(int startLine, int endLine) nothrow + { + emitRaw(columnWithoutNumber); + emitRaw(" "); + emitMargin(config.skippedLines); + emitRaw(" "); + } + + void onLinesSkippedAfterMargin(int startLine, int endLine) nothrow + { + emitRawFormat("%.*s(%d)", cast(int) filename.length, filename.ptr, endLine); + } + + uint graphemesBetweenPositions(int line, int startColumn, int endColumn, + ref Diagnostic diag, ref Message message) nothrow + { + import dmd.root.utf : utf_decodeChar; + + string text = getSourceCode(line); + + if (text.length < startColumn || text.length < endColumn) + return 0; + + text = text[startColumn .. endColumn]; + uint count; + size_t i = 0; + + while (i < text.length) + { + dchar c; + if (utf_decodeChar(text, i, c) !is null) + break; + count++; + } + + return count; + } + + void lineHighlight(int line, int offsetToSquiggles, int numberOfSquiggles, + ref Diagnostic diag, ref Message message, bool spansMultipleLines) nothrow + { + string offsetToSquigglesText = spansMultipleLines ? config.gutterToLabel : " "; + + foreach (_; 0 .. offsetToSquiggles) + emitGutter(offsetToSquigglesText); + + if (numberOfSquiggles > 1) + { + foreach (_; 0 .. numberOfSquiggles) + emitSquiggle(config.squiggle); + } + else if (spansMultipleLines) + emitGutter(config.gutterAsSquiggle); + else + emitSquiggle(config.squiggle); + } + + void printSingleLine(int line, ref Diagnostic diag, ref Message message) nothrow + { + emitRaw(" "); + emitMessageSingleLine(message); + } + + void printMultiLine(scope void delegate() nothrow printMargin, uint offsetToMessage, + int line, int offsetToSquiggles, int numberOfSquiggles, + ref Diagnostic diag, ref Message message) nothrow + { + void beforeTextOnLine(bool isLast) nothrow + { + onLineStart(line); + printMargin(); + + foreach (_; 0 .. offsetToMessage) + emitRaw(" "); + } + + emitMessageMultiLine(&beforeTextOnLine, message); + } +} diff --git a/compiler/src/dmd/errors.d b/compiler/src/dmd/errors.d index 88fc96814c73..b889157de628 100644 --- a/compiler/src/dmd/errors.d +++ b/compiler/src/dmd/errors.d @@ -52,6 +52,7 @@ struct Diagnostic } __gshared Diagnostic[] diagnostics = []; +__gshared Diagnostic[][] completedEvents = []; /*************************** * Error message sink for D compiler. @@ -99,6 +100,25 @@ class ErrorSinkCompiler : ErrorSink void plugSink() { + if (global.params.v.messageStyle == MessageStyle.diagreport) + { + import dmd.diagreport.glue : callEvent; + + // Flushes the last open causal group + if (diagnostics.length > 0) + { + try { completedEvents ~= diagnostics; } + catch (Exception) {} + diagnostics.length = 0; + } + + // Renders each causal group as one event + foreach (ref group; completedEvents) + callEvent(group); + + completedEvents.length = 0; + } + // Exit if there are no collected diagnostics if (!diagnostics.length) return; @@ -452,6 +472,61 @@ private struct DiagnosticContext bool supplemental; // true if supplemental error } +/** + * Collects diagnostics for the diagreport messagestyle. + * Params: + * loc = location of error + * format = printf-style format specification + * ap = printf-style variadic arguments + * kind = kind of error being printed + */ +private void collectDiagnostic(const SourceLoc loc, const(char)* format, va_list ap, ErrorKind kind) nothrow +{ + // A new primary diagnostic means the previous causal group is complete + if (diagnostics.length > 0) + { + try { completedEvents ~= diagnostics; } + catch (Exception) {} + diagnostics.length = 0; + } + + OutBuffer tmp; + tmp.vprintf(format, ap); + + Diagnostic d; + d.loc = loc; + d.kind = kind; + try { d.message = tmp.extractSlice().idup; } + catch (Exception) {} + + try { diagnostics ~= d; } + catch (Exception) {} +} + +/** + * Collects supplementals of diagnostics for the diagreport messagestyle. + * Params: + * loc = location of error + * format = printf-style format specification + * ap = printf-style variadic arguments + * kind = kind of error being printed + */ +private void collectSupplemental(const SourceLoc loc, const(char)* format, va_list ap, ErrorKind kind) nothrow +{ + // Append to the currently open causal group + OutBuffer tmp; + tmp.vprintf(format, ap); + + Diagnostic d; + d.loc = loc; + d.kind = kind; + try { d.message = tmp.extractSlice().idup; } + catch (Exception) {} + + try { diagnostics ~= d; } + catch (Exception) {} +} + /** * Implements $(D error), $(D warning), $(D deprecation), $(D message), and * $(D tip). Report a diagnostic error, taking a va_list parameter, and @@ -487,6 +562,11 @@ private extern(C++) void vreportDiagnostic(const SourceLoc loc, const(char)* for addSarifDiagnostic(loc, format, ap, kind); return; } + if (global.params.v.messageStyle == MessageStyle.diagreport) + { + collectDiagnostic(loc, format, ap, kind); + return; + } printDiagnostic(format, ap, info); if (global.params.v.errorLimit && global.errors >= global.params.v.errorLimit) { @@ -521,6 +601,11 @@ private extern(C++) void vreportDiagnostic(const SourceLoc loc, const(char)* for addSarifDiagnostic(loc, format, ap, kind); return; } + if (global.params.v.messageStyle == MessageStyle.diagreport) + { + collectDiagnostic(loc, format, ap, kind); + return; + } printDiagnostic(format, ap, info); } } @@ -542,6 +627,11 @@ private extern(C++) void vreportDiagnostic(const SourceLoc loc, const(char)* for addSarifDiagnostic(loc, format, ap, kind); return; } + if (global.params.v.messageStyle == MessageStyle.diagreport) + { + collectDiagnostic(loc, format, ap, kind); + return; + } printDiagnostic(format, ap, info); if (global.params.useWarnings == DiagnosticReporting.error) global.warnings++; @@ -558,6 +648,11 @@ private extern(C++) void vreportDiagnostic(const SourceLoc loc, const(char)* for addSarifDiagnostic(loc, format, ap, kind); return; } + if (global.params.v.messageStyle == MessageStyle.diagreport) + { + collectDiagnostic(loc, format, ap, kind); + return; + } printDiagnostic(format, ap, info); } return; @@ -578,6 +673,11 @@ private extern(C++) void vreportDiagnostic(const SourceLoc loc, const(char)* for addSarifDiagnostic(loc, format, ap, kind); return; } + if (global.params.v.messageStyle == MessageStyle.diagreport) + { + collectDiagnostic(loc, format, ap, kind); + return; + } return; } } @@ -614,6 +714,11 @@ private extern(C++) void vsupplementalDiagnostic(const SourceLoc loc, const(char } else info.headerColor = Classification.error; + if (global.params.v.messageStyle == MessageStyle.diagreport) + { + collectSupplemental(loc, format, ap, kind); + return; + } printDiagnostic(format, ap, info); return; @@ -625,6 +730,11 @@ private extern(C++) void vsupplementalDiagnostic(const SourceLoc loc, const(char if (global.params.v.errorLimit == 0 || global.deprecations <= global.params.v.errorLimit) { info.headerColor = Classification.deprecation; + if (global.params.v.messageStyle == MessageStyle.diagreport) + { + collectSupplemental(loc, format, ap, kind); + return; + } printDiagnostic(format, ap, info); } } @@ -1043,4 +1153,4 @@ private void writeHighlights(Console con, ref const OutBuffer buf) colors = true; } } -} +} \ No newline at end of file diff --git a/compiler/src/dmd/frontend.h b/compiler/src/dmd/frontend.h index 9f7017cc1b72..2e9aa597a310 100644 --- a/compiler/src/dmd/frontend.h +++ b/compiler/src/dmd/frontend.h @@ -370,6 +370,7 @@ enum class MessageStyle : uint8_t digitalmars = 0u, gnu = 1u, sarif = 2u, + diagreport = 3u, }; struct SourceLoc final diff --git a/compiler/src/dmd/location.d b/compiler/src/dmd/location.d index 177b72ff8b01..e121ce77e71f 100644 --- a/compiler/src/dmd/location.d +++ b/compiler/src/dmd/location.d @@ -23,7 +23,8 @@ enum MessageStyle : ubyte { digitalmars, /// filename.d(line): message gnu, /// filename.d:line: message, see https://www.gnu.org/prep/standards/html_node/Errors.html - sarif /// JSON SARIF output, see https://docs.oasis-open.org/sarif/sarif/v2.1.0/sarif-v2.1.0.html + sarif, /// JSON SARIF output, see https://docs.oasis-open.org/sarif/sarif/v2.1.0/sarif-v2.1.0.html + diagreport /// diagnostics reporting messagestyle } /** A source code location @@ -228,6 +229,8 @@ void writeSourceLoc(ref OutBuffer buf, case MessageStyle.sarif: // https://docs.oasis-open.org/sarif/sarif/v2.1.0/sarif-v2.1.0.html // No formatting needed here for SARIF break; + case MessageStyle.diagreport: + break; } } diff --git a/compiler/src/dmd/mars.d b/compiler/src/dmd/mars.d index 972fdb77545d..41f2ee85fe6a 100644 --- a/compiler/src/dmd/mars.d +++ b/compiler/src/dmd/mars.d @@ -1136,8 +1136,11 @@ bool parseCommandLine(const ref Strings arguments, const size_t argc, out Param case "sarif": params.v.messageStyle = MessageStyle.sarif; break; + case "diagreport": + params.v.messageStyle = MessageStyle.diagreport; + break; default: - error("unknown error style '%.*s', must be 'digitalmars', 'gnu', or 'sarif'", cast(int) style.length, style.ptr); + error("unknown error style '%.*s', must be 'digitalmars', 'gnu','sarif' or 'diagreport'", cast(int) style.length, style.ptr); } } else if (startsWith(p + 1, "target")) From 4f5148e7946b1ff822b0617c1f32756a28f1131a Mon Sep 17 00:00:00 2001 From: Iskaban10 Date: Wed, 13 May 2026 00:01:36 +0530 Subject: [PATCH 2/2] pre-commit changes made --- compiler/src/build.d | 2 +- compiler/src/dmd/diagreport/glue.d | 12 ++++++------ compiler/src/dmd/diagreport/renderer.d | 8 ++++---- compiler/src/dmd/errors.d | 4 ++-- compiler/src/dmd/location.d | 2 +- 5 files changed, 14 insertions(+), 14 deletions(-) diff --git a/compiler/src/build.d b/compiler/src/build.d index f6f715a38dcf..23e8dd219d32 100755 --- a/compiler/src/build.d +++ b/compiler/src/build.d @@ -1592,7 +1592,7 @@ auto sourceFiles() visitor/strict.d visitor/transitive.d cparse.d dfa/entry.d dfa/utils.d dfa/fast/structure.d dfa/fast/analysis.d dfa/fast/report.d dfa/fast/expression.d dfa/fast/statement.d - diagreport/defs.d diagreport/geometry.d diagreport/renderer.d + diagreport/defs.d diagreport/geometry.d diagreport/renderer.d "), backendHeaders: fileArray(env["C"], " cc.d cdef.d cgcv.d code.d dt.d el.d global.d diff --git a/compiler/src/dmd/diagreport/glue.d b/compiler/src/dmd/diagreport/glue.d index 1fb438f278a6..f6fa5c310607 100644 --- a/compiler/src/dmd/diagreport/glue.d +++ b/compiler/src/dmd/diagreport/glue.d @@ -38,7 +38,7 @@ void callEvent(ref dmd.errors.Diagnostic[] group) nothrow foreach (i, ref d; group) { auto diag = convert(d); - diag.startMessage.id = i + 1; + diag.startMessage.id = i + 1; diags ~= diag; messages ~= d.message; } @@ -104,7 +104,7 @@ void event(string filename, string source, dmd.diagreport.defs.Diagnostic[] diag renderer.emitSquiggle = (string text) nothrow => buf.printDiagnostic("\x1b[31m", text, "\x1b[0m"); } - else + else { renderer.emitMargin = (string text) nothrow => buf.printDiagnostic(text); @@ -124,8 +124,8 @@ void event(string filename, string source, dmd.diagreport.defs.Diagnostic[] diag => buf.printDiagnostic(text); renderer.emitSquiggle = (string text) nothrow => buf.printDiagnostic(text); - } - + } + renderer.getSourceCode = (int lineNumber) nothrow @trusted { int idx = lineNumber - firstLineNumber; @@ -182,7 +182,7 @@ void event(string filename, string source, dmd.diagreport.defs.Diagnostic[] diag fflush(stderr); } -// Split source into lines without Phobos +// Split source into lines without Phobos private string[] splitLines(string source) nothrow { string[] result; @@ -292,7 +292,7 @@ private int getTokenLength(const(char)[] text, size_t offset) nothrow @safe c == ',' || c == ';' || c == ')' || c == '(' || c == ']' || c == '[' || c == '{' || c == '}') { - if (count == 0) count = 1; + if (count == 0) count = 1; break; } count++; diff --git a/compiler/src/dmd/diagreport/renderer.d b/compiler/src/dmd/diagreport/renderer.d index 9df379298dd5..152c052124ca 100644 --- a/compiler/src/dmd/diagreport/renderer.d +++ b/compiler/src/dmd/diagreport/renderer.d @@ -125,15 +125,15 @@ private: // Build columnNumberFormat — e.g. "%3d" for a 3-digit max line number { int lineNumberLength = snprintf(null, 0, "%d", maxLineNumber); - char[32] buf; + char[32] buf; int n = snprintf(buf.ptr, buf.length, "%%%dd", lineNumberLength); char[] fmt; - try { fmt = new char[n + 1]; } + try { fmt = new char[n + 1]; } catch (Exception) { fmt = buf[0 .. n + 1]; } fmt[0 .. n] = buf[0 .. n]; - fmt[n] = '\0'; - columnNumberFormat = cast(string) fmt[0 .. n]; + fmt[n] = '\0'; + columnNumberFormat = cast(string) fmt[0 .. n]; } } diff --git a/compiler/src/dmd/errors.d b/compiler/src/dmd/errors.d index b889157de628..6c4275cf6dbf 100644 --- a/compiler/src/dmd/errors.d +++ b/compiler/src/dmd/errors.d @@ -118,7 +118,7 @@ class ErrorSinkCompiler : ErrorSink completedEvents.length = 0; } - + // Exit if there are no collected diagnostics if (!diagnostics.length) return; @@ -1153,4 +1153,4 @@ private void writeHighlights(Console con, ref const OutBuffer buf) colors = true; } } -} \ No newline at end of file +} diff --git a/compiler/src/dmd/location.d b/compiler/src/dmd/location.d index e121ce77e71f..d3e76045f4b3 100644 --- a/compiler/src/dmd/location.d +++ b/compiler/src/dmd/location.d @@ -229,7 +229,7 @@ void writeSourceLoc(ref OutBuffer buf, case MessageStyle.sarif: // https://docs.oasis-open.org/sarif/sarif/v2.1.0/sarif-v2.1.0.html // No formatting needed here for SARIF break; - case MessageStyle.diagreport: + case MessageStyle.diagreport: break; } }