]>
Commit | Line | Data |
---|---|---|
923072b8 FG |
1 | # Diagnostic and subdiagnostic structs |
2 | rustc has two diagnostic derives that can be used to create simple diagnostics, | |
3 | which are recommended to be used when they are applicable: | |
4 | `#[derive(SessionDiagnostic)]` and `#[derive(SessionSubdiagnostic)]`. | |
5 | ||
6 | Diagnostics created with the derive macros can be translated into different | |
7 | languages and each has a slug that uniquely identifies the diagnostic. | |
8 | ||
9 | ## `#[derive(SessionDiagnostic)]` | |
10 | Instead of using the `DiagnosticBuilder` API to create and emit diagnostics, | |
11 | the `SessionDiagnostic` derive can be used. `#[derive(SessionDiagnostic)]` is | |
12 | only applicable for simple diagnostics that don't require much logic in | |
13 | deciding whether or not to add additional subdiagnostics. | |
14 | ||
15 | Consider the [definition][defn] of the "field already declared" diagnostic | |
16 | shown below: | |
17 | ||
18 | ```rust,ignore | |
19 | #[derive(SessionDiagnostic)] | |
f2b60f7d | 20 | #[diag(typeck::field_already_declared, code = "E0124")] |
923072b8 FG |
21 | pub struct FieldAlreadyDeclared { |
22 | pub field_name: Ident, | |
23 | #[primary_span] | |
24 | #[label] | |
25 | pub span: Span, | |
f2b60f7d | 26 | #[label(typeck::previous_decl_label)] |
923072b8 FG |
27 | pub prev_span: Span, |
28 | } | |
29 | ``` | |
30 | ||
31 | `SessionDiagnostic` can only be applied to structs. Every `SessionDiagnostic` | |
f2b60f7d | 32 | has to have one attribute, `#[diag(...)]`, applied to the struct itself. |
923072b8 FG |
33 | |
34 | If an error has an error code (e.g. "E0624"), then that can be specified using | |
35 | the `code` sub-attribute. Specifying a `code` isn't mandatory, but if you are | |
36 | porting a diagnostic that uses `DiagnosticBuilder` to use `SessionDiagnostic` | |
37 | then you should keep the code if there was one. | |
38 | ||
f2b60f7d FG |
39 | `#[diag(..)]` must provide a slug as the first positional argument (a path to an |
40 | item in `rustc_errors::fluent::*`). A slug uniquely identifies the diagnostic | |
41 | and is also how the compiler knows what error message to emit (in the default | |
42 | locale of the compiler, or in the locale requested by the user). See | |
43 | [translation documentation](./translation.md) to learn more about how | |
44 | translatable error messages are written and how slug items are generated. | |
923072b8 FG |
45 | |
46 | In our example, the Fluent message for the "field already declared" diagnostic | |
47 | looks like this: | |
48 | ||
49 | ```fluent | |
f2b60f7d | 50 | typeck_field_already_declared = |
923072b8 FG |
51 | field `{$field_name}` is already declared |
52 | .label = field already declared | |
f2b60f7d | 53 | .previous_decl_label = `{$field_name}` first declared here |
923072b8 FG |
54 | ``` |
55 | ||
f2b60f7d | 56 | `typeck_field_already_declared` is the slug from our example and is followed |
923072b8 FG |
57 | by the diagnostic message. |
58 | ||
59 | Every field of the `SessionDiagnostic` which does not have an annotation is | |
60 | available in Fluent messages as a variable, like `field_name` in the example | |
61 | above. Fields can be annotated `#[skip_arg]` if this is undesired. | |
62 | ||
63 | Using the `#[primary_span]` attribute on a field (that has type `Span`) | |
64 | indicates the primary span of the diagnostic which will have the main message | |
65 | of the diagnostic. | |
66 | ||
67 | Diagnostics are more than just their primary message, they often include | |
68 | labels, notes, help messages and suggestions, all of which can also be | |
69 | specified on a `SessionDiagnostic`. | |
70 | ||
71 | `#[label]`, `#[help]` and `#[note]` can all be applied to fields which have the | |
72 | type `Span`. Applying any of these attributes will create the corresponding | |
73 | subdiagnostic with that `Span`. These attributes will look for their | |
74 | diagnostic message in a Fluent attribute attached to the primary Fluent | |
75 | message. In our example, `#[label]` will look for | |
f2b60f7d | 76 | `typeck_field_already_declared.label` (which has the message "field already |
923072b8 FG |
77 | declared"). If there is more than one subdiagnostic of the same type, then |
78 | these attributes can also take a value that is the attribute name to look for | |
f2b60f7d | 79 | (e.g. `previous_decl_label` in our example). |
923072b8 FG |
80 | |
81 | Other types have special behavior when used in a `SessionDiagnostic` derive: | |
82 | ||
83 | - Any attribute applied to an `Option<T>` and will only emit a | |
84 | subdiagnostic if the option is `Some(..)`. | |
85 | - Any attribute applied to a `Vec<T>` will be repeated for each element of the | |
86 | vector. | |
87 | ||
88 | `#[help]` and `#[note]` can also be applied to the struct itself, in which case | |
89 | they work exactly like when applied to fields except the subdiagnostic won't | |
90 | have a `Span`. These attributes can also be applied to fields of type `()` for | |
91 | the same effect, which when combined with the `Option` type can be used to | |
92 | represent optional `#[note]`/`#[help]` subdiagnostics. | |
93 | ||
94 | Suggestions can be emitted using one of four field attributes: | |
95 | ||
f2b60f7d FG |
96 | - `#[suggestion(slug, code = "...", applicability = "...")]` |
97 | - `#[suggestion_hidden(slug, code = "...", applicability = "...")]` | |
98 | - `#[suggestion_short(slug, code = "...", applicability = "...")]` | |
99 | - `#[suggestion_verbose(slug, code = "...", applicability = "...")]` | |
923072b8 FG |
100 | |
101 | Suggestions must be applied on either a `Span` field or a `(Span, | |
f2b60f7d FG |
102 | MachineApplicability)` field. Similarly to other field attributes, the slug |
103 | specifies the Fluent attribute with the message and defaults to the equivalent | |
104 | of `.suggestion`. `code` specifies the code that should be suggested as a | |
105 | replacement and is a format string (e.g. `{field_name}` would be replaced by | |
106 | the value of the `field_name` field of the struct), not a Fluent identifier. | |
107 | `applicability` can be used to specify the applicability in the attribute, it | |
108 | cannot be used when the field's type contains an `Applicability`. | |
923072b8 FG |
109 | |
110 | In the end, the `SessionDiagnostic` derive will generate an implementation of | |
111 | `SessionDiagnostic` that looks like the following: | |
112 | ||
113 | ```rust,ignore | |
f2b60f7d | 114 | impl SessionDiagnostic<'_> for FieldAlreadyDeclared { |
923072b8 | 115 | fn into_diagnostic(self, sess: &'_ rustc_session::Session) -> DiagnosticBuilder<'_> { |
f2b60f7d | 116 | let mut diag = sess.struct_err(rustc_errors::fluent::typeck::field_already_declared); |
923072b8 FG |
117 | diag.set_span(self.span); |
118 | diag.span_label( | |
119 | self.span, | |
f2b60f7d | 120 | rustc_errors::fluent::typeck::label |
923072b8 FG |
121 | ); |
122 | diag.span_label( | |
123 | self.prev_span, | |
f2b60f7d | 124 | rustc_errors::fluent::typeck::previous_decl_label |
923072b8 FG |
125 | ); |
126 | diag | |
127 | } | |
128 | } | |
129 | ``` | |
130 | ||
131 | Now that we've defined our diagnostic, how do we [use it][use]? It's quite | |
132 | straightforward, just create an instance of the struct and pass it to | |
133 | `emit_err` (or `emit_warning`): | |
134 | ||
135 | ```rust,ignore | |
136 | tcx.sess.emit_err(FieldAlreadyDeclared { | |
137 | field_name: f.ident, | |
138 | span: f.span, | |
139 | prev_span, | |
140 | }); | |
141 | ``` | |
142 | ||
143 | ### Reference | |
f2b60f7d FG |
144 | `#[derive(SessionDiagnostic)]` and `#[derive(LintDiagnostic)]` support the |
145 | following attributes: | |
923072b8 | 146 | |
f2b60f7d | 147 | - `#[diag(slug, code = "...")]` |
923072b8 FG |
148 | - _Applied to struct._ |
149 | - _Mandatory_ | |
f2b60f7d | 150 | - Defines the text and error code to be associated with the diagnostic. |
064997fb | 151 | - Slug (_Mandatory_) |
923072b8 FG |
152 | - Uniquely identifies the diagnostic and corresponds to its Fluent message, |
153 | mandatory. | |
064997fb FG |
154 | - A path to an item in `rustc_errors::fluent`. Always in a module starting |
155 | with a Fluent resource name (which is typically the name of the crate | |
156 | that the diagnostic is from), e.g. | |
157 | `rustc_errors::fluent::typeck::field_already_declared` | |
158 | (`rustc_errors::fluent` is implicit in the attribute, so just | |
159 | `typeck::field_already_declared`). | |
160 | - See [translation documentation](./translation.md). | |
161 | - `code = "..."` (_Optional_) | |
162 | - Specifies the error code. | |
f2b60f7d | 163 | - `#[note]` or `#[note(slug)]` (_Optional_) |
923072b8 FG |
164 | - _Applied to struct or `Span`/`()` fields._ |
165 | - Adds a note subdiagnostic. | |
f2b60f7d FG |
166 | - Value is a path to an item in `rustc_errors::fluent` for the note's |
167 | message. | |
168 | - Defaults to equivalent of `.note`. | |
923072b8 | 169 | - If applied to a `Span` field, creates a spanned note. |
f2b60f7d | 170 | - `#[help]` or `#[help(slug)]` (_Optional_) |
923072b8 FG |
171 | - _Applied to struct or `Span`/`()` fields._ |
172 | - Adds a help subdiagnostic. | |
f2b60f7d FG |
173 | - Value is a path to an item in `rustc_errors::fluent` for the note's |
174 | message. | |
175 | - Defaults to equivalent of `.help`. | |
923072b8 | 176 | - If applied to a `Span` field, creates a spanned help. |
f2b60f7d | 177 | - `#[label]` or `#[label(slug)]` (_Optional_) |
923072b8 FG |
178 | - _Applied to `Span` fields._ |
179 | - Adds a label subdiagnostic. | |
f2b60f7d FG |
180 | - Value is a path to an item in `rustc_errors::fluent` for the note's |
181 | message. | |
182 | - Defaults to equivalent of `.label`. | |
183 | - `#[warn_]` or `#[warn_(slug)]` (_Optional_) | |
184 | - _Applied to `Span` fields._ | |
185 | - Adds a warning subdiagnostic. | |
186 | - Value is a path to an item in `rustc_errors::fluent` for the note's | |
187 | message. | |
188 | - Defaults to equivalent of `.warn`. | |
189 | - `#[suggestion{,_hidden,_short,_verbose}(slug, code = "...", applicability = "...")]` | |
923072b8 FG |
190 | (_Optional_) |
191 | - _Applied to `(Span, MachineApplicability)` or `Span` fields._ | |
192 | - Adds a suggestion subdiagnostic. | |
f2b60f7d FG |
193 | - Slug (_Mandatory_) |
194 | - A path to an item in `rustc_errors::fluent`. Always in a module starting | |
195 | with a Fluent resource name (which is typically the name of the crate | |
196 | that the diagnostic is from), e.g. | |
197 | `rustc_errors::fluent::typeck::field_already_declared` | |
198 | (`rustc_errors::fluent` is implicit in the attribute, so just | |
199 | `typeck::field_already_declared`). Fluent attributes for all messages | |
200 | exist as top-level items in that module (so `typeck_message.attr` is just | |
201 | `typeck::attr`). | |
202 | - See [translation documentation](./translation.md). | |
203 | - Defaults to `rustc_errors::fluent::_subdiag::suggestion` (or | |
204 | - `.suggestion` in Fluent). | |
923072b8 FG |
205 | - `code = "..."` (_Mandatory_) |
206 | - Value is a format string indicating the code to be suggested as a | |
207 | replacement. | |
208 | - `applicability = "..."` (_Optional_) | |
209 | - String which must be one of `machine-applicable`, `maybe-incorrect`, | |
210 | `has-placeholders` or `unspecified`. | |
211 | - `#[subdiagnostic]` | |
212 | - _Applied to a type that implements `AddToDiagnostic` (from | |
213 | `#[derive(SessionSubdiagnostic)]`)._ | |
214 | - Adds the subdiagnostic represented by the subdiagnostic struct. | |
215 | - `#[primary_span]` (_Optional_) | |
f2b60f7d | 216 | - _Applied to `Span` fields on `SessionSubdiagnostic`s. Not used for `LintDiagnostic`s._ |
923072b8 FG |
217 | - Indicates the primary span of the diagnostic. |
218 | - `#[skip_arg]` (_Optional_) | |
219 | - _Applied to any field._ | |
220 | - Prevents the field from being provided as a diagnostic argument. | |
221 | ||
222 | ## `#[derive(SessionSubdiagnostic)]` | |
223 | It is common in the compiler to write a function that conditionally adds a | |
224 | specific subdiagnostic to an error if it is applicable. Oftentimes these | |
225 | subdiagnostics could be represented using a diagnostic struct even if the | |
226 | overall diagnostic could not. In this circumstance, the `SessionSubdiagnostic` | |
227 | derive can be used to represent a partial diagnostic (e.g a note, label, help or | |
228 | suggestion) as a struct. | |
229 | ||
230 | Consider the [definition][subdiag_defn] of the "expected return type" label | |
231 | shown below: | |
232 | ||
233 | ```rust | |
234 | #[derive(SessionSubdiagnostic)] | |
235 | pub enum ExpectedReturnTypeLabel<'tcx> { | |
064997fb | 236 | #[label(typeck::expected_default_return_type)] |
923072b8 FG |
237 | Unit { |
238 | #[primary_span] | |
239 | span: Span, | |
240 | }, | |
064997fb | 241 | #[label(typeck::expected_return_type)] |
923072b8 FG |
242 | Other { |
243 | #[primary_span] | |
244 | span: Span, | |
245 | expected: Ty<'tcx>, | |
246 | }, | |
247 | } | |
248 | ``` | |
249 | ||
250 | Unlike `SessionDiagnostic`, `SessionSubdiagnostic` can be applied to structs or | |
251 | enums. Attributes that are placed on the type for structs are placed on each | |
252 | variants for enums (or vice versa). Each `SessionSubdiagnostic` should have one | |
253 | attribute applied to the struct or each variant, one of: | |
254 | ||
255 | - `#[label(..)]` for defining a label | |
256 | - `#[note(..)]` for defining a note | |
257 | - `#[help(..)]` for defining a help | |
258 | - `#[suggestion{,_hidden,_short,_verbose}(..)]` for defining a suggestion | |
259 | ||
064997fb FG |
260 | All of the above must provide a slug as the first positional argument (a path |
261 | to an item in `rustc_errors::fluent::*`). A slug uniquely identifies the | |
262 | diagnostic and is also how the compiler knows what error message to emit (in | |
263 | the default locale of the compiler, or in the locale requested by the user). | |
264 | See [translation documentation](./translation.md) to learn more about how | |
265 | translatable error messages are written and how slug items are generated. | |
923072b8 FG |
266 | |
267 | In our example, the Fluent message for the "expected return type" label | |
268 | looks like this: | |
269 | ||
270 | ```fluent | |
f2b60f7d | 271 | typeck_expected_default_return_type = expected `()` because of default return type |
923072b8 | 272 | |
f2b60f7d | 273 | typeck_expected_return_type = expected `{$expected}` because of return type |
923072b8 FG |
274 | ``` |
275 | ||
276 | Using the `#[primary_span]` attribute on a field (with type `Span`) will denote | |
277 | the primary span of the subdiagnostic. A primary span is only necessary for a | |
278 | label or suggestion, which can not be spanless. | |
279 | ||
280 | Every field of the type/variant which does not have an annotation is available | |
281 | in Fluent messages as a variable. Fields can be annotated `#[skip_arg]` if this | |
282 | is undesired. | |
283 | ||
284 | Like `SessionDiagnostic`, `SessionSubdiagnostic` supports `Option<T>` and | |
285 | `Vec<T>` fields. | |
286 | ||
287 | Suggestions can be emitted using one of four attributes on the type/variant: | |
288 | ||
f2b60f7d FG |
289 | - `#[suggestion(..., code = "...", applicability = "...")]` |
290 | - `#[suggestion_hidden(..., code = "...", applicability = "...")]` | |
291 | - `#[suggestion_short(..., code = "...", applicability = "...")]` | |
292 | - `#[suggestion_verbose(..., code = "...", applicability = "...")]` | |
923072b8 FG |
293 | |
294 | Suggestions require `#[primary_span]` be set on a field and can have the | |
295 | following sub-attributes: | |
296 | ||
f2b60f7d FG |
297 | - The first positional argument specifies the path to a item in |
298 | `rustc_errors::fluent` corresponding to the Fluent attribute with the message | |
299 | and defaults to the equivalent of `.suggestion`. | |
923072b8 FG |
300 | - `code` specifies the code that should be suggested as a replacement and is a |
301 | format string (e.g. `{field_name}` would be replaced by the value of the | |
302 | `field_name` field of the struct), not a Fluent identifier. | |
303 | - `applicability` can be used to specify the applicability in the attribute, it | |
304 | cannot be used when the field's type contains an `Applicability`. | |
305 | ||
306 | Applicabilities can also be specified as a field (of type `Applicability`) | |
307 | using the `#[applicability]` attribute. | |
308 | ||
309 | In the end, the `SessionSubdiagnostic` derive will generate an implementation | |
310 | of `SessionSubdiagnostic` that looks like the following: | |
311 | ||
312 | ```rust | |
313 | impl<'tcx> AddToDiagnostic for ExpectedReturnTypeLabel<'tcx> { | |
314 | fn add_to_diagnostic(self, diag: &mut rustc_errors::Diagnostic) { | |
315 | use rustc_errors::{Applicability, IntoDiagnosticArg}; | |
316 | match self { | |
317 | ExpectedReturnTypeLabel::Unit { span } => { | |
f2b60f7d | 318 | diag.span_label(span, rustc_errors::fluent::typeck::expected_default_return_type) |
923072b8 FG |
319 | } |
320 | ExpectedReturnTypeLabel::Other { span, expected } => { | |
321 | diag.set_arg("expected", expected); | |
f2b60f7d | 322 | diag.span_label(span, rustc_errors::fluent::typeck::expected_return_type) |
923072b8 FG |
323 | } |
324 | ||
325 | } | |
326 | } | |
327 | } | |
328 | ``` | |
329 | ||
330 | Once defined, a subdiagnostic can be used by passing it to the `subdiagnostic` | |
331 | function ([example][subdiag_use_1] and [example][subdiag_use_2]) on a | |
332 | diagnostic or by assigning it to a `#[subdiagnostic]`-annotated field of a | |
333 | diagnostic struct. | |
334 | ||
335 | ### Reference | |
336 | `#[derive(SessionSubdiagnostic)]` supports the following attributes: | |
337 | ||
064997fb | 338 | - `#[label(slug)]`, `#[help(slug)]` or `#[note(slug)]` |
923072b8 FG |
339 | - _Applied to struct or enum variant. Mutually exclusive with struct/enum variant attributes._ |
340 | - _Mandatory_ | |
341 | - Defines the type to be representing a label, help or note. | |
064997fb | 342 | - Slug (_Mandatory_) |
923072b8 FG |
343 | - Uniquely identifies the diagnostic and corresponds to its Fluent message, |
344 | mandatory. | |
064997fb FG |
345 | - A path to an item in `rustc_errors::fluent`. Always in a module starting |
346 | with a Fluent resource name (which is typically the name of the crate | |
347 | that the diagnostic is from), e.g. | |
348 | `rustc_errors::fluent::typeck::field_already_declared` | |
349 | (`rustc_errors::fluent` is implicit in the attribute, so just | |
350 | `typeck::field_already_declared`). | |
351 | - See [translation documentation](./translation.md). | |
f2b60f7d | 352 | - `#[suggestion{,_hidden,_short,_verbose}(slug, code = "...", applicability = "...")]` |
923072b8 FG |
353 | - _Applied to struct or enum variant. Mutually exclusive with struct/enum variant attributes._ |
354 | - _Mandatory_ | |
355 | - Defines the type to be representing a suggestion. | |
f2b60f7d FG |
356 | - Slug (_Mandatory_) |
357 | - A path to an item in `rustc_errors::fluent`. Always in a module starting | |
358 | with a Fluent resource name (which is typically the name of the crate | |
359 | that the diagnostic is from), e.g. | |
360 | `rustc_errors::fluent::typeck::field_already_declared` | |
361 | (`rustc_errors::fluent` is implicit in the attribute, so just | |
362 | `typeck::field_already_declared`). Fluent attributes for all messages | |
363 | exist as top-level items in that module (so `typeck_message.attr` is just | |
364 | `typeck::attr`). | |
365 | - See [translation documentation](./translation.md). | |
366 | - Defaults to `rustc_errors::fluent::_subdiag::suggestion` (or | |
367 | - `.suggestion` in Fluent). | |
923072b8 FG |
368 | - `code = "..."` (_Mandatory_) |
369 | - Value is a format string indicating the code to be suggested as a | |
370 | replacement. | |
371 | - `applicability = "..."` (_Optional_) | |
372 | - _Mutually exclusive with `#[applicability]` on a field._ | |
373 | - Value is the applicability of the suggestion. | |
374 | - String which must be one of: | |
375 | - `machine-applicable` | |
376 | - `maybe-incorrect` | |
377 | - `has-placeholders` | |
378 | - `unspecified` | |
379 | - `#[primary_span]` (_Mandatory_ for labels and suggestions; _optional_ otherwise) | |
380 | - _Applied to `Span` fields._ | |
381 | - Indicates the primary span of the subdiagnostic. | |
382 | - `#[applicability]` (_Optional_; only applicable to suggestions) | |
383 | - _Applied to `Applicability` fields._ | |
384 | - Indicates the applicability of the suggestion. | |
385 | - `#[skip_arg]` (_Optional_) | |
386 | - _Applied to any field._ | |
387 | - Prevents the field from being provided as a diagnostic argument. | |
388 | ||
389 | [defn]: https://github.com/rust-lang/rust/blob/bbe9d27b8ff36da56638aa43d6d0cdfdf89a4e57/compiler/rustc_typeck/src/errors.rs#L65-L74 | |
390 | [use]: https://github.com/rust-lang/rust/blob/eb82facb1626166188d49599a3313fc95201f556/compiler/rustc_typeck/src/collect.rs#L981-L985 | |
391 | ||
392 | [subdiag_defn]: https://github.com/rust-lang/rust/blob/e70c60d34b9783a2fd3171d88d248c2e0ec8ecdd/compiler/rustc_typeck/src/errors.rs#L220-L233 | |
393 | [subdiag_use_1]: https://github.com/rust-lang/rust/blob/e70c60d34b9783a2fd3171d88d248c2e0ec8ecdd/compiler/rustc_typeck/src/check/fn_ctxt/suggestions.rs#L556-L560 | |
394 | [subdiag_use_2]: https://github.com/rust-lang/rust/blob/e70c60d34b9783a2fd3171d88d248c2e0ec8ecdd/compiler/rustc_typeck/src/check/fn_ctxt/suggestions.rs#L575-L579 |