Ryan Cumming 8f726b7db6 Include primary span label in VS Code diagnostics
In most cases the primary label span repeats information found elsewhere
in the diagnostic. For example, with E0061:

```
{
  "message": "this function takes 2 parameters but 3 parameters were supplied",
  "spans": [{"label": "expected 2 parameters"}]
}
```

However, with some mismatched type errors (E0308) the expected type only
appears in the primary span's label, e.g.:

```
{
  "message": "mismatched types",
  "spans": [{"label": "expected usize, found u32"}]
}
```

I initially added the primary span label to the message unconditionally.
However, for most error types the child diagnostics repeat the primary
span label with more detail. `rustc` also renders the duplicate text but
because the span label and child diagnostics appear in visually distinct
places it's not as confusing.

This takes a heuristic approach where it will only add the primary span
label if there are no child message lines.
2019-06-30 11:12:56 +10:00

201 lines
6.9 KiB
TypeScript

import * as assert from 'assert';
import * as fs from 'fs';
import * as vscode from 'vscode';
import {
MappedRustDiagnostic,
mapRustDiagnosticToVsCode,
RustDiagnostic,
SuggestionApplicability
} from '../../../utils/diagnostics/rust';
function loadDiagnosticFixture(name: string): RustDiagnostic {
const jsonText = fs
.readFileSync(
// We're actually in our JavaScript output directory, climb out
`${__dirname}/../../../../src/test/fixtures/rust-diagnostics/${name}.json`
)
.toString();
return JSON.parse(jsonText);
}
function mapFixtureToVsCode(name: string): MappedRustDiagnostic {
const rd = loadDiagnosticFixture(name);
const mapResult = mapRustDiagnosticToVsCode(rd);
if (!mapResult) {
return assert.fail('Mapping unexpectedly failed');
}
return mapResult;
}
describe('mapRustDiagnosticToVsCode', () => {
it('should map an incompatible type for trait error', () => {
const { diagnostic, suggestedFixes } = mapFixtureToVsCode(
'error/E0053'
);
assert.strictEqual(
diagnostic.severity,
vscode.DiagnosticSeverity.Error
);
assert.strictEqual(diagnostic.source, 'rustc');
assert.strictEqual(
diagnostic.message,
[
`method \`next\` has an incompatible type for trait`,
`expected type \`fn(&mut ty::list_iter::ListIterator<'list, M>) -> std::option::Option<&ty::Ref<M>>\``,
` found type \`fn(&ty::list_iter::ListIterator<'list, M>) -> std::option::Option<&'list ty::Ref<M>>\``
].join('\n')
);
assert.strictEqual(diagnostic.code, 'E0053');
assert.strictEqual(diagnostic.tags, undefined);
// No related information
assert.deepStrictEqual(diagnostic.relatedInformation, []);
// There are no suggested fixes
assert.strictEqual(suggestedFixes.length, 0);
});
it('should map an unused variable warning', () => {
const { diagnostic, suggestedFixes } = mapFixtureToVsCode(
'warning/unused_variables'
);
assert.strictEqual(
diagnostic.severity,
vscode.DiagnosticSeverity.Warning
);
assert.strictEqual(
diagnostic.message,
[
'unused variable: `foo`',
'#[warn(unused_variables)] on by default'
].join('\n')
);
assert.strictEqual(diagnostic.code, 'unused_variables');
assert.strictEqual(diagnostic.source, 'rustc');
assert.deepStrictEqual(diagnostic.tags, [
vscode.DiagnosticTag.Unnecessary
]);
// No related information
assert.deepStrictEqual(diagnostic.relatedInformation, []);
// One suggested fix available to prefix the variable
assert.strictEqual(suggestedFixes.length, 1);
const [suggestedFix] = suggestedFixes;
assert.strictEqual(
suggestedFix.title,
'consider prefixing with an underscore: `_foo`'
);
assert.strictEqual(
suggestedFix.applicability,
SuggestionApplicability.MachineApplicable
);
});
it('should map a wrong number of parameters error', () => {
const { diagnostic, suggestedFixes } = mapFixtureToVsCode(
'error/E0061'
);
assert.strictEqual(
diagnostic.severity,
vscode.DiagnosticSeverity.Error
);
assert.strictEqual(
diagnostic.message,
[
'this function takes 2 parameters but 3 parameters were supplied',
'expected 2 parameters'
].join('\n')
);
assert.strictEqual(diagnostic.code, 'E0061');
assert.strictEqual(diagnostic.source, 'rustc');
assert.strictEqual(diagnostic.tags, undefined);
// One related information for the original definition
const relatedInformation = diagnostic.relatedInformation;
if (!relatedInformation) {
return assert.fail('Related information unexpectedly undefined');
}
assert.strictEqual(relatedInformation.length, 1);
const [related] = relatedInformation;
assert.strictEqual(related.message, 'defined here');
// There are no suggested fixes
assert.strictEqual(suggestedFixes.length, 0);
});
it('should map a Clippy copy pass by ref warning', () => {
const { diagnostic, suggestedFixes } = mapFixtureToVsCode(
'clippy/trivially_copy_pass_by_ref'
);
assert.strictEqual(
diagnostic.severity,
vscode.DiagnosticSeverity.Warning
);
assert.strictEqual(diagnostic.source, 'clippy');
assert.strictEqual(
diagnostic.message,
[
'this argument is passed by reference, but would be more efficient if passed by value',
'#[warn(clippy::trivially_copy_pass_by_ref)] implied by #[warn(clippy::all)]',
'for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#trivially_copy_pass_by_ref'
].join('\n')
);
assert.strictEqual(diagnostic.code, 'trivially_copy_pass_by_ref');
assert.strictEqual(diagnostic.tags, undefined);
// One related information for the lint definition
const relatedInformation = diagnostic.relatedInformation;
if (!relatedInformation) {
return assert.fail('Related information unexpectedly undefined');
}
assert.strictEqual(relatedInformation.length, 1);
const [related] = relatedInformation;
assert.strictEqual(related.message, 'lint level defined here');
// One suggested fix to pass by value
assert.strictEqual(suggestedFixes.length, 1);
const [suggestedFix] = suggestedFixes;
assert.strictEqual(
suggestedFix.title,
'consider passing by value instead: `self`'
);
// Clippy does not mark this with any applicability
assert.strictEqual(
suggestedFix.applicability,
SuggestionApplicability.Unspecified
);
});
it('should map a mismatched type error', () => {
const { diagnostic, suggestedFixes } = mapFixtureToVsCode(
'error/E0308'
);
assert.strictEqual(
diagnostic.severity,
vscode.DiagnosticSeverity.Error
);
assert.strictEqual(
diagnostic.message,
['mismatched types', 'expected usize, found u32'].join('\n')
);
assert.strictEqual(diagnostic.code, 'E0308');
assert.strictEqual(diagnostic.source, 'rustc');
assert.strictEqual(diagnostic.tags, undefined);
// No related information
assert.deepStrictEqual(diagnostic.relatedInformation, []);
// There are no suggested fixes
assert.strictEqual(suggestedFixes.length, 0);
});
});