diff options
author | Emil Lauridsen <[email protected]> | 2020-03-12 14:01:53 +0000 |
---|---|---|
committer | Emil Lauridsen <[email protected]> | 2020-03-12 14:01:53 +0000 |
commit | 637c795b3c9a56795977ade0cedbc7a9fb7dc453 (patch) | |
tree | c88f77b45c2a5f24b31f1b7ed10956619438989f | |
parent | 05b4fc6d79060fc3120f92b01119e3a851c37829 (diff) |
Correctly handle multi-line fixes from cargo/clippy
5 files changed, 267 insertions, 30 deletions
diff --git a/crates/ra_cargo_watch/src/conv.rs b/crates/ra_cargo_watch/src/conv.rs index 0246adfb5..a3f05bede 100644 --- a/crates/ra_cargo_watch/src/conv.rs +++ b/crates/ra_cargo_watch/src/conv.rs | |||
@@ -8,6 +8,7 @@ use lsp_types::{ | |||
8 | Location, NumberOrString, Position, Range, TextEdit, Url, WorkspaceEdit, | 8 | Location, NumberOrString, Position, Range, TextEdit, Url, WorkspaceEdit, |
9 | }; | 9 | }; |
10 | use std::{ | 10 | use std::{ |
11 | collections::HashMap, | ||
11 | fmt::Write, | 12 | fmt::Write, |
12 | path::{Component, Path, PathBuf, Prefix}, | 13 | path::{Component, Path, PathBuf, Prefix}, |
13 | str::FromStr, | 14 | str::FromStr, |
@@ -126,44 +127,34 @@ fn map_rust_child_diagnostic( | |||
126 | rd: &RustDiagnostic, | 127 | rd: &RustDiagnostic, |
127 | workspace_root: &PathBuf, | 128 | workspace_root: &PathBuf, |
128 | ) -> MappedRustChildDiagnostic { | 129 | ) -> MappedRustChildDiagnostic { |
129 | let span: &DiagnosticSpan = match rd.spans.iter().find(|s| s.is_primary) { | 130 | let spans: Vec<&DiagnosticSpan> = rd.spans.iter().filter(|s| s.is_primary).collect(); |
130 | Some(span) => span, | 131 | if spans.is_empty() { |
131 | None => { | 132 | // `rustc` uses these spanless children as a way to print multi-line |
132 | // `rustc` uses these spanless children as a way to print multi-line | 133 | // messages |
133 | // messages | 134 | return MappedRustChildDiagnostic::MessageLine(rd.message.clone()); |
134 | return MappedRustChildDiagnostic::MessageLine(rd.message.clone()); | 135 | } |
135 | } | ||
136 | }; | ||
137 | |||
138 | // If we have a primary span use its location, otherwise use the parent | ||
139 | let location = map_span_to_location(&span, workspace_root); | ||
140 | |||
141 | if let Some(suggested_replacement) = &span.suggested_replacement { | ||
142 | // Include our replacement in the title unless it's empty | ||
143 | let title = if !suggested_replacement.is_empty() { | ||
144 | format!("{}: '{}'", rd.message, suggested_replacement) | ||
145 | } else { | ||
146 | rd.message.clone() | ||
147 | }; | ||
148 | 136 | ||
149 | let edit = { | 137 | let mut edit_map: HashMap<Url, Vec<TextEdit>> = HashMap::new(); |
150 | let edits = vec![TextEdit::new(location.range, suggested_replacement.clone())]; | 138 | for &span in &spans { |
151 | let mut edit_map = std::collections::HashMap::new(); | 139 | if let Some(suggested_replacement) = &span.suggested_replacement { |
152 | edit_map.insert(location.uri, edits); | 140 | let location = map_span_to_location(span, workspace_root); |
153 | WorkspaceEdit::new(edit_map) | 141 | let edit = TextEdit::new(location.range, suggested_replacement.clone()); |
154 | }; | 142 | edit_map.entry(location.uri).or_default().push(edit); |
143 | } | ||
144 | } | ||
155 | 145 | ||
146 | if !edit_map.is_empty() { | ||
156 | MappedRustChildDiagnostic::SuggestedFix(CodeAction { | 147 | MappedRustChildDiagnostic::SuggestedFix(CodeAction { |
157 | title, | 148 | title: rd.message.clone(), |
158 | kind: Some("quickfix".to_string()), | 149 | kind: Some("quickfix".to_string()), |
159 | diagnostics: None, | 150 | diagnostics: None, |
160 | edit: Some(edit), | 151 | edit: Some(WorkspaceEdit::new(edit_map)), |
161 | command: None, | 152 | command: None, |
162 | is_preferred: None, | 153 | is_preferred: None, |
163 | }) | 154 | }) |
164 | } else { | 155 | } else { |
165 | MappedRustChildDiagnostic::Related(DiagnosticRelatedInformation { | 156 | MappedRustChildDiagnostic::Related(DiagnosticRelatedInformation { |
166 | location, | 157 | location: map_span_to_location(spans[0], workspace_root), |
167 | message: rd.message.clone(), | 158 | message: rd.message.clone(), |
168 | }) | 159 | }) |
169 | } | 160 | } |
diff --git a/crates/ra_cargo_watch/src/conv/snapshots/ra_cargo_watch__conv__test__snap_clippy_pass_by_ref.snap b/crates/ra_cargo_watch/src/conv/snapshots/ra_cargo_watch__conv__test__snap_clippy_pass_by_ref.snap index 95ca163dc..47801ae79 100644 --- a/crates/ra_cargo_watch/src/conv/snapshots/ra_cargo_watch__conv__test__snap_clippy_pass_by_ref.snap +++ b/crates/ra_cargo_watch/src/conv/snapshots/ra_cargo_watch__conv__test__snap_clippy_pass_by_ref.snap | |||
@@ -63,7 +63,7 @@ MappedRustDiagnostic { | |||
63 | }, | 63 | }, |
64 | fixes: [ | 64 | fixes: [ |
65 | CodeAction { | 65 | CodeAction { |
66 | title: "consider passing by value instead: \'self\'", | 66 | title: "consider passing by value instead", |
67 | kind: Some( | 67 | kind: Some( |
68 | "quickfix", | 68 | "quickfix", |
69 | ), | 69 | ), |
diff --git a/crates/ra_cargo_watch/src/conv/snapshots/ra_cargo_watch__conv__test__snap_multi_line_fix.snap b/crates/ra_cargo_watch/src/conv/snapshots/ra_cargo_watch__conv__test__snap_multi_line_fix.snap new file mode 100644 index 000000000..23c4f5a2c --- /dev/null +++ b/crates/ra_cargo_watch/src/conv/snapshots/ra_cargo_watch__conv__test__snap_multi_line_fix.snap | |||
@@ -0,0 +1,112 @@ | |||
1 | --- | ||
2 | source: crates/ra_cargo_watch/src/conv/test.rs | ||
3 | expression: diag | ||
4 | --- | ||
5 | MappedRustDiagnostic { | ||
6 | location: Location { | ||
7 | uri: "file:///test/src/main.rs", | ||
8 | range: Range { | ||
9 | start: Position { | ||
10 | line: 3, | ||
11 | character: 4, | ||
12 | }, | ||
13 | end: Position { | ||
14 | line: 3, | ||
15 | character: 5, | ||
16 | }, | ||
17 | }, | ||
18 | }, | ||
19 | diagnostic: Diagnostic { | ||
20 | range: Range { | ||
21 | start: Position { | ||
22 | line: 3, | ||
23 | character: 4, | ||
24 | }, | ||
25 | end: Position { | ||
26 | line: 3, | ||
27 | character: 5, | ||
28 | }, | ||
29 | }, | ||
30 | severity: Some( | ||
31 | Warning, | ||
32 | ), | ||
33 | code: Some( | ||
34 | String( | ||
35 | "let_and_return", | ||
36 | ), | ||
37 | ), | ||
38 | source: Some( | ||
39 | "clippy", | ||
40 | ), | ||
41 | message: "returning the result of a let binding from a block\n`#[warn(clippy::let_and_return)]` on by default\nfor further information visit https://rust-lang.github.io/rust-clippy/master/index.html#let_and_return", | ||
42 | related_information: Some( | ||
43 | [ | ||
44 | DiagnosticRelatedInformation { | ||
45 | location: Location { | ||
46 | uri: "file:///test/src/main.rs", | ||
47 | range: Range { | ||
48 | start: Position { | ||
49 | line: 2, | ||
50 | character: 4, | ||
51 | }, | ||
52 | end: Position { | ||
53 | line: 2, | ||
54 | character: 30, | ||
55 | }, | ||
56 | }, | ||
57 | }, | ||
58 | message: "unnecessary let binding", | ||
59 | }, | ||
60 | ], | ||
61 | ), | ||
62 | tags: None, | ||
63 | }, | ||
64 | fixes: [ | ||
65 | CodeAction { | ||
66 | title: "return the expression directly", | ||
67 | kind: Some( | ||
68 | "quickfix", | ||
69 | ), | ||
70 | diagnostics: None, | ||
71 | edit: Some( | ||
72 | WorkspaceEdit { | ||
73 | changes: Some( | ||
74 | { | ||
75 | "file:///test/src/main.rs": [ | ||
76 | TextEdit { | ||
77 | range: Range { | ||
78 | start: Position { | ||
79 | line: 2, | ||
80 | character: 4, | ||
81 | }, | ||
82 | end: Position { | ||
83 | line: 2, | ||
84 | character: 30, | ||
85 | }, | ||
86 | }, | ||
87 | new_text: "", | ||
88 | }, | ||
89 | TextEdit { | ||
90 | range: Range { | ||
91 | start: Position { | ||
92 | line: 3, | ||
93 | character: 4, | ||
94 | }, | ||
95 | end: Position { | ||
96 | line: 3, | ||
97 | character: 5, | ||
98 | }, | ||
99 | }, | ||
100 | new_text: "(0..10).collect()", | ||
101 | }, | ||
102 | ], | ||
103 | }, | ||
104 | ), | ||
105 | document_changes: None, | ||
106 | }, | ||
107 | ), | ||
108 | command: None, | ||
109 | is_preferred: None, | ||
110 | }, | ||
111 | ], | ||
112 | } | ||
diff --git a/crates/ra_cargo_watch/src/conv/snapshots/ra_cargo_watch__conv__test__snap_rustc_unused_variable.snap b/crates/ra_cargo_watch/src/conv/snapshots/ra_cargo_watch__conv__test__snap_rustc_unused_variable.snap index 3e1fe736c..8bab09540 100644 --- a/crates/ra_cargo_watch/src/conv/snapshots/ra_cargo_watch__conv__test__snap_rustc_unused_variable.snap +++ b/crates/ra_cargo_watch/src/conv/snapshots/ra_cargo_watch__conv__test__snap_rustc_unused_variable.snap | |||
@@ -48,7 +48,7 @@ MappedRustDiagnostic { | |||
48 | }, | 48 | }, |
49 | fixes: [ | 49 | fixes: [ |
50 | CodeAction { | 50 | CodeAction { |
51 | title: "consider prefixing with an underscore: \'_foo\'", | 51 | title: "consider prefixing with an underscore", |
52 | kind: Some( | 52 | kind: Some( |
53 | "quickfix", | 53 | "quickfix", |
54 | ), | 54 | ), |
diff --git a/crates/ra_cargo_watch/src/conv/test.rs b/crates/ra_cargo_watch/src/conv/test.rs index 6b86525b8..c880dcdc3 100644 --- a/crates/ra_cargo_watch/src/conv/test.rs +++ b/crates/ra_cargo_watch/src/conv/test.rs | |||
@@ -936,3 +936,137 @@ fn snap_macro_compiler_error() { | |||
936 | let diag = map_rust_diagnostic_to_lsp(&diag, &workspace_root).expect("couldn't map diagnostic"); | 936 | let diag = map_rust_diagnostic_to_lsp(&diag, &workspace_root).expect("couldn't map diagnostic"); |
937 | insta::assert_debug_snapshot!(diag); | 937 | insta::assert_debug_snapshot!(diag); |
938 | } | 938 | } |
939 | |||
940 | #[test] | ||
941 | #[cfg(not(windows))] | ||
942 | fn snap_multi_line_fix() { | ||
943 | let diag = parse_diagnostic( | ||
944 | r##"{ | ||
945 | "rendered": "warning: returning the result of a let binding from a block\n --> src/main.rs:4:5\n |\n3 | let a = (0..10).collect();\n | -------------------------- unnecessary let binding\n4 | a\n | ^\n |\n = note: `#[warn(clippy::let_and_return)]` on by default\n = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#let_and_return\nhelp: return the expression directly\n |\n3 | \n4 | (0..10).collect()\n |\n\n", | ||
946 | "children": [ | ||
947 | { | ||
948 | "children": [], | ||
949 | "code": null, | ||
950 | "level": "note", | ||
951 | "message": "`#[warn(clippy::let_and_return)]` on by default", | ||
952 | "rendered": null, | ||
953 | "spans": [] | ||
954 | }, | ||
955 | { | ||
956 | "children": [], | ||
957 | "code": null, | ||
958 | "level": "help", | ||
959 | "message": "for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#let_and_return", | ||
960 | "rendered": null, | ||
961 | "spans": [] | ||
962 | }, | ||
963 | { | ||
964 | "children": [], | ||
965 | "code": null, | ||
966 | "level": "help", | ||
967 | "message": "return the expression directly", | ||
968 | "rendered": null, | ||
969 | "spans": [ | ||
970 | { | ||
971 | "byte_end": 55, | ||
972 | "byte_start": 29, | ||
973 | "column_end": 31, | ||
974 | "column_start": 5, | ||
975 | "expansion": null, | ||
976 | "file_name": "src/main.rs", | ||
977 | "is_primary": true, | ||
978 | "label": null, | ||
979 | "line_end": 3, | ||
980 | "line_start": 3, | ||
981 | "suggested_replacement": "", | ||
982 | "suggestion_applicability": "MachineApplicable", | ||
983 | "text": [ | ||
984 | { | ||
985 | "highlight_end": 31, | ||
986 | "highlight_start": 5, | ||
987 | "text": " let a = (0..10).collect();" | ||
988 | } | ||
989 | ] | ||
990 | }, | ||
991 | { | ||
992 | "byte_end": 61, | ||
993 | "byte_start": 60, | ||
994 | "column_end": 6, | ||
995 | "column_start": 5, | ||
996 | "expansion": null, | ||
997 | "file_name": "src/main.rs", | ||
998 | "is_primary": true, | ||
999 | "label": null, | ||
1000 | "line_end": 4, | ||
1001 | "line_start": 4, | ||
1002 | "suggested_replacement": "(0..10).collect()", | ||
1003 | "suggestion_applicability": "MachineApplicable", | ||
1004 | "text": [ | ||
1005 | { | ||
1006 | "highlight_end": 6, | ||
1007 | "highlight_start": 5, | ||
1008 | "text": " a" | ||
1009 | } | ||
1010 | ] | ||
1011 | } | ||
1012 | ] | ||
1013 | } | ||
1014 | ], | ||
1015 | "code": { | ||
1016 | "code": "clippy::let_and_return", | ||
1017 | "explanation": null | ||
1018 | }, | ||
1019 | "level": "warning", | ||
1020 | "message": "returning the result of a let binding from a block", | ||
1021 | "spans": [ | ||
1022 | { | ||
1023 | "byte_end": 55, | ||
1024 | "byte_start": 29, | ||
1025 | "column_end": 31, | ||
1026 | "column_start": 5, | ||
1027 | "expansion": null, | ||
1028 | "file_name": "src/main.rs", | ||
1029 | "is_primary": false, | ||
1030 | "label": "unnecessary let binding", | ||
1031 | "line_end": 3, | ||
1032 | "line_start": 3, | ||
1033 | "suggested_replacement": null, | ||
1034 | "suggestion_applicability": null, | ||
1035 | "text": [ | ||
1036 | { | ||
1037 | "highlight_end": 31, | ||
1038 | "highlight_start": 5, | ||
1039 | "text": " let a = (0..10).collect();" | ||
1040 | } | ||
1041 | ] | ||
1042 | }, | ||
1043 | { | ||
1044 | "byte_end": 61, | ||
1045 | "byte_start": 60, | ||
1046 | "column_end": 6, | ||
1047 | "column_start": 5, | ||
1048 | "expansion": null, | ||
1049 | "file_name": "src/main.rs", | ||
1050 | "is_primary": true, | ||
1051 | "label": null, | ||
1052 | "line_end": 4, | ||
1053 | "line_start": 4, | ||
1054 | "suggested_replacement": null, | ||
1055 | "suggestion_applicability": null, | ||
1056 | "text": [ | ||
1057 | { | ||
1058 | "highlight_end": 6, | ||
1059 | "highlight_start": 5, | ||
1060 | "text": " a" | ||
1061 | } | ||
1062 | ] | ||
1063 | } | ||
1064 | ] | ||
1065 | } | ||
1066 | "##, | ||
1067 | ); | ||
1068 | |||
1069 | let workspace_root = PathBuf::from("/test/"); | ||
1070 | let diag = map_rust_diagnostic_to_lsp(&diag, &workspace_root).expect("couldn't map diagnostic"); | ||
1071 | insta::assert_debug_snapshot!(diag); | ||
1072 | } | ||