Skip to content
This repository was archived by the owner on Dec 29, 2022. It is now read-only.

Commit c3ffe42

Browse files
committed
Handle macro error diagnostics
1 parent ca84c50 commit c3ffe42

File tree

2 files changed

+182
-24
lines changed

2 files changed

+182
-24
lines changed

src/actions/diagnostics.rs

Lines changed: 77 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -136,7 +136,13 @@ pub fn parse_diagnostics(
136136
"rustc"
137137
};
138138

139-
let rls_span = span.rls_span().zero_indexed();
139+
let rls_span = if span.file_name.ends_with(" macros>") && span.expansion.is_some() {
140+
// span points to a macro, use a more useful source location
141+
&span.expansion.as_ref().unwrap().span
142+
} else {
143+
span
144+
}.rls_span().zero_indexed();
145+
140146
let file_path = cwd.join(&rls_span.file);
141147

142148
let diagnostic = Diagnostic {
@@ -335,6 +341,7 @@ impl IsWithin for Range {
335341
#[cfg(test)]
336342
mod diagnostic_message_test {
337343
use super::*;
344+
use ls_types::Position;
338345

339346
pub(super) fn parse_compiler_message(
340347
compiler_message: &str,
@@ -649,14 +656,49 @@ help: consider borrowing here: `&string`"#,
649656

650657
assert!(messages[0].1.is_empty(), "{:?}", messages[0].1);
651658
}
659+
660+
/// ```
661+
/// fn main() {
662+
/// let mut out = String::new();
663+
/// write!(out, "{}", 123);
664+
/// }
665+
/// ```
666+
#[test]
667+
fn macro_error_no_trait() {
668+
let diag = parse_compiler_message(
669+
include_str!("../../test_data/compiler_message/macro-error-no-trait.json"),
670+
true,
671+
);
672+
assert_eq!(diag.diagnostics.len(), 1, "{:#?}", diag.diagnostics);
673+
674+
let file = &diag.diagnostics.keys().nth(0).unwrap();
675+
assert!(file.to_str().unwrap().ends_with("src/main.rs"), "Unexpected file {:?}", file);
676+
677+
let diagnostic = &diag.diagnostics.values().nth(0).unwrap()[0];
678+
assert_eq!(diagnostic.0.source, Some("rustc".into()));
679+
assert_eq!(diagnostic.0.range, Range {
680+
start: Position::new(2, 4),
681+
end: Position::new(2, 27),
682+
});
683+
684+
let messages = diag.to_messages();
685+
assert_eq!(
686+
messages[0].0,
687+
"no method named `write_fmt` found for type `std::string::String` \
688+
in the current scope\n\n\
689+
help: items from traits can only be used if the trait is in scope"
690+
);
691+
692+
assert!(messages[0].1.is_empty(), "{:?}", messages[0].1);
693+
}
652694
}
653695

654696
/// Tests for creating suggestions from the compilers json output
655697
#[cfg(test)]
656698
mod diagnostic_suggestion_test {
657699
use self::diagnostic_message_test::*;
658700
use super::*;
659-
use ls_types;
701+
use ls_types::Position;
660702

661703
#[test]
662704
fn suggest_use_when_cannot_find_type() {
@@ -680,15 +722,11 @@ mod diagnostic_suggestion_test {
680722
"Line 15: Add `use std::collections::HashSet;\n`"
681723
);
682724

683-
let expected_position = ls_types::Position {
684-
line: 14,
685-
character: 0,
686-
};
687725
assert_eq!(
688726
use_hash_set.range,
689727
Range {
690-
start: expected_position,
691-
end: expected_position,
728+
start: Position::new(14, 0),
729+
end: Position::new(14, 0),
692730
}
693731
);
694732
}
@@ -715,14 +753,8 @@ mod diagnostic_suggestion_test {
715753
assert_eq!(
716754
change_to_mut.range,
717755
Range {
718-
start: ls_types::Position {
719-
line: 132,
720-
character: 12,
721-
},
722-
end: ls_types::Position {
723-
line: 132,
724-
character: 18,
725-
},
756+
start: Position::new(132, 12),
757+
end: Position::new(132, 18),
726758
}
727759
);
728760
}
@@ -752,14 +784,35 @@ mod diagnostic_suggestion_test {
752784
assert_eq!(
753785
change_to_mut.range,
754786
Range {
755-
start: ls_types::Position {
756-
line: 354,
757-
character: 34,
758-
},
759-
end: ls_types::Position {
760-
line: 354,
761-
character: 46,
762-
},
787+
start: Position::new(354, 34),
788+
end: Position::new(354, 46),
789+
}
790+
);
791+
}
792+
793+
#[test]
794+
fn suggest_macro_error_no_trait() {
795+
let diag = parse_compiler_message(
796+
include_str!("../../test_data/compiler_message/macro-error-no-trait.json"),
797+
true,
798+
);
799+
let diagnostics = diag.diagnostics.values().nth(0).unwrap();
800+
801+
eprintln!("{:#?}", diagnostics);
802+
803+
let change_to_mut = diagnostics
804+
.iter()
805+
.flat_map(|(_, suggestions)| suggestions)
806+
.find(|s| s.new_text == "use std::fmt::Write;\n\n")
807+
.expect("`use std::fmt::Write;` not found");
808+
809+
assert_eq!(change_to_mut.label, "Line 1: Add `use std::fmt::Write;\n\n`");
810+
811+
assert_eq!(
812+
change_to_mut.range,
813+
Range {
814+
start: Position::new(0, 0),
815+
end: Position::new(0, 0),
763816
}
764817
);
765818
}
Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
{
2+
"children": [{
3+
"children": [],
4+
"code": null,
5+
"level": "help",
6+
"message": "items from traits can only be used if the trait is in scope",
7+
"rendered": null,
8+
"spans": []
9+
}, {
10+
"children": [],
11+
"code": null,
12+
"level": "help",
13+
"message": "the following trait is implemented but not in scope, perhaps add a `use` for it:",
14+
"rendered": null,
15+
"spans": [{
16+
"byte_end": 0,
17+
"byte_start": 0,
18+
"column_end": 1,
19+
"column_start": 1,
20+
"expansion": null,
21+
"file_name": "src/main.rs",
22+
"is_primary": true,
23+
"label": null,
24+
"line_end": 1,
25+
"line_start": 1,
26+
"suggested_replacement": "use std::fmt::Write;\n\n",
27+
"suggestion_applicability": "Unspecified",
28+
"text": [{
29+
"highlight_end": 1,
30+
"highlight_start": 1,
31+
"text": "fn main() {"
32+
}]
33+
}]
34+
}],
35+
"code": {
36+
"code": "E0599",
37+
"explanation": "\nThis error occurs when a method is used on a type which doesn't implement it:\n\nErroneous code example:\n\n```compile_fail,E0599\nstruct Mouth;\n\nlet x = Mouth;\nx.chocolate(); // error: no method named `chocolate` found for type `Mouth`\n // in the current scope\n```\n"
38+
},
39+
"level": "error",
40+
"message": "no method named `write_fmt` found for type `std::string::String` in the current scope",
41+
"rendered": "error[E0599]: no method named `write_fmt` found for type `std::string::String` in the current scope\n --> src/main.rs:3:5\n |\n3 | write!(out, \"{}\", 123);\n | ^^^^^^^^^^^^^^^^^^^^^^^\n |\n = help: items from traits can only be used if the trait is in scope\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)\nhelp: the following trait is implemented but not in scope, perhaps add a `use` for it:\n |\n1 | use std::fmt::Write;\n |\n\n",
42+
"spans": [{
43+
"byte_end": 60,
44+
"byte_start": 51,
45+
"column_end": 18,
46+
"column_start": 9,
47+
"expansion": {
48+
"def_site_span": {
49+
"byte_end": 98,
50+
"byte_start": 0,
51+
"column_end": 56,
52+
"column_start": 1,
53+
"expansion": null,
54+
"file_name": "<write macros>",
55+
"is_primary": false,
56+
"label": null,
57+
"line_end": 2,
58+
"line_start": 1,
59+
"suggested_replacement": null,
60+
"suggestion_applicability": null,
61+
"text": [{
62+
"highlight_end": 43,
63+
"highlight_start": 1,
64+
"text": "( $ dst : expr , $ ( $ arg : tt ) * ) => ("
65+
}, {
66+
"highlight_end": 56,
67+
"highlight_start": 1,
68+
"text": "$ dst . write_fmt ( format_args ! ( $ ( $ arg ) * ) ) )"
69+
}]
70+
},
71+
"macro_decl_name": "write!",
72+
"span": {
73+
"byte_end": 72,
74+
"byte_start": 49,
75+
"column_end": 28,
76+
"column_start": 5,
77+
"expansion": null,
78+
"file_name": "src/main.rs",
79+
"is_primary": false,
80+
"label": null,
81+
"line_end": 3,
82+
"line_start": 3,
83+
"suggested_replacement": null,
84+
"suggestion_applicability": null,
85+
"text": [{
86+
"highlight_end": 28,
87+
"highlight_start": 5,
88+
"text": " write!(out, \"{}\", 123);"
89+
}]
90+
}
91+
},
92+
"file_name": "<write macros>",
93+
"is_primary": true,
94+
"label": null,
95+
"line_end": 2,
96+
"line_start": 2,
97+
"suggested_replacement": null,
98+
"suggestion_applicability": null,
99+
"text": [{
100+
"highlight_end": 18,
101+
"highlight_start": 9,
102+
"text": "$ dst . write_fmt ( format_args ! ( $ ( $ arg ) * ) ) )"
103+
}]
104+
}]
105+
}

0 commit comments

Comments
 (0)