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) {
|
export function parseRustOutput(lines: string, inputFilename?: string, pathPrefix?: string) {
|
||||||
|
const inputBasename = inputFilename ? path.basename(inputFilename) : undefined;
|
||||||
const quickfixes: {re: RegExp; makeFix: (match: string[]) => Fix}[] = [
|
const quickfixes: {re: RegExp; makeFix: (match: string[]) => Fix}[] = [
|
||||||
{
|
{
|
||||||
re: / *help: add `#!\[feature\((.*?)\)]`/,
|
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[] = [];
|
const result: ResultLine[] = [];
|
||||||
let currentDiagnostic: ResultLine | undefined;
|
let currentDiagnostic: ResultLine | undefined;
|
||||||
eachLine(lines, line => {
|
eachLine(lines, line => {
|
||||||
@@ -298,14 +299,17 @@ export function parseRustOutput(lines: string, inputFilename?: string, pathPrefi
|
|||||||
const filteredLine = filterEscapeSequences(line);
|
const filteredLine = filterEscapeSequences(line);
|
||||||
const match = filteredLine.match(re);
|
const match = filteredLine.match(re);
|
||||||
|
|
||||||
if (match) {
|
if (match?.groups) {
|
||||||
const line = Number.parseInt(match[1], 10);
|
const file =
|
||||||
const column = Number.parseInt(match[3] || '0', 10);
|
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();
|
currentDiagnostic = result.pop();
|
||||||
if (currentDiagnostic !== undefined) {
|
if (currentDiagnostic !== undefined) {
|
||||||
const text = filterEscapeSequences(currentDiagnostic.text);
|
const text = filterEscapeSequences(currentDiagnostic.text);
|
||||||
currentDiagnostic.tag = {
|
currentDiagnostic.tag = {
|
||||||
|
file,
|
||||||
line,
|
line,
|
||||||
column,
|
column,
|
||||||
text,
|
text,
|
||||||
@@ -316,6 +320,7 @@ export function parseRustOutput(lines: string, inputFilename?: string, pathPrefi
|
|||||||
}
|
}
|
||||||
|
|
||||||
lineObj.tag = {
|
lineObj.tag = {
|
||||||
|
file,
|
||||||
line,
|
line,
|
||||||
column,
|
column,
|
||||||
text: '', // Left empty so that it does not show up in the editor
|
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', () => {
|
describe('Rust compiler output', () => {
|
||||||
it('handles simple cases', () => {
|
it('handles simple cases', () => {
|
||||||
expect(utils.parseRustOutput('Line one\nLine two', 'bob.rs')).toEqual([{text: 'Line one'}, {text: 'Line two'}]);
|
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'},
|
{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',
|
text: 'Line one',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
tag: {column: 0, line: 1, text: '', severity: 3},
|
tag: {file: 'bob.rs', column: 0, line: 1, text: '', severity: 3},
|
||||||
text: ' --> <source>:1',
|
text: ' --> <source>:1:0',
|
||||||
},
|
},
|
||||||
{text: 'Unrelated'},
|
{text: 'Unrelated'},
|
||||||
]);
|
]);
|
||||||
expect(utils.parseRustOutput('Line one\n --> bob.rs:1:5', 'bob.rs')).toEqual([
|
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',
|
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',
|
text: ' --> <source>:1:5',
|
||||||
},
|
},
|
||||||
]);
|
]);
|
||||||
expect(utils.parseRustOutput('Multiple spaces\n --> bob.rs:1:5', 'bob.rs')).toEqual([
|
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',
|
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',
|
text: ' --> <source>:1:5',
|
||||||
},
|
},
|
||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('replaces all references to input source', () => {
|
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>',
|
text: 'error: Error in <source>',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
tag: {column: 0, line: 1, text: '', severity: 3},
|
tag: {file: 'bob.rs', column: 42, line: 1, text: '', severity: 3},
|
||||||
text: ' --> <source>:1',
|
text: ' --> <source>:1:42',
|
||||||
},
|
},
|
||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
@@ -297,11 +297,11 @@ describe('Rust compiler output', () => {
|
|||||||
it('treats <stdin> as if it were the compiler source', () => {
|
it('treats <stdin> as if it were the compiler source', () => {
|
||||||
expect(utils.parseRustOutput('error: <stdin> is sad\n --> <stdin>:120:25', 'bob.rs')).toEqual([
|
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',
|
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',
|
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'},
|
{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', () => {
|
describe('Tool output', () => {
|
||||||
|
|||||||
Reference in New Issue
Block a user