1 /**
2  * Compiler implementation of the
3  * $(LINK2 http://www.dlang.org, D programming language).
4  *
5  * Copyright:   Copyright (C) 1999-2020 by The D Language Foundation, All Rights Reserved
6  * Authors:     $(LINK2 http://www.digitalmars.com, Walter Bright)
7  * License:     $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0)
8  * Source:      $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/errors.d, _errors.d)
9  * Documentation:  https://dlang.org/phobos/dmd_errors.html
10  * Coverage:    https://codecov.io/gh/dlang/dmd/src/master/src/dmd/errors.d
11  */
12 
13 module dmd.diagnostic;
14 
15 import core.stdc.stdarg : va_list;
16 
17 import dmd.globals : Loc;
18 
19 alias DiagnosticHandler = void delegate(const ref Loc loc,
20     Severity severity, scope const char* messageFormat,
21     va_list args, bool isSupplemental = false) pure nothrow;
22 
23 /// The severity level of a diagnostic.
24 enum Severity
25 {
26     /// An error occurred.
27     error,
28 
29      /// A warning occurred.
30     warning,
31 
32      /// A deprecation occurred.
33     deprecation,
34 }
35 
36 struct Diagnostic
37 {
38     /// The location of where the diagnostic occurred.
39     const Loc location;
40 
41     /// The message.
42     const string message;
43 
44     /// The severity of the diagnostic.
45     const Severity severity;
46 
47     /// The supplemental diagnostics belonging to this diagnostic.
48     private const(Diagnostic)[] _supplementalDiagnostics;
49 
50     /// Returns: a textual representation of the diagnostic set
51     string toString() const pure
52     {
53         import std.format : format;
54         import std..string : fromStringz;
55 
56         return format!"%s:%s:%s: %s: %s%s%(%s\n%)"(
57             location.filename.fromStringz,
58             location.linnum,
59             location.charnum,
60             severity,
61             message,
62             supplementalDiagnostics.length > 0 ? "\n" : "",
63             supplementalDiagnostics
64         );
65     }
66 
67 pure nothrow @safe:
68 
69     /// Returns: the supplemental diagnostics attached to this diagnostic.
70     const(Diagnostic[]) supplementalDiagnostics() const @nogc
71     {
72         return _supplementalDiagnostics;
73     }
74 
75     /**
76      * Adds a supplemental diagnostic to this diagnostic.
77      *
78      * Params:
79      *  diagnostic = the supplemental diagnostic to add
80      */
81     private void addSupplementalDiagnostic(Diagnostic diagnostic)
82     in(diagnostic.severity == severity)
83     {
84         _supplementalDiagnostics ~= diagnostic;
85     }
86 }
87 
88 /// Stores a set of diagnostics.
89 struct DiagnosticSet
90 {
91     private Diagnostic[] _diagnostics;
92 
93 pure:
94 
95     /// Returns: a textual representation of the diagnostic set
96     string toString() const
97     {
98         import std.format : format;
99 
100         return format!"%(%s\n%)"(_diagnostics);
101     }
102 
103 @safe nothrow:
104 
105     /**
106      * Adds the given diagnostic to the set of diagnostics.
107      *
108      * Params:
109      *  diagnostic = the diagnostic to add
110      */
111     DiagnosticSet opOpAssign(string op)(Diagnostic diagnostic)
112     if (op == "~")
113     {
114         _diagnostics ~= diagnostic;
115         return this;
116     }
117 
118     /// ditto
119     void add(Diagnostic diagnostic)
120     {
121         _diagnostics ~= diagnostic;
122     }
123 
124     /**
125      * Adds the given supplemental diagnostic to the last added diagnostic.
126      *
127      * Params:
128      *  diagnostic = the supplemental diagnostic to add
129      */
130     void addSupplemental(Diagnostic diagnostic)
131     {
132         _diagnostics[$ - 1].addSupplementalDiagnostic(diagnostic);
133     }
134 
135 @nogc:
136 
137     /// Returns: the diagnostic at the front of the range.
138     const(Diagnostic) front() const
139     {
140         return _diagnostics[0];
141     }
142 
143     /// Advances the range forward.
144     void popFront()
145     {
146         _diagnostics = _diagnostics[1 .. $];
147     }
148 
149     /// Returns: `true` if no diagnostics are stored.
150     bool empty() const
151     {
152         return _diagnostics.length == 0;
153     }
154 
155     /// Returns: the number of diagnostics stored.
156     size_t length() const
157     {
158         return _diagnostics.length;
159     }
160 
161     /**
162      * Returns the diagnostic stored at the given index.
163      *
164      * Params:
165      *  index = the index of the diagnostic to return
166      *
167      * Returns: the diagnostic
168      */
169     const(Diagnostic) opIndex(size_t index) const
170     {
171         return _diagnostics[index];
172     }
173 }
174 
175 DiagnosticHandler suppressingDiagnosticHandler = (
176     const ref Loc loc,
177     Severity severity,
178     scope const char* messageFormat,
179     va_list args,
180     bool isSupplemental = false
181 ) pure nothrow {};
182 
183 struct CollectingDiagnosticHandler
184 {
185     private DiagnosticSet diagnostics_;
186 
187     DiagnosticSet diagnostics() pure nothrow @safe @nogc
188     {
189         return diagnostics_;
190     }
191 
192     void handleDiagnostic(
193         const ref Loc loc, Severity severity,
194         scope const char* messageFormat,
195         va_list args,
196         bool isSupplemental = false
197     ) pure nothrow
198     {
199         import dmd.root.outbuffer : OutBuffer;
200 
201         auto buffer = OutBuffer();
202         buffer.vprintf(messageFormat, args);
203         auto diagnostic = Diagnostic(loc, buffer.extractSlice, severity);
204 
205         if (isSupplemental)
206             diagnostics_.addSupplemental(diagnostic);
207         else
208             diagnostics_ ~= diagnostic;
209     }
210 }
211 
212 struct DefaultDiagnosticReporter
213 {
214     void report(DiagnosticSet diagnostics) nothrow
215     {
216         alias ReportingFunction =
217             extern (C++) void function(
218                 ref const(Loc) loc, const(char)* format, ...
219             ) nothrow;
220 
221         static ReportingFunction getReportingFunction(Severity severity)
222         {
223             import dmd.errors : deprecation, error, warning;
224 
225             final switch (severity)
226             {
227                 case Severity.error: return &error;
228                 case Severity.warning: return &warning;
229                 case Severity.deprecation: return &deprecation;
230             }
231         }
232 
233         static ReportingFunction getSupplementalReportingFunction(Severity severity)
234         {
235             import dmd.errors : deprecationSupplemental, errorSupplemental,
236                 warningSupplemental;
237 
238             final switch (severity)
239             {
240                 case Severity.error: return &errorSupplemental;
241                 case Severity.warning: return &warningSupplemental;
242                 case Severity.deprecation: return &deprecationSupplemental;
243             }
244         }
245 
246         static void report(const ref Diagnostic diagnostic, ReportingFunction func)
247         {
248             with (diagnostic)
249                 func(location, "%.*s", cast(int) message.length, message.ptr);
250         }
251 
252         foreach (const ref diagnostic; diagnostics)
253         {
254             const func = getReportingFunction(diagnostic.severity);
255             report(diagnostic, func);
256 
257             foreach (const ref supplemental; diagnostic.supplementalDiagnostics)
258             {
259                 const supplementalFunc =
260                     getSupplementalReportingFunction(diagnostic.severity);
261                 report(supplemental, supplementalFunc);
262             }
263         }
264     }
265 }
266 
267 struct DefaultDiagnosticHandler
268 {
269     private CollectingDiagnosticHandler handler;
270     private DefaultDiagnosticReporter reporter;
271 
272     DiagnosticHandler diagnosticHandler() pure nothrow @nogc @safe return
273     {
274         return &handler.handleDiagnostic;
275     }
276 
277     void report() nothrow
278     {
279         reporter.report(handler.diagnostics);
280         handler.diagnostics_ = DiagnosticSet();
281     }
282 }