Skip to content

Commit 46ca40c

Browse files
Merge #2575
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>
2 parents ee93fac + bb9c60d commit 46ca40c

File tree

3 files changed

+333
-0
lines changed

3 files changed

+333
-0
lines changed
Lines changed: 261 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,261 @@
1+
{
2+
"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",
3+
"children": [
4+
{
5+
"children": [],
6+
"code": null,
7+
"level": "help",
8+
"message": "the trait `std::cmp::PartialEq<&str>` is not implemented for `{integer}`",
9+
"rendered": null,
10+
"spans": []
11+
}
12+
],
13+
"code": {
14+
"code": "E0277",
15+
"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"
16+
},
17+
"level": "error",
18+
"message": "can't compare `{integer}` with `&str`",
19+
"spans": [
20+
{
21+
"byte_end": 155,
22+
"byte_start": 153,
23+
"column_end": 33,
24+
"column_start": 31,
25+
"expansion": {
26+
"def_site_span": {
27+
"byte_end": 940,
28+
"byte_start": 0,
29+
"column_end": 6,
30+
"column_start": 1,
31+
"expansion": null,
32+
"file_name": "<::core::macros::assert_eq macros>",
33+
"is_primary": false,
34+
"label": null,
35+
"line_end": 36,
36+
"line_start": 1,
37+
"suggested_replacement": null,
38+
"suggestion_applicability": null,
39+
"text": [
40+
{
41+
"highlight_end": 35,
42+
"highlight_start": 1,
43+
"text": "($ left : expr, $ right : expr) =>"
44+
},
45+
{
46+
"highlight_end": 3,
47+
"highlight_start": 1,
48+
"text": "({"
49+
},
50+
{
51+
"highlight_end": 33,
52+
"highlight_start": 1,
53+
"text": " match (& $ left, & $ right)"
54+
},
55+
{
56+
"highlight_end": 7,
57+
"highlight_start": 1,
58+
"text": " {"
59+
},
60+
{
61+
"highlight_end": 34,
62+
"highlight_start": 1,
63+
"text": " (left_val, right_val) =>"
64+
},
65+
{
66+
"highlight_end": 11,
67+
"highlight_start": 1,
68+
"text": " {"
69+
},
70+
{
71+
"highlight_end": 46,
72+
"highlight_start": 1,
73+
"text": " if ! (* left_val == * right_val)"
74+
},
75+
{
76+
"highlight_end": 15,
77+
"highlight_start": 1,
78+
"text": " {"
79+
},
80+
{
81+
"highlight_end": 25,
82+
"highlight_start": 1,
83+
"text": " panic !"
84+
},
85+
{
86+
"highlight_end": 57,
87+
"highlight_start": 1,
88+
"text": " (r#\"assertion failed: `(left == right)`"
89+
},
90+
{
91+
"highlight_end": 16,
92+
"highlight_start": 1,
93+
"text": " left: `{:?}`,"
94+
},
95+
{
96+
"highlight_end": 18,
97+
"highlight_start": 1,
98+
"text": " right: `{:?}`\"#,"
99+
},
100+
{
101+
"highlight_end": 47,
102+
"highlight_start": 1,
103+
"text": " & * left_val, & * right_val)"
104+
},
105+
{
106+
"highlight_end": 15,
107+
"highlight_start": 1,
108+
"text": " }"
109+
},
110+
{
111+
"highlight_end": 11,
112+
"highlight_start": 1,
113+
"text": " }"
114+
},
115+
{
116+
"highlight_end": 7,
117+
"highlight_start": 1,
118+
"text": " }"
119+
},
120+
{
121+
"highlight_end": 42,
122+
"highlight_start": 1,
123+
"text": " }) ; ($ left : expr, $ right : expr,) =>"
124+
},
125+
{
126+
"highlight_end": 49,
127+
"highlight_start": 1,
128+
"text": "({ $ crate :: assert_eq ! ($ left, $ right) }) ;"
129+
},
130+
{
131+
"highlight_end": 53,
132+
"highlight_start": 1,
133+
"text": "($ left : expr, $ right : expr, $ ($ arg : tt) +) =>"
134+
},
135+
{
136+
"highlight_end": 3,
137+
"highlight_start": 1,
138+
"text": "({"
139+
},
140+
{
141+
"highlight_end": 37,
142+
"highlight_start": 1,
143+
"text": " match (& ($ left), & ($ right))"
144+
},
145+
{
146+
"highlight_end": 7,
147+
"highlight_start": 1,
148+
"text": " {"
149+
},
150+
{
151+
"highlight_end": 34,
152+
"highlight_start": 1,
153+
"text": " (left_val, right_val) =>"
154+
},
155+
{
156+
"highlight_end": 11,
157+
"highlight_start": 1,
158+
"text": " {"
159+
},
160+
{
161+
"highlight_end": 46,
162+
"highlight_start": 1,
163+
"text": " if ! (* left_val == * right_val)"
164+
},
165+
{
166+
"highlight_end": 15,
167+
"highlight_start": 1,
168+
"text": " {"
169+
},
170+
{
171+
"highlight_end": 25,
172+
"highlight_start": 1,
173+
"text": " panic !"
174+
},
175+
{
176+
"highlight_end": 57,
177+
"highlight_start": 1,
178+
"text": " (r#\"assertion failed: `(left == right)`"
179+
},
180+
{
181+
"highlight_end": 16,
182+
"highlight_start": 1,
183+
"text": " left: `{:?}`,"
184+
},
185+
{
186+
"highlight_end": 22,
187+
"highlight_start": 1,
188+
"text": " right: `{:?}`: {}\"#,"
189+
},
190+
{
191+
"highlight_end": 72,
192+
"highlight_start": 1,
193+
"text": " & * left_val, & * right_val, $ crate :: format_args !"
194+
},
195+
{
196+
"highlight_end": 33,
197+
"highlight_start": 1,
198+
"text": " ($ ($ arg) +))"
199+
},
200+
{
201+
"highlight_end": 15,
202+
"highlight_start": 1,
203+
"text": " }"
204+
},
205+
{
206+
"highlight_end": 11,
207+
"highlight_start": 1,
208+
"text": " }"
209+
},
210+
{
211+
"highlight_end": 7,
212+
"highlight_start": 1,
213+
"text": " }"
214+
},
215+
{
216+
"highlight_end": 6,
217+
"highlight_start": 1,
218+
"text": " }) ;"
219+
}
220+
]
221+
},
222+
"macro_decl_name": "assert_eq!",
223+
"span": {
224+
"byte_end": 38,
225+
"byte_start": 16,
226+
"column_end": 27,
227+
"column_start": 5,
228+
"expansion": null,
229+
"file_name": "src/main.rs",
230+
"is_primary": false,
231+
"label": null,
232+
"line_end": 2,
233+
"line_start": 2,
234+
"suggested_replacement": null,
235+
"suggestion_applicability": null,
236+
"text": [
237+
{
238+
"highlight_end": 27,
239+
"highlight_start": 5,
240+
"text": " assert_eq!(1, \"love\");"
241+
}
242+
]
243+
}
244+
},
245+
"file_name": "<::core::macros::assert_eq macros>",
246+
"is_primary": true,
247+
"label": "no implementation for `{integer} == &str`",
248+
"line_end": 7,
249+
"line_start": 7,
250+
"suggested_replacement": null,
251+
"suggestion_applicability": null,
252+
"text": [
253+
{
254+
"highlight_end": 33,
255+
"highlight_start": 31,
256+
"text": " if ! (* left_val == * right_val)"
257+
}
258+
]
259+
}
260+
]
261+
}

editors/code/src/test/utils/diagnotics/rust.test.ts

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -199,4 +199,38 @@ describe('mapRustDiagnosticToVsCode', () => {
199199
// There are no suggested fixes
200200
assert.strictEqual(suggestedFixes.length, 0);
201201
});
202+
203+
it('should map a macro invocation location to normal file path', () => {
204+
const { location, diagnostic, suggestedFixes } = mapFixtureToVsCode(
205+
'error/E0277',
206+
);
207+
208+
assert.strictEqual(
209+
diagnostic.severity,
210+
vscode.DiagnosticSeverity.Error,
211+
);
212+
assert.strictEqual(
213+
diagnostic.message,
214+
[
215+
"can't compare `{integer}` with `&str`",
216+
'the trait `std::cmp::PartialEq<&str>` is not implemented for `{integer}`',
217+
].join('\n'),
218+
);
219+
assert.strictEqual(diagnostic.code, 'E0277');
220+
assert.strictEqual(diagnostic.source, 'rustc');
221+
assert.deepStrictEqual(diagnostic.tags, []);
222+
223+
// No related information
224+
assert.deepStrictEqual(diagnostic.relatedInformation, []);
225+
226+
// There are no suggested fixes
227+
assert.strictEqual(suggestedFixes.length, 0);
228+
229+
// The file url should be normal file
230+
// Ignore the first part because it depends on vs workspace location
231+
assert.strictEqual(
232+
location.uri.path.substr(-'src/main.rs'.length),
233+
'src/main.rs',
234+
);
235+
});
202236
});

editors/code/src/utils/diagnostics/rust.ts

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,12 @@ export enum SuggestionApplicability {
1010
Unspecified = 'Unspecified',
1111
}
1212

13+
export interface RustDiagnosticSpanMacroExpansion {
14+
span: RustDiagnosticSpan;
15+
macro_decl_name: string;
16+
def_site_span?: RustDiagnosticSpan;
17+
}
18+
1319
// Reference:
1420
// https://github.com/rust-lang/rust/blob/master/src/libsyntax/json.rs
1521
export interface RustDiagnosticSpan {
@@ -20,6 +26,7 @@ export interface RustDiagnosticSpan {
2026
is_primary: boolean;
2127
file_name: string;
2228
label?: string;
29+
expansion?: RustDiagnosticSpanMacroExpansion;
2330
suggested_replacement?: string;
2431
suggestion_applicability?: SuggestionApplicability;
2532
}
@@ -60,10 +67,41 @@ function mapLevelToSeverity(s: string): vscode.DiagnosticSeverity {
6067
return vscode.DiagnosticSeverity.Information;
6168
}
6269

70+
/**
71+
* Check whether a file name is from macro invocation
72+
*/
73+
function isFromMacro(fileName: string): boolean {
74+
return fileName.startsWith('<') && fileName.endsWith('>');
75+
}
76+
77+
/**
78+
* Converts a Rust macro span to a VsCode location recursively
79+
*/
80+
function mapMacroSpanToLocation(
81+
spanMacro: RustDiagnosticSpanMacroExpansion,
82+
): vscode.Location | undefined {
83+
if (!isFromMacro(spanMacro.span.file_name)) {
84+
return mapSpanToLocation(spanMacro.span);
85+
}
86+
87+
if (spanMacro.span.expansion) {
88+
return mapMacroSpanToLocation(spanMacro.span.expansion);
89+
}
90+
91+
return;
92+
}
93+
6394
/**
6495
* Converts a Rust span to a VsCode location
6596
*/
6697
function mapSpanToLocation(span: RustDiagnosticSpan): vscode.Location {
98+
if (isFromMacro(span.file_name) && span.expansion) {
99+
const macroLoc = mapMacroSpanToLocation(span.expansion);
100+
if (macroLoc) {
101+
return macroLoc;
102+
}
103+
}
104+
67105
const fileName = path.join(vscode.workspace.rootPath || '', span.file_name);
68106
const fileUri = vscode.Uri.file(fileName);
69107

0 commit comments

Comments
 (0)