mirror of
https://github.com/compiler-explorer/compiler-explorer.git
synced 2025-12-27 07:04:04 -05:00
Fix linking Rust error messages to source (#8345)
The compiler output pane only links error messages to the source editor when a filename is given. `parseRustOutput` didn’t parse the filename, so Rust error messages were never linked. This PR also simplifies the regex used to parse the `--> filename:line:column` line in the `rustc` output. As far as I’m aware (and I checked the history of the corresponding output in the `rustc` source), the output format will always be `--> filename:line:column` and not anything else accepted by the previous regex (no parentheses around the line number, the column is not optional).
This commit is contained in:
13
lib/utils.ts
13
lib/utils.ts
@@ -255,6 +255,7 @@ export function parseOutput(
|
||||
}
|
||||
|
||||
export function parseRustOutput(lines: string, inputFilename?: string, pathPrefix?: string) {
|
||||
const inputBasename = inputFilename ? path.basename(inputFilename) : undefined;
|
||||
const quickfixes: {re: RegExp; makeFix: (match: string[]) => Fix}[] = [
|
||||
{
|
||||
re: / *help: add `#!\[feature\((.*?)\)]`/,
|
||||
@@ -288,7 +289,7 @@ export function parseRustOutput(lines: string, inputFilename?: string, pathPrefi
|
||||
},
|
||||
];
|
||||
|
||||
const re = /^\s+-->\s+<source>[(:](\d+)(:?,?(\d+):?)?[):]*\s*(.*)/;
|
||||
const re = /^\s+-->\s+(?<filename>.*):(?<line>\d+):(?<column>\d+)/;
|
||||
const result: ResultLine[] = [];
|
||||
let currentDiagnostic: ResultLine | undefined;
|
||||
eachLine(lines, line => {
|
||||
@@ -298,14 +299,17 @@ export function parseRustOutput(lines: string, inputFilename?: string, pathPrefi
|
||||
const filteredLine = filterEscapeSequences(line);
|
||||
const match = filteredLine.match(re);
|
||||
|
||||
if (match) {
|
||||
const line = Number.parseInt(match[1], 10);
|
||||
const column = Number.parseInt(match[3] || '0', 10);
|
||||
if (match?.groups) {
|
||||
const file =
|
||||
match.groups.filename === '<source>' ? inputBasename : path.basename(match.groups.filename);
|
||||
const line = Number.parseInt(match.groups.line, 10);
|
||||
const column = Number.parseInt(match.groups.column, 10);
|
||||
|
||||
currentDiagnostic = result.pop();
|
||||
if (currentDiagnostic !== undefined) {
|
||||
const text = filterEscapeSequences(currentDiagnostic.text);
|
||||
currentDiagnostic.tag = {
|
||||
file,
|
||||
line,
|
||||
column,
|
||||
text,
|
||||
@@ -316,6 +320,7 @@ export function parseRustOutput(lines: string, inputFilename?: string, pathPrefi
|
||||
}
|
||||
|
||||
lineObj.tag = {
|
||||
file,
|
||||
line,
|
||||
column,
|
||||
text: '', // Left empty so that it does not show up in the editor
|
||||
|
||||
@@ -247,49 +247,49 @@ describe('Pascal compiler output', () => {
|
||||
describe('Rust compiler output', () => {
|
||||
it('handles simple cases', () => {
|
||||
expect(utils.parseRustOutput('Line one\nLine two', 'bob.rs')).toEqual([{text: 'Line one'}, {text: 'Line two'}]);
|
||||
expect(utils.parseRustOutput('Unrelated\nLine one\n --> bob.rs:1\nUnrelated', 'bob.rs')).toEqual([
|
||||
expect(utils.parseRustOutput('Unrelated\nLine one\n --> bob.rs:1:0\nUnrelated', 'bob.rs')).toEqual([
|
||||
{text: 'Unrelated'},
|
||||
{
|
||||
tag: {column: 0, line: 1, text: 'Line one', severity: 3, fixes: []},
|
||||
tag: {file: 'bob.rs', column: 0, line: 1, text: 'Line one', severity: 3, fixes: []},
|
||||
text: 'Line one',
|
||||
},
|
||||
{
|
||||
tag: {column: 0, line: 1, text: '', severity: 3},
|
||||
text: ' --> <source>:1',
|
||||
tag: {file: 'bob.rs', column: 0, line: 1, text: '', severity: 3},
|
||||
text: ' --> <source>:1:0',
|
||||
},
|
||||
{text: 'Unrelated'},
|
||||
]);
|
||||
expect(utils.parseRustOutput('Line one\n --> bob.rs:1:5', 'bob.rs')).toEqual([
|
||||
{
|
||||
tag: {column: 5, line: 1, text: 'Line one', severity: 3, fixes: []},
|
||||
tag: {file: 'bob.rs', column: 5, line: 1, text: 'Line one', severity: 3, fixes: []},
|
||||
text: 'Line one',
|
||||
},
|
||||
{
|
||||
tag: {column: 5, line: 1, text: '', severity: 3},
|
||||
tag: {file: 'bob.rs', column: 5, line: 1, text: '', severity: 3},
|
||||
text: ' --> <source>:1:5',
|
||||
},
|
||||
]);
|
||||
expect(utils.parseRustOutput('Multiple spaces\n --> bob.rs:1:5', 'bob.rs')).toEqual([
|
||||
{
|
||||
tag: {column: 5, line: 1, text: 'Multiple spaces', severity: 3, fixes: []},
|
||||
tag: {file: 'bob.rs', column: 5, line: 1, text: 'Multiple spaces', severity: 3, fixes: []},
|
||||
text: 'Multiple spaces',
|
||||
},
|
||||
{
|
||||
tag: {column: 5, line: 1, text: '', severity: 3},
|
||||
tag: {file: 'bob.rs', column: 5, line: 1, text: '', severity: 3},
|
||||
text: ' --> <source>:1:5',
|
||||
},
|
||||
]);
|
||||
});
|
||||
|
||||
it('replaces all references to input source', () => {
|
||||
expect(utils.parseRustOutput('error: Error in bob.rs\n --> bob.rs:1', 'bob.rs')).toEqual([
|
||||
expect(utils.parseRustOutput('error: Error in bob.rs\n --> bob.rs:1:42', 'bob.rs')).toEqual([
|
||||
{
|
||||
tag: {column: 0, line: 1, text: 'error: Error in <source>', severity: 3, fixes: []},
|
||||
tag: {file: 'bob.rs', column: 42, line: 1, text: 'error: Error in <source>', severity: 3, fixes: []},
|
||||
text: 'error: Error in <source>',
|
||||
},
|
||||
{
|
||||
tag: {column: 0, line: 1, text: '', severity: 3},
|
||||
text: ' --> <source>:1',
|
||||
tag: {file: 'bob.rs', column: 42, line: 1, text: '', severity: 3},
|
||||
text: ' --> <source>:1:42',
|
||||
},
|
||||
]);
|
||||
});
|
||||
@@ -297,11 +297,11 @@ describe('Rust compiler output', () => {
|
||||
it('treats <stdin> as if it were the compiler source', () => {
|
||||
expect(utils.parseRustOutput('error: <stdin> is sad\n --> <stdin>:120:25', 'bob.rs')).toEqual([
|
||||
{
|
||||
tag: {column: 25, line: 120, text: 'error: <source> is sad', severity: 3, fixes: []},
|
||||
tag: {file: 'bob.rs', column: 25, line: 120, text: 'error: <source> is sad', severity: 3, fixes: []},
|
||||
text: 'error: <source> is sad',
|
||||
},
|
||||
{
|
||||
tag: {column: 25, line: 120, text: '', severity: 3},
|
||||
tag: {file: 'bob.rs', column: 25, line: 120, text: '', severity: 3},
|
||||
text: ' --> <source>:120:25',
|
||||
},
|
||||
]);
|
||||
@@ -412,6 +412,68 @@ describe('Rust compiler output', () => {
|
||||
{text: ' = help: add `#![feature(num_midpoint_signed)]` to the crate attributes to enable'},
|
||||
]);
|
||||
});
|
||||
|
||||
it('attaches filenames to errors from multiple files', () => {
|
||||
// https://godbolt.org/z/nWE3PTeGf
|
||||
expect(
|
||||
utils.parseRustOutput(
|
||||
`warning: function \`f1\` is never used
|
||||
--> <source>:3:4
|
||||
|
||||
warning: function \`f2\` is never used
|
||||
--> /app/m.rs:1:4
|
||||
|
||||
warning: 2 warnings emitted`,
|
||||
'example.rs',
|
||||
),
|
||||
).toEqual([
|
||||
{
|
||||
text: 'warning: function `f1` is never used',
|
||||
tag: {
|
||||
line: 3,
|
||||
column: 4,
|
||||
file: 'example.rs',
|
||||
severity: 2,
|
||||
text: 'warning: function `f1` is never used',
|
||||
fixes: [],
|
||||
},
|
||||
},
|
||||
{
|
||||
text: ' --> <source>:3:4',
|
||||
tag: {
|
||||
line: 3,
|
||||
column: 4,
|
||||
file: 'example.rs',
|
||||
severity: 3,
|
||||
text: '',
|
||||
},
|
||||
},
|
||||
{text: ''},
|
||||
{
|
||||
text: 'warning: function `f2` is never used',
|
||||
tag: {
|
||||
line: 1,
|
||||
column: 4,
|
||||
file: 'm.rs',
|
||||
severity: 2,
|
||||
text: 'warning: function `f2` is never used',
|
||||
fixes: [],
|
||||
},
|
||||
},
|
||||
{
|
||||
text: ' --> /app/m.rs:1:4',
|
||||
tag: {
|
||||
line: 1,
|
||||
column: 4,
|
||||
file: 'm.rs',
|
||||
severity: 3,
|
||||
text: '',
|
||||
},
|
||||
},
|
||||
{text: ''},
|
||||
{text: 'warning: 2 warnings emitted'},
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Tool output', () => {
|
||||
|
||||
Reference in New Issue
Block a user