Rustc's json output format


#1

Continuing the discussion from Editor compatibility and the new error format:

rustc can already produce json: -Z unstable-options --error-format=json, but it’s rather crude/redundant so far. In the Atom plugin build-cargo I chose to simply ignore all output lines not starting with a { (so any cargo output). There’s an open issue for cargo here: https://github.com/rust-lang/cargo/issues/1403

I’m quite happy with the current format. It allowed me to create error messages of the following look:

https://cloud.githubusercontent.com/assets/332036/15097688/ddfc170c-1523-11e6-8394-d24a79d125ea.png

Once I run a markdown -> html formatter over the explanation, it should look just like the error page.

The format is defined here: https://github.com/rust-lang/rust/blob/master/src/libsyntax/errors/json.rs#L74-L146

The format itself allows children to have children of their own, so technically someone parsing the format needs to allow for that case, even though rustc never produces it. Maybe the format should make that clear by separating out a Child type with fewer fields.


#2

Two tips:

  • There is RUSTFLAGS environmental variable which should help with passing --error-format
  • “chose to simply ignore all output lines not starting with a” <-- this hopefully will become unnecessary when #1473 is fixed

#3

So this is roughly the format that I would like to see. I can’t see to copy-and-paste here so this is a link to a gist. But the high-level idea is to include roughly the same logical information that we include today, along with presentation information. The presentation information would be literally the same output that rustc emits, but augmented with formatting info (like "this should be styled as the “main message”, or “this is the filename/line-number” or “this is a line from the source text”). The goal of the presentation information is to make it easy for tools to reproduce rustc’s output and formatting, which will presumably evolve and improve, without having to reproduce all of our internal logic for extracting snippets and rendering annotations.


#4

I see two problems with RUSTFLAGS to convey a request for JSON:

  • rustc itself doesn’t respect it, only cargo (we could change this)
  • I see RUSTFLAGS as being more for humans to inject flags than tools, but I guess that’s a relatively minor point; specifically, I expect people to do things like make foo RUSTFLAGS="-g" which would then ignore any RUSTFLAGS settings installed by the editor.

But this will depend a lot on the environment. I am assuming a tool like emacs, where users may wish to specify their own build command, but we would like to silently control the output format. But this particular aspect of the discussion (how to select json output) feels like it belongs more to the original thread


#5

Yeah, cargo outputting as json would be great.


#6

This is indeed cool. It seems to me that you too might benefit from the “presentation” info I have been describing, but maybe not.


#7

So basically it could be html with style attributes. Then end users can choose their css style or simply strip the html.

Diagnostic -> logical -> suggestions (Vec<(Span, String)>)?

What if there are multiple possible suggestions?

Diagnostic -> logical -> error code

There are also errors due to deny’d lints, these don’t have an error code, but they have a lint name. Clippy goes as far as giving a url to the lint documentation, but the explanation is probably enough.

I don’t think so, the logical format is great to process, and I’m not creating the tooltip, I’m utilizing an existing framework.


#8

Yeah. We could even use HTML I guess.

I’m not sure I follow. Currently, though, we produce a suggested edit. I guess you are saying what if we made multiple suggested edits?

Yeah, it’d be good to expose the lint name in the JSON I imagine.

Seems fine. I figured that the more “inline” the presentation, the less useful rustc’s output would be. I could imagine though that you might want to explain borrow check errors this way (for example) – but maybe not. Maybe you can render it inline.


#9

By the way, is this some publicly available Atom plugin?


#10

It hasn’t been merged yet, but to get the same behaviour, you can install the build package (version 0.63.0), install the linter package (search for linter base) and create an .atom-build.js in your project root with the following content:

module.exports = {
  cmd: "cargo",
  args: ["build"],
  env: {
    "RUSTFLAGS": "-Z unstable-options --error-format=json"
  },
  functionMatch: function(output) {
    var array = [];
    function level2severity(level) {
      switch (level) {
        case 'warning': return 'warning';
        case 'error': return 'error';
        case 'note': return 'info';
        default: return 'error';
      }
    }
    output.split(/\n/).forEach(line => {
      if (line[0] != '{') {
          return;
      }
      const json = JSON.parse(line);
      var trace = [];
      json.children.forEach(child => {
        child.spans.forEach(span => {
          trace.push({
            message: child.message,
            file: span.file_name,
            line: span.line_start,
            line_end: span.line_end,
            col: span.column_start,
            col_end: span.column_end,
            type: 'Trace', // FIXME: change to `child.level` after https://github.com/steelbrain/linter/issues/1149 is fixed
            severity: level2severity(json.level),
          });
        });
      });
      if (json.code) {
        trace.push({
          message: json.code.explanation,
          type: "Explanation",
          severity: 'info',
        });
      }
      json.spans.forEach(span => {
        array.push({
          message: json.message,
          file: span.file_name,
          line: span.line_start,
          line_end: span.line_end,
          col: span.column_start,
          col_end: span.column_end,
          type: json.level, // FIXME: change to `json.code ? json.code : json.level` after https://github.com/steelbrain/linter/issues/1149 is fixed
          severity: level2severity(json.level),
          trace: trace,
        });
      });
    });
    return array;
  }
};

Adding the .atom-build.js file from within atom might require an atom restart (Ctrl + Alt + R). Then you should be able to press Ctrl + Alt + B to trigger a build. The exact keys may vary between operating systems (F6 seems to work, too).


#11

So, here’s my current report on the usability of the json output:

“expected X, got Y” errors

These are split into two children without spans. There’s a few ways that they could be improved.

  1. for types, the error could give the span of the reason for the expected. So if it’s a function argument, give a span for that argument’s declaration.
  2. these are so common in rustc that there’s special code processing this kind of text into multiple lines
  • json should profit from this, too, maybe an expected field?
  1. these should not be split up, but I’m not sure how to do this better, other than 2.

multiple suggested imports

To automate future 1-click-solutions, instead of listing them in the current note manner, the suggested_replacement could be used to offer inserting a new line on the top of the file with use xyz;.

suggestions for suggestions

  1. remove the suggested_replacement field, and instead add a new "error level".
  2. generally use suggestions more often. E.g. in unresolved name errors

the label field

I’ve so far ignored it. Many errors do not set it, and with those that do set it, I’ve found that I can’t replace the main message, and that it’s partially redundant with the notes. An example is a type-mismatch. let _: () = 5; gives "type mismatch" for the message, expected type '()' and got type '_' for the notes, and then "expected (), found integral variable" for the label. The label is nicer than the notes, but still needs the message.

I could simply show it when it’s there, but I don’t want to clutter the error popups with redundancy.

#Macro Expansion

I’m not sure how to improve on this yet. An alternative would be to repeat the error at every expansion location. Or just show something at the location of the expansion.

#WIP

Anyway, this is a work in progress. If anyone has ideas what to try out, I’ll check how it works and post some screenshots.


Stabilizing json compiler message output