2575: [VS Code Extension] Remap error location if it extracted inside macros r=edwin0cheng a=edwin0cheng

It should fix for #2569






Co-authored-by: Edwin Cheng <edwin0cheng@gmail.com>
This commit is contained in:
bors[bot] 2019-12-18 03:03:51 +00:00 committed by GitHub
commit 46ca40ccfc
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 333 additions and 0 deletions

View file

@ -0,0 +1,261 @@
{
"rendered": "error[E0277]: can't compare `{integer}` with `&str`\n --> src/main.rs:2:5\n |\n2 | assert_eq!(1, \"love\");\n | ^^^^^^^^^^^^^^^^^^^^^^ no implementation for `{integer} == &str`\n |\n = help: the trait `std::cmp::PartialEq<&str>` is not implemented for `{integer}`\n = note: this error originates in a macro outside of the current crate (in Nightly builds, run with -Z external-macro-backtrace for more info)\n\n",
"children": [
{
"children": [],
"code": null,
"level": "help",
"message": "the trait `std::cmp::PartialEq<&str>` is not implemented for `{integer}`",
"rendered": null,
"spans": []
}
],
"code": {
"code": "E0277",
"explanation": "\nYou tried to use a type which doesn't implement some trait in a place which\nexpected that trait. Erroneous code example:\n\n```compile_fail,E0277\n// here we declare the Foo trait with a bar method\ntrait Foo {\n fn bar(&self);\n}\n\n// we now declare a function which takes an object implementing the Foo trait\nfn some_func<T: Foo>(foo: T) {\n foo.bar();\n}\n\nfn main() {\n // we now call the method with the i32 type, which doesn't implement\n // the Foo trait\n some_func(5i32); // error: the trait bound `i32 : Foo` is not satisfied\n}\n```\n\nIn order to fix this error, verify that the type you're using does implement\nthe trait. Example:\n\n```\ntrait Foo {\n fn bar(&self);\n}\n\nfn some_func<T: Foo>(foo: T) {\n foo.bar(); // we can now use this method since i32 implements the\n // Foo trait\n}\n\n// we implement the trait on the i32 type\nimpl Foo for i32 {\n fn bar(&self) {}\n}\n\nfn main() {\n some_func(5i32); // ok!\n}\n```\n\nOr in a generic context, an erroneous code example would look like:\n\n```compile_fail,E0277\nfn some_func<T>(foo: T) {\n println!(\"{:?}\", foo); // error: the trait `core::fmt::Debug` is not\n // implemented for the type `T`\n}\n\nfn main() {\n // We now call the method with the i32 type,\n // which *does* implement the Debug trait.\n some_func(5i32);\n}\n```\n\nNote that the error here is in the definition of the generic function: Although\nwe only call it with a parameter that does implement `Debug`, the compiler\nstill rejects the function: It must work with all possible input types. In\norder to make this example compile, we need to restrict the generic type we're\naccepting:\n\n```\nuse std::fmt;\n\n// Restrict the input type to types that implement Debug.\nfn some_func<T: fmt::Debug>(foo: T) {\n println!(\"{:?}\", foo);\n}\n\nfn main() {\n // Calling the method is still fine, as i32 implements Debug.\n some_func(5i32);\n\n // This would fail to compile now:\n // struct WithoutDebug;\n // some_func(WithoutDebug);\n}\n```\n\nRust only looks at the signature of the called function, as such it must\nalready specify all requirements that will be used for every type parameter.\n"
},
"level": "error",
"message": "can't compare `{integer}` with `&str`",
"spans": [
{
"byte_end": 155,
"byte_start": 153,
"column_end": 33,
"column_start": 31,
"expansion": {
"def_site_span": {
"byte_end": 940,
"byte_start": 0,
"column_end": 6,
"column_start": 1,
"expansion": null,
"file_name": "<::core::macros::assert_eq macros>",
"is_primary": false,
"label": null,
"line_end": 36,
"line_start": 1,
"suggested_replacement": null,
"suggestion_applicability": null,
"text": [
{
"highlight_end": 35,
"highlight_start": 1,
"text": "($ left : expr, $ right : expr) =>"
},
{
"highlight_end": 3,
"highlight_start": 1,
"text": "({"
},
{
"highlight_end": 33,
"highlight_start": 1,
"text": " match (& $ left, & $ right)"
},
{
"highlight_end": 7,
"highlight_start": 1,
"text": " {"
},
{
"highlight_end": 34,
"highlight_start": 1,
"text": " (left_val, right_val) =>"
},
{
"highlight_end": 11,
"highlight_start": 1,
"text": " {"
},
{
"highlight_end": 46,
"highlight_start": 1,
"text": " if ! (* left_val == * right_val)"
},
{
"highlight_end": 15,
"highlight_start": 1,
"text": " {"
},
{
"highlight_end": 25,
"highlight_start": 1,
"text": " panic !"
},
{
"highlight_end": 57,
"highlight_start": 1,
"text": " (r#\"assertion failed: `(left == right)`"
},
{
"highlight_end": 16,
"highlight_start": 1,
"text": " left: `{:?}`,"
},
{
"highlight_end": 18,
"highlight_start": 1,
"text": " right: `{:?}`\"#,"
},
{
"highlight_end": 47,
"highlight_start": 1,
"text": " & * left_val, & * right_val)"
},
{
"highlight_end": 15,
"highlight_start": 1,
"text": " }"
},
{
"highlight_end": 11,
"highlight_start": 1,
"text": " }"
},
{
"highlight_end": 7,
"highlight_start": 1,
"text": " }"
},
{
"highlight_end": 42,
"highlight_start": 1,
"text": " }) ; ($ left : expr, $ right : expr,) =>"
},
{
"highlight_end": 49,
"highlight_start": 1,
"text": "({ $ crate :: assert_eq ! ($ left, $ right) }) ;"
},
{
"highlight_end": 53,
"highlight_start": 1,
"text": "($ left : expr, $ right : expr, $ ($ arg : tt) +) =>"
},
{
"highlight_end": 3,
"highlight_start": 1,
"text": "({"
},
{
"highlight_end": 37,
"highlight_start": 1,
"text": " match (& ($ left), & ($ right))"
},
{
"highlight_end": 7,
"highlight_start": 1,
"text": " {"
},
{
"highlight_end": 34,
"highlight_start": 1,
"text": " (left_val, right_val) =>"
},
{
"highlight_end": 11,
"highlight_start": 1,
"text": " {"
},
{
"highlight_end": 46,
"highlight_start": 1,
"text": " if ! (* left_val == * right_val)"
},
{
"highlight_end": 15,
"highlight_start": 1,
"text": " {"
},
{
"highlight_end": 25,
"highlight_start": 1,
"text": " panic !"
},
{
"highlight_end": 57,
"highlight_start": 1,
"text": " (r#\"assertion failed: `(left == right)`"
},
{
"highlight_end": 16,
"highlight_start": 1,
"text": " left: `{:?}`,"
},
{
"highlight_end": 22,
"highlight_start": 1,
"text": " right: `{:?}`: {}\"#,"
},
{
"highlight_end": 72,
"highlight_start": 1,
"text": " & * left_val, & * right_val, $ crate :: format_args !"
},
{
"highlight_end": 33,
"highlight_start": 1,
"text": " ($ ($ arg) +))"
},
{
"highlight_end": 15,
"highlight_start": 1,
"text": " }"
},
{
"highlight_end": 11,
"highlight_start": 1,
"text": " }"
},
{
"highlight_end": 7,
"highlight_start": 1,
"text": " }"
},
{
"highlight_end": 6,
"highlight_start": 1,
"text": " }) ;"
}
]
},
"macro_decl_name": "assert_eq!",
"span": {
"byte_end": 38,
"byte_start": 16,
"column_end": 27,
"column_start": 5,
"expansion": null,
"file_name": "src/main.rs",
"is_primary": false,
"label": null,
"line_end": 2,
"line_start": 2,
"suggested_replacement": null,
"suggestion_applicability": null,
"text": [
{
"highlight_end": 27,
"highlight_start": 5,
"text": " assert_eq!(1, \"love\");"
}
]
}
},
"file_name": "<::core::macros::assert_eq macros>",
"is_primary": true,
"label": "no implementation for `{integer} == &str`",
"line_end": 7,
"line_start": 7,
"suggested_replacement": null,
"suggestion_applicability": null,
"text": [
{
"highlight_end": 33,
"highlight_start": 31,
"text": " if ! (* left_val == * right_val)"
}
]
}
]
}

View file

@ -199,4 +199,38 @@ describe('mapRustDiagnosticToVsCode', () => {
// There are no suggested fixes
assert.strictEqual(suggestedFixes.length, 0);
});
it('should map a macro invocation location to normal file path', () => {
const { location, diagnostic, suggestedFixes } = mapFixtureToVsCode(
'error/E0277',
);
assert.strictEqual(
diagnostic.severity,
vscode.DiagnosticSeverity.Error,
);
assert.strictEqual(
diagnostic.message,
[
"can't compare `{integer}` with `&str`",
'the trait `std::cmp::PartialEq<&str>` is not implemented for `{integer}`',
].join('\n'),
);
assert.strictEqual(diagnostic.code, 'E0277');
assert.strictEqual(diagnostic.source, 'rustc');
assert.deepStrictEqual(diagnostic.tags, []);
// No related information
assert.deepStrictEqual(diagnostic.relatedInformation, []);
// There are no suggested fixes
assert.strictEqual(suggestedFixes.length, 0);
// The file url should be normal file
// Ignore the first part because it depends on vs workspace location
assert.strictEqual(
location.uri.path.substr(-'src/main.rs'.length),
'src/main.rs',
);
});
});

View file

@ -10,6 +10,12 @@ export enum SuggestionApplicability {
Unspecified = 'Unspecified',
}
export interface RustDiagnosticSpanMacroExpansion {
span: RustDiagnosticSpan;
macro_decl_name: string;
def_site_span?: RustDiagnosticSpan;
}
// Reference:
// https://github.com/rust-lang/rust/blob/master/src/libsyntax/json.rs
export interface RustDiagnosticSpan {
@ -20,6 +26,7 @@ export interface RustDiagnosticSpan {
is_primary: boolean;
file_name: string;
label?: string;
expansion?: RustDiagnosticSpanMacroExpansion;
suggested_replacement?: string;
suggestion_applicability?: SuggestionApplicability;
}
@ -60,10 +67,41 @@ function mapLevelToSeverity(s: string): vscode.DiagnosticSeverity {
return vscode.DiagnosticSeverity.Information;
}
/**
* Check whether a file name is from macro invocation
*/
function isFromMacro(fileName: string): boolean {
return fileName.startsWith('<') && fileName.endsWith('>');
}
/**
* Converts a Rust macro span to a VsCode location recursively
*/
function mapMacroSpanToLocation(
spanMacro: RustDiagnosticSpanMacroExpansion,
): vscode.Location | undefined {
if (!isFromMacro(spanMacro.span.file_name)) {
return mapSpanToLocation(spanMacro.span);
}
if (spanMacro.span.expansion) {
return mapMacroSpanToLocation(spanMacro.span.expansion);
}
return;
}
/**
* Converts a Rust span to a VsCode location
*/
function mapSpanToLocation(span: RustDiagnosticSpan): vscode.Location {
if (isFromMacro(span.file_name) && span.expansion) {
const macroLoc = mapMacroSpanToLocation(span.expansion);
if (macroLoc) {
return macroLoc;
}
}
const fileName = path.join(vscode.workspace.rootPath || '', span.file_name);
const fileUri = vscode.Uri.file(fileName);