mirror of
https://github.com/rust-lang/rust-analyzer.git
synced 2025-10-01 11:31:15 +00:00

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.
201 lines
6.9 KiB
TypeScript
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);
|
|
});
|
|
});
|