Skip to content

Commit 85c42b7

Browse files
committed
Auto merge of rust-lang#115691 - jsgf:typed-json-diags, r=est31,dtolnay
Add `$message_type` field to distinguish json diagnostic outputs Currently the json-formatted outputs have no way to unambiguously determine which kind of message is being output. A consumer can look for specific fields in the json object (eg "message"), but there's no guarantee that in future some other kind of output will have a field of the same name. This PR adds a `"type"` field to add json outputs which can be used to unambiguously determine which kind of output it is. The mapping is: `diagnostic`: regular compiler diagnostics `artifact`: artifact notifications `future_incompat`: Future incompatibility report `unused_extern`: Unused crate warnings/errors This matches the "internally tagged" representation for serde enums.
2 parents 390e3c8 + fe50c53 commit 85c42b7

17 files changed

+100
-67
lines changed

compiler/rustc_errors/src/json.rs

+35-30
Original file line numberDiff line numberDiff line change
@@ -145,6 +145,25 @@ impl JsonEmitter {
145145
pub fn ignored_directories_in_source_blocks(self, value: Vec<String>) -> Self {
146146
Self { ignored_directories_in_source_blocks: value, ..self }
147147
}
148+
149+
fn emit(&mut self, val: EmitTyped<'_>) -> io::Result<()> {
150+
if self.pretty {
151+
serde_json::to_writer_pretty(&mut *self.dst, &val)?
152+
} else {
153+
serde_json::to_writer(&mut *self.dst, &val)?
154+
};
155+
self.dst.write_all(b"\n")?;
156+
self.dst.flush()
157+
}
158+
}
159+
160+
#[derive(Serialize)]
161+
#[serde(tag = "$message_type", rename_all = "snake_case")]
162+
enum EmitTyped<'a> {
163+
Diagnostic(Diagnostic),
164+
Artifact(ArtifactNotification<'a>),
165+
FutureIncompat(FutureIncompatReport<'a>),
166+
UnusedExtern(UnusedExterns<'a, 'a, 'a>),
148167
}
149168

150169
impl Translate for JsonEmitter {
@@ -160,47 +179,36 @@ impl Translate for JsonEmitter {
160179
impl Emitter for JsonEmitter {
161180
fn emit_diagnostic(&mut self, diag: &crate::Diagnostic) {
162181
let data = Diagnostic::from_errors_diagnostic(diag, self);
163-
let result = if self.pretty {
164-
writeln!(&mut self.dst, "{}", serde_json::to_string_pretty(&data).unwrap())
165-
} else {
166-
writeln!(&mut self.dst, "{}", serde_json::to_string(&data).unwrap())
167-
}
168-
.and_then(|_| self.dst.flush());
182+
let result = self.emit(EmitTyped::Diagnostic(data));
169183
if let Err(e) = result {
170184
panic!("failed to print diagnostics: {e:?}");
171185
}
172186
}
173187

174188
fn emit_artifact_notification(&mut self, path: &Path, artifact_type: &str) {
175189
let data = ArtifactNotification { artifact: path, emit: artifact_type };
176-
let result = if self.pretty {
177-
writeln!(&mut self.dst, "{}", serde_json::to_string_pretty(&data).unwrap())
178-
} else {
179-
writeln!(&mut self.dst, "{}", serde_json::to_string(&data).unwrap())
180-
}
181-
.and_then(|_| self.dst.flush());
190+
let result = self.emit(EmitTyped::Artifact(data));
182191
if let Err(e) = result {
183192
panic!("failed to print notification: {e:?}");
184193
}
185194
}
186195

187196
fn emit_future_breakage_report(&mut self, diags: Vec<crate::Diagnostic>) {
188-
let data: Vec<FutureBreakageItem> = diags
197+
let data: Vec<FutureBreakageItem<'_>> = diags
189198
.into_iter()
190199
.map(|mut diag| {
191200
if diag.level == crate::Level::Allow {
192201
diag.level = crate::Level::Warning(None);
193202
}
194-
FutureBreakageItem { diagnostic: Diagnostic::from_errors_diagnostic(&diag, self) }
203+
FutureBreakageItem {
204+
diagnostic: EmitTyped::Diagnostic(Diagnostic::from_errors_diagnostic(
205+
&diag, self,
206+
)),
207+
}
195208
})
196209
.collect();
197210
let report = FutureIncompatReport { future_incompat_report: data };
198-
let result = if self.pretty {
199-
writeln!(&mut self.dst, "{}", serde_json::to_string_pretty(&report).unwrap())
200-
} else {
201-
writeln!(&mut self.dst, "{}", serde_json::to_string(&report).unwrap())
202-
}
203-
.and_then(|_| self.dst.flush());
211+
let result = self.emit(EmitTyped::FutureIncompat(report));
204212
if let Err(e) = result {
205213
panic!("failed to print future breakage report: {e:?}");
206214
}
@@ -209,12 +217,7 @@ impl Emitter for JsonEmitter {
209217
fn emit_unused_externs(&mut self, lint_level: rustc_lint_defs::Level, unused_externs: &[&str]) {
210218
let lint_level = lint_level.as_str();
211219
let data = UnusedExterns { lint_level, unused_extern_names: unused_externs };
212-
let result = if self.pretty {
213-
writeln!(&mut self.dst, "{}", serde_json::to_string_pretty(&data).unwrap())
214-
} else {
215-
writeln!(&mut self.dst, "{}", serde_json::to_string(&data).unwrap())
216-
}
217-
.and_then(|_| self.dst.flush());
220+
let result = self.emit(EmitTyped::UnusedExtern(data));
218221
if let Err(e) = result {
219222
panic!("failed to print unused externs: {e:?}");
220223
}
@@ -313,13 +316,15 @@ struct ArtifactNotification<'a> {
313316
}
314317

315318
#[derive(Serialize)]
316-
struct FutureBreakageItem {
317-
diagnostic: Diagnostic,
319+
struct FutureBreakageItem<'a> {
320+
// Always EmitTyped::Diagnostic, but we want to make sure it gets serialized
321+
// with "$message_type".
322+
diagnostic: EmitTyped<'a>,
318323
}
319324

320325
#[derive(Serialize)]
321-
struct FutureIncompatReport {
322-
future_incompat_report: Vec<FutureBreakageItem>,
326+
struct FutureIncompatReport<'a> {
327+
future_incompat_report: Vec<FutureBreakageItem<'a>>,
323328
}
324329

325330
// NOTE: Keep this in sync with the equivalent structs in rustdoc's

src/doc/rustc/src/json.md

+11-3
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,11 @@ If parsing the output with Rust, the
1111
[`cargo_metadata`](https://crates.io/crates/cargo_metadata) crate provides
1212
some support for parsing the messages.
1313

14-
When parsing, care should be taken to be forwards-compatible with future changes
15-
to the format. Optional values may be `null`. New fields may be added. Enumerated
16-
fields like "level" or "suggestion_applicability" may add new values.
14+
Each type of message has a `$message_type` field which can be used to
15+
distinguish the different formats. When parsing, care should be taken
16+
to be forwards-compatible with future changes to the format. Optional
17+
values may be `null`. New fields may be added. Enumerated fields like
18+
"level" or "suggestion_applicability" may add new values.
1719

1820
## Diagnostics
1921

@@ -29,6 +31,8 @@ Diagnostics have the following format:
2931

3032
```javascript
3133
{
34+
/* Type of this message */
35+
"$message_type": "diagnostic",
3236
/* The primary message. */
3337
"message": "unused variable: `x`",
3438
/* The diagnostic code.
@@ -217,6 +221,8 @@ flag][option-emit] documentation.
217221

218222
```javascript
219223
{
224+
/* Type of this message */
225+
"$message_type": "artifact",
220226
/* The filename that was generated. */
221227
"artifact": "libfoo.rlib",
222228
/* The kind of artifact that was generated. Possible values:
@@ -239,6 +245,8 @@ information, even if the diagnostics have been suppressed (such as with an
239245

240246
```javascript
241247
{
248+
/* Type of this message */
249+
"$message_type": "future_incompat",
242250
/* An array of objects describing a warning that will become a hard error
243251
in the future.
244252
*/

tests/ui/diagnostic-width/flag-json.stderr

+3-3
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
{"message":"mismatched types","code":{"code":"E0308","explanation":"Expected type did not match the received type.
1+
{"$message_type":"diagnostic","message":"mismatched types","code":{"code":"E0308","explanation":"Expected type did not match the received type.
22

33
Erroneous code examples:
44

@@ -33,8 +33,8 @@ LL | ..._: () = 42;
3333
| expected due to this
3434

3535
"}
36-
{"message":"aborting due to previous error","code":null,"level":"error","spans":[],"children":[],"rendered":"error: aborting due to previous error
36+
{"$message_type":"diagnostic","message":"aborting due to previous error","code":null,"level":"error","spans":[],"children":[],"rendered":"error: aborting due to previous error
3737

3838
"}
39-
{"message":"For more information about this error, try `rustc --explain E0308`.","code":null,"level":"failure-note","spans":[],"children":[],"rendered":"For more information about this error, try `rustc --explain E0308`.
39+
{"$message_type":"diagnostic","message":"For more information about this error, try `rustc --explain E0308`.","code":null,"level":"failure-note","spans":[],"children":[],"rendered":"For more information about this error, try `rustc --explain E0308`.
4040
"}

tests/ui/json/json-bom-plus-crlf-multifile.stderr

+5-5
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
{"message":"mismatched types","code":{"code":"E0308","explanation":"Expected type did not match the received type.
1+
{"$message_type":"diagnostic","message":"mismatched types","code":{"code":"E0308","explanation":"Expected type did not match the received type.
22

33
Erroneous code examples:
44

@@ -26,7 +26,7 @@ most common being when calling a function and passing an argument which has a
2626
different type than the matching type in the function declaration.
2727
"},"level":"error","spans":[{"file_name":"$DIR/json-bom-plus-crlf-multifile-aux.rs","byte_start":621,"byte_end":622,"line_start":17,"line_end":17,"column_start":22,"column_end":23,"is_primary":true,"text":[{"text":" let s : String = 1; // Error in the middle of line.","highlight_start":22,"highlight_end":23}],"label":"expected `String`, found integer","suggested_replacement":null,"suggestion_applicability":null,"expansion":null},{"file_name":"$DIR/json-bom-plus-crlf-multifile-aux.rs","byte_start":612,"byte_end":618,"line_start":17,"line_end":17,"column_start":13,"column_end":19,"is_primary":false,"text":[{"text":" let s : String = 1; // Error in the middle of line.","highlight_start":13,"highlight_end":19}],"label":"expected due to this","suggested_replacement":null,"suggestion_applicability":null,"expansion":null}],"children":[{"message":"try using a conversion method","code":null,"level":"help","spans":[{"file_name":"$DIR/json-bom-plus-crlf-multifile-aux.rs","byte_start":622,"byte_end":622,"line_start":17,"line_end":17,"column_start":23,"column_end":23,"is_primary":true,"text":[{"text":" let s : String = 1; // Error in the middle of line.","highlight_start":23,"highlight_end":23}],"label":null,"suggested_replacement":".to_string()","suggestion_applicability":"MaybeIncorrect","expansion":null}],"children":[],"rendered":null}],"rendered":"$DIR/json-bom-plus-crlf-multifile-aux.rs:17:22: error[E0308]: mismatched types
2828
"}
29-
{"message":"mismatched types","code":{"code":"E0308","explanation":"Expected type did not match the received type.
29+
{"$message_type":"diagnostic","message":"mismatched types","code":{"code":"E0308","explanation":"Expected type did not match the received type.
3030

3131
Erroneous code examples:
3232

@@ -54,7 +54,7 @@ most common being when calling a function and passing an argument which has a
5454
different type than the matching type in the function declaration.
5555
"},"level":"error","spans":[{"file_name":"$DIR/json-bom-plus-crlf-multifile-aux.rs","byte_start":681,"byte_end":682,"line_start":19,"line_end":19,"column_start":22,"column_end":23,"is_primary":true,"text":[{"text":" let s : String = 1","highlight_start":22,"highlight_end":23}],"label":"expected `String`, found integer","suggested_replacement":null,"suggestion_applicability":null,"expansion":null},{"file_name":"$DIR/json-bom-plus-crlf-multifile-aux.rs","byte_start":672,"byte_end":678,"line_start":19,"line_end":19,"column_start":13,"column_end":19,"is_primary":false,"text":[{"text":" let s : String = 1","highlight_start":13,"highlight_end":19}],"label":"expected due to this","suggested_replacement":null,"suggestion_applicability":null,"expansion":null}],"children":[{"message":"try using a conversion method","code":null,"level":"help","spans":[{"file_name":"$DIR/json-bom-plus-crlf-multifile-aux.rs","byte_start":682,"byte_end":682,"line_start":19,"line_end":19,"column_start":23,"column_end":23,"is_primary":true,"text":[{"text":" let s : String = 1","highlight_start":23,"highlight_end":23}],"label":null,"suggested_replacement":".to_string()","suggestion_applicability":"MaybeIncorrect","expansion":null}],"children":[],"rendered":null}],"rendered":"$DIR/json-bom-plus-crlf-multifile-aux.rs:19:22: error[E0308]: mismatched types
5656
"}
57-
{"message":"mismatched types","code":{"code":"E0308","explanation":"Expected type did not match the received type.
57+
{"$message_type":"diagnostic","message":"mismatched types","code":{"code":"E0308","explanation":"Expected type did not match the received type.
5858

5959
Erroneous code examples:
6060

@@ -82,7 +82,7 @@ most common being when calling a function and passing an argument which has a
8282
different type than the matching type in the function declaration.
8383
"},"level":"error","spans":[{"file_name":"$DIR/json-bom-plus-crlf-multifile-aux.rs","byte_start":745,"byte_end":746,"line_start":23,"line_end":23,"column_start":1,"column_end":2,"is_primary":true,"text":[{"text":"1; // Error after the newline.","highlight_start":1,"highlight_end":2}],"label":"expected `String`, found integer","suggested_replacement":null,"suggestion_applicability":null,"expansion":null},{"file_name":"$DIR/json-bom-plus-crlf-multifile-aux.rs","byte_start":735,"byte_end":741,"line_start":22,"line_end":22,"column_start":13,"column_end":19,"is_primary":false,"text":[{"text":" let s : String =","highlight_start":13,"highlight_end":19}],"label":"expected due to this","suggested_replacement":null,"suggestion_applicability":null,"expansion":null}],"children":[{"message":"try using a conversion method","code":null,"level":"help","spans":[{"file_name":"$DIR/json-bom-plus-crlf-multifile-aux.rs","byte_start":746,"byte_end":746,"line_start":23,"line_end":23,"column_start":2,"column_end":2,"is_primary":true,"text":[{"text":"1; // Error after the newline.","highlight_start":2,"highlight_end":2}],"label":null,"suggested_replacement":".to_string()","suggestion_applicability":"MaybeIncorrect","expansion":null}],"children":[],"rendered":null}],"rendered":"$DIR/json-bom-plus-crlf-multifile-aux.rs:23:1: error[E0308]: mismatched types
8484
"}
85-
{"message":"mismatched types","code":{"code":"E0308","explanation":"Expected type did not match the received type.
85+
{"$message_type":"diagnostic","message":"mismatched types","code":{"code":"E0308","explanation":"Expected type did not match the received type.
8686

8787
Erroneous code examples:
8888

@@ -110,5 +110,5 @@ most common being when calling a function and passing an argument which has a
110110
different type than the matching type in the function declaration.
111111
"},"level":"error","spans":[{"file_name":"$DIR/json-bom-plus-crlf-multifile-aux.rs","byte_start":801,"byte_end":809,"line_start":25,"line_end":26,"column_start":22,"column_end":6,"is_primary":true,"text":[{"text":" let s : String = (","highlight_start":22,"highlight_end":23},{"text":" ); // Error spanning the newline.","highlight_start":1,"highlight_end":6}],"label":"expected `String`, found `()`","suggested_replacement":null,"suggestion_applicability":null,"expansion":null},{"file_name":"$DIR/json-bom-plus-crlf-multifile-aux.rs","byte_start":792,"byte_end":798,"line_start":25,"line_end":25,"column_start":13,"column_end":19,"is_primary":false,"text":[{"text":" let s : String = (","highlight_start":13,"highlight_end":19}],"label":"expected due to this","suggested_replacement":null,"suggestion_applicability":null,"expansion":null}],"children":[],"rendered":"$DIR/json-bom-plus-crlf-multifile-aux.rs:25:22: error[E0308]: mismatched types
112112
"}
113-
{"message":"aborting due to 4 previous errors","code":null,"level":"error","spans":[],"children":[],"rendered":"error: aborting due to 4 previous errors
113+
{"$message_type":"diagnostic","message":"aborting due to 4 previous errors","code":null,"level":"error","spans":[],"children":[],"rendered":"error: aborting due to 4 previous errors
114114
"}

0 commit comments

Comments
 (0)