diff options
author | bors[bot] <26634292+bors[bot]@users.noreply.github.com> | 2019-06-29 10:50:56 +0100 |
---|---|---|
committer | bors[bot] <26634292+bors[bot]@users.noreply.github.com> | 2019-06-29 10:50:56 +0100 |
commit | 8865db6768fd913b971df56fc0bcf7439fcb1e71 (patch) | |
tree | 3fff3954f7f5ca0c6045f9f7d96c1cd75bd896ee /editors/code/src/test/utils/diagnotics/rust.test.ts | |
parent | 64f71dd3fff7b902656a2c2465a1b5071b2b1903 (diff) | |
parent | 50c6ab709e38b63fb1e3ee5db40426ef131cbd71 (diff) |
Merge #1454
1454: Fix `cargo watch` code action filtering r=etaoins a=etaoins
There are two issues with the implementation of `provideCodeActions` introduced in #1439:
1. We're returning the code action based on the file its diagnostic is in; not the file the suggested fix is in. I'm not sure how often fixes are suggested cross-file but it's something we should handle.
2. We're not filtering code actions based on the passed range. The means if there is any suggestion in a file we'll show an action for every line of the file. I naively thought that VS Code would filter for us but that was wrong.
Unfortunately the VS Code `CodeAction` object is very complex - it can handle edits across multiple files, run commands, etc. This makes it complex to check them for equality or see if any of their edits intersects with a specified range.
To make it easier to work with suggestions this introduces a `SuggestedFix` model object and a `SuggestFixCollection` code action provider. This is a layer between the raw Rust JSON and VS Code's `CodeAction`s. I was reluctant to introduce another layer of abstraction here but my attempt to work directly with VS Code's model objects was worse.
Co-authored-by: Ryan Cumming <[email protected]>
Diffstat (limited to 'editors/code/src/test/utils/diagnotics/rust.test.ts')
-rw-r--r-- | editors/code/src/test/utils/diagnotics/rust.test.ts | 173 |
1 files changed, 173 insertions, 0 deletions
diff --git a/editors/code/src/test/utils/diagnotics/rust.test.ts b/editors/code/src/test/utils/diagnotics/rust.test.ts new file mode 100644 index 000000000..b555a4819 --- /dev/null +++ b/editors/code/src/test/utils/diagnotics/rust.test.ts | |||
@@ -0,0 +1,173 @@ | |||
1 | import * as assert from 'assert'; | ||
2 | import * as fs from 'fs'; | ||
3 | import * as vscode from 'vscode'; | ||
4 | |||
5 | import { | ||
6 | MappedRustDiagnostic, | ||
7 | mapRustDiagnosticToVsCode, | ||
8 | RustDiagnostic, | ||
9 | SuggestionApplicability | ||
10 | } from '../../../utils/diagnostics/rust'; | ||
11 | |||
12 | function loadDiagnosticFixture(name: string): RustDiagnostic { | ||
13 | const jsonText = fs | ||
14 | .readFileSync( | ||
15 | // We're actually in our JavaScript output directory, climb out | ||
16 | `${__dirname}/../../../../src/test/fixtures/rust-diagnostics/${name}.json` | ||
17 | ) | ||
18 | .toString(); | ||
19 | |||
20 | return JSON.parse(jsonText); | ||
21 | } | ||
22 | |||
23 | function mapFixtureToVsCode(name: string): MappedRustDiagnostic { | ||
24 | const rd = loadDiagnosticFixture(name); | ||
25 | const mapResult = mapRustDiagnosticToVsCode(rd); | ||
26 | |||
27 | if (!mapResult) { | ||
28 | return assert.fail('Mapping unexpectedly failed'); | ||
29 | } | ||
30 | return mapResult; | ||
31 | } | ||
32 | |||
33 | describe('mapRustDiagnosticToVsCode', () => { | ||
34 | it('should map an incompatible type for trait error', () => { | ||
35 | const { diagnostic, suggestedFixes } = mapFixtureToVsCode( | ||
36 | 'error/E0053' | ||
37 | ); | ||
38 | |||
39 | assert.strictEqual( | ||
40 | diagnostic.severity, | ||
41 | vscode.DiagnosticSeverity.Error | ||
42 | ); | ||
43 | assert.strictEqual(diagnostic.source, 'rustc'); | ||
44 | assert.strictEqual( | ||
45 | diagnostic.message, | ||
46 | [ | ||
47 | `method \`next\` has an incompatible type for trait`, | ||
48 | `expected type \`fn(&mut ty::list_iter::ListIterator<'list, M>) -> std::option::Option<&ty::Ref<M>>\``, | ||
49 | ` found type \`fn(&ty::list_iter::ListIterator<'list, M>) -> std::option::Option<&'list ty::Ref<M>>\`` | ||
50 | ].join('\n') | ||
51 | ); | ||
52 | assert.strictEqual(diagnostic.code, 'E0053'); | ||
53 | assert.strictEqual(diagnostic.tags, undefined); | ||
54 | |||
55 | // No related information | ||
56 | assert.deepStrictEqual(diagnostic.relatedInformation, []); | ||
57 | |||
58 | // There are no suggested fixes | ||
59 | assert.strictEqual(suggestedFixes.length, 0); | ||
60 | }); | ||
61 | |||
62 | it('should map an unused variable warning', () => { | ||
63 | const { diagnostic, suggestedFixes } = mapFixtureToVsCode( | ||
64 | 'warning/unused_variables' | ||
65 | ); | ||
66 | |||
67 | assert.strictEqual( | ||
68 | diagnostic.severity, | ||
69 | vscode.DiagnosticSeverity.Warning | ||
70 | ); | ||
71 | assert.strictEqual( | ||
72 | diagnostic.message, | ||
73 | [ | ||
74 | 'unused variable: `foo`', | ||
75 | '#[warn(unused_variables)] on by default' | ||
76 | ].join('\n') | ||
77 | ); | ||
78 | assert.strictEqual(diagnostic.code, 'unused_variables'); | ||
79 | assert.strictEqual(diagnostic.source, 'rustc'); | ||
80 | assert.deepStrictEqual(diagnostic.tags, [ | ||
81 | vscode.DiagnosticTag.Unnecessary | ||
82 | ]); | ||
83 | |||
84 | // No related information | ||
85 | assert.deepStrictEqual(diagnostic.relatedInformation, []); | ||
86 | |||
87 | // One suggested fix available to prefix the variable | ||
88 | assert.strictEqual(suggestedFixes.length, 1); | ||
89 | const [suggestedFix] = suggestedFixes; | ||
90 | assert.strictEqual( | ||
91 | suggestedFix.title, | ||
92 | 'consider prefixing with an underscore: `_foo`' | ||
93 | ); | ||
94 | assert.strictEqual( | ||
95 | suggestedFix.applicability, | ||
96 | SuggestionApplicability.MachineApplicable | ||
97 | ); | ||
98 | }); | ||
99 | |||
100 | it('should map a wrong number of parameters error', () => { | ||
101 | const { diagnostic, suggestedFixes } = mapFixtureToVsCode( | ||
102 | 'error/E0061' | ||
103 | ); | ||
104 | |||
105 | assert.strictEqual( | ||
106 | diagnostic.severity, | ||
107 | vscode.DiagnosticSeverity.Error | ||
108 | ); | ||
109 | assert.strictEqual( | ||
110 | diagnostic.message, | ||
111 | 'this function takes 2 parameters but 3 parameters were supplied' | ||
112 | ); | ||
113 | assert.strictEqual(diagnostic.code, 'E0061'); | ||
114 | assert.strictEqual(diagnostic.source, 'rustc'); | ||
115 | assert.strictEqual(diagnostic.tags, undefined); | ||
116 | |||
117 | // One related information for the original definition | ||
118 | const relatedInformation = diagnostic.relatedInformation; | ||
119 | if (!relatedInformation) { | ||
120 | return assert.fail('Related information unexpectedly undefined'); | ||
121 | } | ||
122 | assert.strictEqual(relatedInformation.length, 1); | ||
123 | const [related] = relatedInformation; | ||
124 | assert.strictEqual(related.message, 'defined here'); | ||
125 | |||
126 | // There are no suggested fixes | ||
127 | assert.strictEqual(suggestedFixes.length, 0); | ||
128 | }); | ||
129 | |||
130 | it('should map a Clippy copy pass by ref warning', () => { | ||
131 | const { diagnostic, suggestedFixes } = mapFixtureToVsCode( | ||
132 | 'clippy/trivially_copy_pass_by_ref' | ||
133 | ); | ||
134 | |||
135 | assert.strictEqual( | ||
136 | diagnostic.severity, | ||
137 | vscode.DiagnosticSeverity.Warning | ||
138 | ); | ||
139 | assert.strictEqual(diagnostic.source, 'clippy'); | ||
140 | assert.strictEqual( | ||
141 | diagnostic.message, | ||
142 | [ | ||
143 | 'this argument is passed by reference, but would be more efficient if passed by value', | ||
144 | '#[warn(clippy::trivially_copy_pass_by_ref)] implied by #[warn(clippy::all)]', | ||
145 | 'for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#trivially_copy_pass_by_ref' | ||
146 | ].join('\n') | ||
147 | ); | ||
148 | assert.strictEqual(diagnostic.code, 'trivially_copy_pass_by_ref'); | ||
149 | assert.strictEqual(diagnostic.tags, undefined); | ||
150 | |||
151 | // One related information for the lint definition | ||
152 | const relatedInformation = diagnostic.relatedInformation; | ||
153 | if (!relatedInformation) { | ||
154 | return assert.fail('Related information unexpectedly undefined'); | ||
155 | } | ||
156 | assert.strictEqual(relatedInformation.length, 1); | ||
157 | const [related] = relatedInformation; | ||
158 | assert.strictEqual(related.message, 'lint level defined here'); | ||
159 | |||
160 | // One suggested fix to pass by value | ||
161 | assert.strictEqual(suggestedFixes.length, 1); | ||
162 | const [suggestedFix] = suggestedFixes; | ||
163 | assert.strictEqual( | ||
164 | suggestedFix.title, | ||
165 | 'consider passing by value instead: `self`' | ||
166 | ); | ||
167 | // Clippy does not mark this with any applicability | ||
168 | assert.strictEqual( | ||
169 | suggestedFix.applicability, | ||
170 | SuggestionApplicability.Unspecified | ||
171 | ); | ||
172 | }); | ||
173 | }); | ||