Rust-GCC / cargo-gccrs

Gaining support for cargo via rustc-wrapper
Other
26 stars 6 forks source link

Output gccrs errors in json format #30

Open bjorn3 opened 3 years ago

bjorn3 commented 3 years ago

This allows cargo to cache them and allows programs like rust-analyzer to show errors inline. The rustc json error format consists of two parts: The rendered diagnostic as rustc would show it when not using the json error format. This is used by cargo. And the diagnostic in a structured format describing each part of the error message, like which parts of the source are underlined.

$ echo "fn foo() {} # fn bar() {}" | rustc - --error-format json |& jq
{
  "message": "expected one of `!` or `[`, found keyword `fn`",
  "code": null,
  "level": "error",
  "spans": [
    {
      "file_name": "<anon>",
      "byte_start": 14,
      "byte_end": 16,
      "line_start": 1,
      "line_end": 1,
      "column_start": 15,
      "column_end": 17,
      "is_primary": true,
      "text": [
        {
          "text": "fn foo() {} # fn bar() {}",
          "highlight_start": 15,
          "highlight_end": 17
        }
      ],
      "label": "expected one of `!` or `[`",
      "suggested_replacement": null,
      "suggestion_applicability": null,
      "expansion": null
    }
  ],
  "children": [],
  "rendered": "error: expected one of `!` or `[`, found keyword `fn`\n --> <anon>:1:15\n  |\n1 | fn foo() {} # fn bar() {}\n  |               ^^ expected one of `!` or `[`\n\n"
}
{
  "message": "aborting due to previous error",
  "code": null,
  "level": "error",
  "spans": [],
  "children": [],
  "rendered": "error: aborting due to previous error\n\n"
}

You can also pass --json diagnostic-rendered-ansi to use color ansi escape codes for the rendered diagnostic.

CohenArthur commented 3 years ago

I think this is something that should be done on the compiler side of things rather, but I'll see about opening the issues and how we can use it. On the top of your head, do you know where in the codebase cargo uses those JSONs? If not I'll look into it, thanks a lot for the issue!

bjorn3 commented 3 years ago

It is displayed to the user at https://github.com/rust-lang/cargo/blob/32238b474797da5b6730840219e4a487d73f2f72/src/cargo/core/compiler/mod.rs#L1255-L1288 and written to the disk for replaying during the next compilation at https://github.com/rust-lang/cargo/blob/32238b474797da5b6730840219e4a487d73f2f72/src/cargo/core/compiler/mod.rs#L1203-L1211

bjorn3 commented 3 years ago

I was thinking that gcc probably has it's own machine readable error format, so it may make sense to translate from this format to rustc's format in cargo-gccrs, just like it translates from rustc's options to gcc's options.

CohenArthur commented 3 years ago

I was thinking that gcc probably has it's own machine readable error format, so it may make sense to translate from this format to rustc's format in cargo-gccrs, just like it translates from rustc's options to gcc's options.

Ah good point, I didn't think about this. I have no idea about gcc's implementation for this. @philberty @tschwinge @dkm would you know if this is implemented already and where to find it?

dkm commented 3 years ago

Never really used that, but https://gcc.gnu.org/onlinedocs/gcc/Diagnostic-Message-Formatting-Options.html gives a -fdiagnostics-format=json. Maybe you could use that and make cargo and other tools happy?

CohenArthur commented 3 years ago

> gccrs -fdiagnostics-format=json src/main.rs &| jq .

[
  {
    "kind": "error",
    "column-origin": 1,
    "children": [],
    "locations": [
      {
        "caret": {
          "byte-column": 1,
          "display-column": 1,
          "line": 1,
          "file": "src/main.rs",
          "column": 1
        }
      },
      {
        "caret": {
          "byte-column": 1,
          "display-column": 1,
          "line": 1,
          "file": "src/main.rs",
          "column": 1
        }
      }
    ],
    "message": "expected [()] got [<integer>]"
  }
]

> rustc --error-format=json src/main.rs &| jq .

{
  "message": "mismatched types",
  "code": {
    "code": "E0308",
    "explanation": "Expected type did not match the received type.\n\nErroneous code examples:\n\n```compile_fail,E0308\nfn plus_one(x: i32) -> i32 {\n    x + 1\n}\n\nplus_one(\"Not a number\");\n//       ^^^^^^^^^^^^^^ expected `i32`, found `&str`\n\nif \"Not a bool\" {\n// ^^^^^^^^^^^^ expected `bool`, found `&str`\n}\n\nlet x: f32 = \"Not a float\";\n//     ---   ^^^^^^^^^^^^^ expected `f32`, found `&str`\n//     |\n//     expected due to this\n```\n\nThis error occurs when an expression was used in a place where the compiler\nexpected an expression of a different type. It can occur in several cases, the\nmost common being when calling a function and passing an argument which has a\ndifferent type than the matching type in the function declaration.\n"
  },
  "level": "error",
  "spans": [
    {
      "file_name": "src/main.rs",
      "byte_start": 16,
      "byte_end": 17,
      "line_start": 2,
      "line_end": 2,
      "column_start": 5,
      "column_end": 6,
      "is_primary": true,
      "text": [
        {
          "text": "    1",
          "highlight_start": 5,
          "highlight_end": 6
        }
      ],
      "label": "expected `()`, found integer",
      "suggested_replacement": null,
      "suggestion_applicability": null,
      "expansion": null
    },
    {
      "file_name": "src/main.rs",
      "byte_start": 10,
      "byte_end": 10,
      "line_start": 1,
      "line_end": 1,
      "column_start": 11,
      "column_end": 11,
      "is_primary": false,
      "text": [
        {
          "text": "fn main() {",
          "highlight_start": 11,
          "highlight_end": 11
        }
      ],
      "label": "expected `()` because of default return type",
      "suggested_replacement": null,
      "suggestion_applicability": null,
      "expansion": null
    }
  ],
  "children": [],
  "rendered": "error[E0308]: mismatched types\n --> src/main.rs:2:5\n  |\n1 | fn main() {\n  |           - expected `()` because of default return type\n2 |     1\n  |     ^ expected `()`, found integer\n\n"
}
{
  "message": "aborting due to previous error",
  "code": null,
  "level": "error",
  "spans": [],
  "children": [],
  "rendered": "error: aborting due to previous error\n\n"
}
{
  "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`.\n"
}

I think the message could definitely be translated. I'll look into how important this is for cargo and get on it if it is high priority. Thanks a lot @dkm I did not know about that gcc option at all!

CohenArthur commented 3 years ago

This would also probably close #29