@@ -14,6 +14,293 @@ use crate::files::File;
14
14
// the APIs in this module.
15
15
mod old;
16
16
17
+ /// A collection of information that can be rendered into a diagnostic.
18
+ ///
19
+ /// A diagnostic is a collection of information gathered by a tool intended
20
+ /// for presentation to an end user, and which describes a group of related
21
+ /// characteristics in the inputs given to the tool. Typically, but not always,
22
+ /// a characteristic is a deficiency. An example of a characteristic that is
23
+ /// _not_ a deficiency is the `reveal_type` diagnostic for our type checker.
24
+ #[ derive( Debug , Clone , Eq , PartialEq ) ]
25
+ pub struct Diagnostic {
26
+ /// The actual diagnostic.
27
+ ///
28
+ /// We box the diagnostic since it is somewhat big.
29
+ inner : Box < DiagnosticInner > ,
30
+ }
31
+
32
+ impl Diagnostic {
33
+ /// Create a new diagnostic with the given identifier, severity and
34
+ /// message.
35
+ ///
36
+ /// The identifier should be something that uniquely identifies the _type_
37
+ /// of diagnostic being reported. It should be usable as a reference point
38
+ /// for humans communicating about diagnostic categories. It will also
39
+ /// appear in the output when this diagnostic is rendered.
40
+ ///
41
+ /// The severity should describe the assumed level of importance to an end
42
+ /// user.
43
+ ///
44
+ /// The message is meant to be read by end users. The primary message
45
+ /// is meant to be a single terse description (usually a short phrase)
46
+ /// describing the group of related characteristics that the diagnostic
47
+ /// describes. Stated differently, if only one thing from a diagnostic can
48
+ /// be shown to an end user in a particular context, it is the primary
49
+ /// message.
50
+ pub fn new < ' a > (
51
+ id : DiagnosticId ,
52
+ severity : Severity ,
53
+ message : impl std:: fmt:: Display + ' a ,
54
+ ) -> Diagnostic {
55
+ let message = message. to_string ( ) . into_boxed_str ( ) ;
56
+ let inner = Box :: new ( DiagnosticInner {
57
+ id,
58
+ severity,
59
+ message,
60
+ annotations : vec ! [ ] ,
61
+ subs : vec ! [ ] ,
62
+ #[ cfg( debug_assertions) ]
63
+ printed : false ,
64
+ } ) ;
65
+ Diagnostic { inner }
66
+ }
67
+
68
+ /// Add an annotation to this diagnostic.
69
+ ///
70
+ /// Annotations for a diagnostic are optional, but if any are added,
71
+ /// callers should strive to make at least one of them primary. That is, it
72
+ /// should be constructed via [`Annotation::primary`]. A diagnostic with no
73
+ /// primary annotations is allowed, but its rendering may be sub-optimal.
74
+ pub fn annotate ( & mut self , ann : Annotation ) {
75
+ self . inner . annotations . push ( ann) ;
76
+ }
77
+
78
+ /// Adds an "info" sub-diagnostic with the given message.
79
+ ///
80
+ /// If callers want to add an "info" sub-diagnostic with annotations, then
81
+ /// create a [`SubDiagnostic`] manually and use [`Diagnostic::sub`] to
82
+ /// attach it to a parent diagnostic.
83
+ ///
84
+ /// An "info" diagnostic is useful when contextualizing or otherwise
85
+ /// helpful information can be added to help end users understand the
86
+ /// main diagnostic message better. For example, if a the main diagnostic
87
+ /// message is about a function call being invalid, a useful "info"
88
+ /// sub-diagnostic could show the function definition (or only the relevant
89
+ /// parts of it).
90
+ pub fn info < ' a > ( & mut self , message : impl std:: fmt:: Display + ' a ) {
91
+ self . sub ( SubDiagnostic :: new ( Severity :: Info , message) ) ;
92
+ }
93
+
94
+ /// Adds a "sub" diagnostic to this diagnostic.
95
+ ///
96
+ /// This is useful when a sub diagnostic has its own annotations attached
97
+ /// to it. For the simpler case of a sub-diagnostic with only a message,
98
+ /// using a method like [`Diagnostic::info`] may be more convenient.
99
+ pub fn sub ( & mut self , sub : SubDiagnostic ) {
100
+ self . inner . subs . push ( sub) ;
101
+ }
102
+ }
103
+
104
+ #[ derive( Debug , Clone , Eq , PartialEq ) ]
105
+ struct DiagnosticInner {
106
+ id : DiagnosticId ,
107
+ severity : Severity ,
108
+ message : Box < str > ,
109
+ annotations : Vec < Annotation > ,
110
+ subs : Vec < SubDiagnostic > ,
111
+ /// This will make the `Drop` impl panic if a `Diagnostic` hasn't
112
+ /// been printed to stderr. This is usually a bug, so we want it to
113
+ /// be loud. But only when `debug_assertions` is enabled.
114
+ #[ cfg( debug_assertions) ]
115
+ printed : bool ,
116
+ }
117
+
118
+ impl Drop for DiagnosticInner {
119
+ fn drop ( & mut self ) {
120
+ #[ cfg( debug_assertions) ]
121
+ {
122
+ if self . printed || std:: thread:: panicking ( ) {
123
+ return ;
124
+ }
125
+ panic ! (
126
+ "diagnostic `{id}` with severity `{severity:?}` and message `{message}` \
127
+ did not get printed to stderr before being dropped",
128
+ id = self . id,
129
+ severity = self . severity,
130
+ message = self . message,
131
+ ) ;
132
+ }
133
+ }
134
+ }
135
+
136
+ /// A collection of information subservient to a diagnostic.
137
+ ///
138
+ /// A sub-diagnostic is always rendered after the parent diagnostic it is
139
+ /// attached to. A parent diagnostic may have many sub-diagnostics, and it is
140
+ /// guaranteed that they will not interleave with one another in rendering.
141
+ ///
142
+ /// Currently, the order in which sub-diagnostics are rendered relative to one
143
+ /// another (for a single parent diagnostic) is the order in which they were
144
+ /// attached to the diagnostic.
145
+ #[ derive( Debug , Clone , Eq , PartialEq ) ]
146
+ pub struct SubDiagnostic {
147
+ /// Like with `Diagnostic`, we box the `SubDiagnostic` to make it
148
+ /// pointer-sized.
149
+ inner : Box < SubDiagnosticInner > ,
150
+ }
151
+
152
+ impl SubDiagnostic {
153
+ /// Create a new sub-diagnostic with the given severity and message.
154
+ ///
155
+ /// The severity should describe the assumed level of importance to an end
156
+ /// user.
157
+ ///
158
+ /// The message is meant to be read by end users. The primary message
159
+ /// is meant to be a single terse description (usually a short phrase)
160
+ /// describing the group of related characteristics that the sub-diagnostic
161
+ /// describes. Stated differently, if only one thing from a diagnostic can
162
+ /// be shown to an end user in a particular context, it is the primary
163
+ /// message.
164
+ pub fn new < ' a > ( severity : Severity , message : impl std:: fmt:: Display + ' a ) -> SubDiagnostic {
165
+ let message = message. to_string ( ) . into_boxed_str ( ) ;
166
+ let inner = Box :: new ( SubDiagnosticInner {
167
+ severity,
168
+ message,
169
+ annotations : vec ! [ ] ,
170
+ #[ cfg( debug_assertions) ]
171
+ printed : false ,
172
+ } ) ;
173
+ SubDiagnostic { inner }
174
+ }
175
+
176
+ /// Add an annotation to this sub-diagnostic.
177
+ ///
178
+ /// Annotations for a sub-diagnostic, like for a diagnostic, are optional.
179
+ /// If any are added, callers should strive to make at least one of them
180
+ /// primary. That is, it should be constructed via [`Annotation::primary`].
181
+ /// A diagnostic with no primary annotations is allowed, but its rendering
182
+ /// may be sub-optimal.
183
+ ///
184
+ /// Note that it is expected to be somewhat more common for sub-diagnostics
185
+ /// to have no annotations (e.g., a simple note) than for a diagnostic to
186
+ /// have no annotations.
187
+ pub fn annotate ( & mut self , ann : Annotation ) {
188
+ self . inner . annotations . push ( ann) ;
189
+ }
190
+ }
191
+
192
+ #[ derive( Debug , Clone , Eq , PartialEq ) ]
193
+ struct SubDiagnosticInner {
194
+ severity : Severity ,
195
+ message : Box < str > ,
196
+ annotations : Vec < Annotation > ,
197
+ /// This will make the `Drop` impl panic if a `SubDiagnostic` hasn't
198
+ /// been printed to stderr. This is usually a bug, so we want it to
199
+ /// be loud. But only when `debug_assertions` is enabled.
200
+ #[ cfg( debug_assertions) ]
201
+ printed : bool ,
202
+ }
203
+
204
+ impl Drop for SubDiagnosticInner {
205
+ fn drop ( & mut self ) {
206
+ #[ cfg( debug_assertions) ]
207
+ {
208
+ if self . printed || std:: thread:: panicking ( ) {
209
+ return ;
210
+ }
211
+ panic ! (
212
+ "sub-diagnostic with severity `{severity:?}` and message `{message}` \
213
+ did not get printed to stderr before being dropped",
214
+ severity = self . severity,
215
+ message = self . message,
216
+ ) ;
217
+ }
218
+ }
219
+ }
220
+
221
+ /// A pointer to a subsequence in the end user's input.
222
+ ///
223
+ /// Also known as an annotation, the pointer can optionally contain a short
224
+ /// message, typically describing in general terms what is being pointed to.
225
+ ///
226
+ /// An annotation is either primary or secondary, depending on whether it was
227
+ /// constructed via [`Annotation::primary`] or [`Annotation::secondary`].
228
+ /// Semantically, a primary annotation is meant to point to the "locus" of a
229
+ /// diagnostic. Visually, the difference between a primary and a secondary
230
+ /// annotation is usually just a different form of highlighting on the
231
+ /// corresponding span.
232
+ ///
233
+ /// # Advice
234
+ ///
235
+ /// The span on an annotation should be as _specific_ as possible. For example,
236
+ /// if there is a problem with a function call because one of its arguments has
237
+ /// an invalid type, then the span should point to the specific argument and
238
+ /// not to the entire function call.
239
+ ///
240
+ /// Messages attached to annotations should also be as brief and specific as
241
+ /// possible. Long messages could negative impact the quality of rendering.
242
+ #[ derive( Debug , Clone , Eq , PartialEq ) ]
243
+ pub struct Annotation {
244
+ /// The span of this annotation, corresponding to some subsequence of the
245
+ /// user's input that we want to highlight.
246
+ span : Span ,
247
+ /// An optional message associated with this annotation's span.
248
+ ///
249
+ /// When present, rendering will include this message in the output and
250
+ /// draw a line between the highlighted span and the message.
251
+ message : Option < Box < str > > ,
252
+ /// Whether this annotation is "primary" or not. When it isn't primary, an
253
+ /// annotation is said to be "secondary."
254
+ is_primary : bool ,
255
+ }
256
+
257
+ impl Annotation {
258
+ /// Create a "primary" annotation.
259
+ ///
260
+ /// A primary annotation is meant to highlight the "locus" of a diagnostic.
261
+ /// That is, it should point to something in the end user's input that is
262
+ /// the subject or "point" of a diagnostic.
263
+ ///
264
+ /// A diagnostic may have many primary annotations. A diagnostic may not
265
+ /// have any annotations, but if it does, at least one _ought_ to be
266
+ /// primary.
267
+ pub fn primary ( span : Span ) -> Annotation {
268
+ Annotation {
269
+ span,
270
+ message : None ,
271
+ is_primary : true ,
272
+ }
273
+ }
274
+
275
+ /// Create a "secondary" annotation.
276
+ ///
277
+ /// A secondary annotation is meant to highlight relevant context for a
278
+ /// diagnostic, but not to point to the "locus" of the diagnostic.
279
+ ///
280
+ /// A diagnostic with only secondary annotations is usually not sensible,
281
+ /// but it is allowed and will produce a reasonable rendering.
282
+ pub fn secondary ( span : Span ) -> Annotation {
283
+ Annotation {
284
+ span,
285
+ message : None ,
286
+ is_primary : false ,
287
+ }
288
+ }
289
+
290
+ /// Attach a message to this annotation.
291
+ ///
292
+ /// An annotation without a message will still have a presence in
293
+ /// rendering. In particular, it will highlight the span association with
294
+ /// this annotation in some way.
295
+ ///
296
+ /// When a message is attached to an annotation, then it will be associated
297
+ /// with the highlighted span in some way during rendering.
298
+ pub fn message < ' a > ( self , message : impl std:: fmt:: Display + ' a ) -> Annotation {
299
+ let message = Some ( message. to_string ( ) . into_boxed_str ( ) ) ;
300
+ Annotation { message, ..self }
301
+ }
302
+ }
303
+
17
304
/// A string identifier for a lint rule.
18
305
///
19
306
/// This string is used in command line and configuration interfaces. The name should always
0 commit comments