diff options
Diffstat (limited to 'crates/ide_diagnostics/src/handlers/incorrect_case.rs')
-rw-r--r-- | crates/ide_diagnostics/src/handlers/incorrect_case.rs | 459 |
1 files changed, 459 insertions, 0 deletions
diff --git a/crates/ide_diagnostics/src/handlers/incorrect_case.rs b/crates/ide_diagnostics/src/handlers/incorrect_case.rs new file mode 100644 index 000000000..68f25f284 --- /dev/null +++ b/crates/ide_diagnostics/src/handlers/incorrect_case.rs | |||
@@ -0,0 +1,459 @@ | |||
1 | use hir::{db::AstDatabase, InFile}; | ||
2 | use ide_db::{assists::Assist, defs::NameClass}; | ||
3 | use syntax::AstNode; | ||
4 | |||
5 | use crate::{ | ||
6 | // references::rename::rename_with_semantics, | ||
7 | unresolved_fix, | ||
8 | Diagnostic, | ||
9 | DiagnosticsContext, | ||
10 | Severity, | ||
11 | }; | ||
12 | |||
13 | // Diagnostic: incorrect-ident-case | ||
14 | // | ||
15 | // This diagnostic is triggered if an item name doesn't follow https://doc.rust-lang.org/1.0.0/style/style/naming/README.html[Rust naming convention]. | ||
16 | pub(crate) fn incorrect_case(ctx: &DiagnosticsContext<'_>, d: &hir::IncorrectCase) -> Diagnostic { | ||
17 | Diagnostic::new( | ||
18 | "incorrect-ident-case", | ||
19 | format!( | ||
20 | "{} `{}` should have {} name, e.g. `{}`", | ||
21 | d.ident_type, d.ident_text, d.expected_case, d.suggested_text | ||
22 | ), | ||
23 | ctx.sema.diagnostics_display_range(InFile::new(d.file, d.ident.clone().into())).range, | ||
24 | ) | ||
25 | .severity(Severity::WeakWarning) | ||
26 | .with_fixes(fixes(ctx, d)) | ||
27 | } | ||
28 | |||
29 | fn fixes(ctx: &DiagnosticsContext<'_>, d: &hir::IncorrectCase) -> Option<Vec<Assist>> { | ||
30 | let root = ctx.sema.db.parse_or_expand(d.file)?; | ||
31 | let name_node = d.ident.to_node(&root); | ||
32 | let def = NameClass::classify(&ctx.sema, &name_node)?.defined(ctx.sema.db)?; | ||
33 | |||
34 | let name_node = InFile::new(d.file, name_node.syntax()); | ||
35 | let frange = name_node.original_file_range(ctx.sema.db); | ||
36 | |||
37 | let label = format!("Rename to {}", d.suggested_text); | ||
38 | let mut res = unresolved_fix("change_case", &label, frange.range); | ||
39 | if ctx.resolve.should_resolve(&res.id) { | ||
40 | let source_change = def.rename(&ctx.sema, &d.suggested_text); | ||
41 | res.source_change = Some(source_change.ok().unwrap_or_default()); | ||
42 | } | ||
43 | |||
44 | Some(vec![res]) | ||
45 | } | ||
46 | |||
47 | #[cfg(test)] | ||
48 | mod change_case { | ||
49 | use crate::tests::{check_diagnostics, check_fix}; | ||
50 | |||
51 | #[test] | ||
52 | fn test_rename_incorrect_case() { | ||
53 | check_fix( | ||
54 | r#" | ||
55 | pub struct test_struct$0 { one: i32 } | ||
56 | |||
57 | pub fn some_fn(val: test_struct) -> test_struct { | ||
58 | test_struct { one: val.one + 1 } | ||
59 | } | ||
60 | "#, | ||
61 | r#" | ||
62 | pub struct TestStruct { one: i32 } | ||
63 | |||
64 | pub fn some_fn(val: TestStruct) -> TestStruct { | ||
65 | TestStruct { one: val.one + 1 } | ||
66 | } | ||
67 | "#, | ||
68 | ); | ||
69 | |||
70 | check_fix( | ||
71 | r#" | ||
72 | pub fn some_fn(NonSnakeCase$0: u8) -> u8 { | ||
73 | NonSnakeCase | ||
74 | } | ||
75 | "#, | ||
76 | r#" | ||
77 | pub fn some_fn(non_snake_case: u8) -> u8 { | ||
78 | non_snake_case | ||
79 | } | ||
80 | "#, | ||
81 | ); | ||
82 | |||
83 | check_fix( | ||
84 | r#" | ||
85 | pub fn SomeFn$0(val: u8) -> u8 { | ||
86 | if val != 0 { SomeFn(val - 1) } else { val } | ||
87 | } | ||
88 | "#, | ||
89 | r#" | ||
90 | pub fn some_fn(val: u8) -> u8 { | ||
91 | if val != 0 { some_fn(val - 1) } else { val } | ||
92 | } | ||
93 | "#, | ||
94 | ); | ||
95 | |||
96 | check_fix( | ||
97 | r#" | ||
98 | fn some_fn() { | ||
99 | let whatAWeird_Formatting$0 = 10; | ||
100 | another_func(whatAWeird_Formatting); | ||
101 | } | ||
102 | "#, | ||
103 | r#" | ||
104 | fn some_fn() { | ||
105 | let what_a_weird_formatting = 10; | ||
106 | another_func(what_a_weird_formatting); | ||
107 | } | ||
108 | "#, | ||
109 | ); | ||
110 | } | ||
111 | |||
112 | #[test] | ||
113 | fn test_uppercase_const_no_diagnostics() { | ||
114 | check_diagnostics( | ||
115 | r#" | ||
116 | fn foo() { | ||
117 | const ANOTHER_ITEM: &str = "some_item"; | ||
118 | } | ||
119 | "#, | ||
120 | ); | ||
121 | } | ||
122 | |||
123 | #[test] | ||
124 | fn test_rename_incorrect_case_struct_method() { | ||
125 | check_fix( | ||
126 | r#" | ||
127 | pub struct TestStruct; | ||
128 | |||
129 | impl TestStruct { | ||
130 | pub fn SomeFn$0() -> TestStruct { | ||
131 | TestStruct | ||
132 | } | ||
133 | } | ||
134 | "#, | ||
135 | r#" | ||
136 | pub struct TestStruct; | ||
137 | |||
138 | impl TestStruct { | ||
139 | pub fn some_fn() -> TestStruct { | ||
140 | TestStruct | ||
141 | } | ||
142 | } | ||
143 | "#, | ||
144 | ); | ||
145 | } | ||
146 | |||
147 | #[test] | ||
148 | fn test_single_incorrect_case_diagnostic_in_function_name_issue_6970() { | ||
149 | check_diagnostics( | ||
150 | r#" | ||
151 | fn FOO() {} | ||
152 | // ^^^ 💡 weak: Function `FOO` should have snake_case name, e.g. `foo` | ||
153 | "#, | ||
154 | ); | ||
155 | check_fix(r#"fn FOO$0() {}"#, r#"fn foo() {}"#); | ||
156 | } | ||
157 | |||
158 | #[test] | ||
159 | fn incorrect_function_name() { | ||
160 | check_diagnostics( | ||
161 | r#" | ||
162 | fn NonSnakeCaseName() {} | ||
163 | // ^^^^^^^^^^^^^^^^ 💡 weak: Function `NonSnakeCaseName` should have snake_case name, e.g. `non_snake_case_name` | ||
164 | "#, | ||
165 | ); | ||
166 | } | ||
167 | |||
168 | #[test] | ||
169 | fn incorrect_function_params() { | ||
170 | check_diagnostics( | ||
171 | r#" | ||
172 | fn foo(SomeParam: u8) {} | ||
173 | // ^^^^^^^^^ 💡 weak: Parameter `SomeParam` should have snake_case name, e.g. `some_param` | ||
174 | |||
175 | fn foo2(ok_param: &str, CAPS_PARAM: u8) {} | ||
176 | // ^^^^^^^^^^ 💡 weak: Parameter `CAPS_PARAM` should have snake_case name, e.g. `caps_param` | ||
177 | "#, | ||
178 | ); | ||
179 | } | ||
180 | |||
181 | #[test] | ||
182 | fn incorrect_variable_names() { | ||
183 | check_diagnostics( | ||
184 | r#" | ||
185 | fn foo() { | ||
186 | let SOME_VALUE = 10; | ||
187 | // ^^^^^^^^^^ 💡 weak: Variable `SOME_VALUE` should have snake_case name, e.g. `some_value` | ||
188 | let AnotherValue = 20; | ||
189 | // ^^^^^^^^^^^^ 💡 weak: Variable `AnotherValue` should have snake_case name, e.g. `another_value` | ||
190 | } | ||
191 | "#, | ||
192 | ); | ||
193 | } | ||
194 | |||
195 | #[test] | ||
196 | fn incorrect_struct_names() { | ||
197 | check_diagnostics( | ||
198 | r#" | ||
199 | struct non_camel_case_name {} | ||
200 | // ^^^^^^^^^^^^^^^^^^^ 💡 weak: Structure `non_camel_case_name` should have CamelCase name, e.g. `NonCamelCaseName` | ||
201 | |||
202 | struct SCREAMING_CASE {} | ||
203 | // ^^^^^^^^^^^^^^ 💡 weak: Structure `SCREAMING_CASE` should have CamelCase name, e.g. `ScreamingCase` | ||
204 | "#, | ||
205 | ); | ||
206 | } | ||
207 | |||
208 | #[test] | ||
209 | fn no_diagnostic_for_camel_cased_acronyms_in_struct_name() { | ||
210 | check_diagnostics( | ||
211 | r#" | ||
212 | struct AABB {} | ||
213 | "#, | ||
214 | ); | ||
215 | } | ||
216 | |||
217 | #[test] | ||
218 | fn incorrect_struct_field() { | ||
219 | check_diagnostics( | ||
220 | r#" | ||
221 | struct SomeStruct { SomeField: u8 } | ||
222 | // ^^^^^^^^^ 💡 weak: Field `SomeField` should have snake_case name, e.g. `some_field` | ||
223 | "#, | ||
224 | ); | ||
225 | } | ||
226 | |||
227 | #[test] | ||
228 | fn incorrect_enum_names() { | ||
229 | check_diagnostics( | ||
230 | r#" | ||
231 | enum some_enum { Val(u8) } | ||
232 | // ^^^^^^^^^ 💡 weak: Enum `some_enum` should have CamelCase name, e.g. `SomeEnum` | ||
233 | |||
234 | enum SOME_ENUM {} | ||
235 | // ^^^^^^^^^ 💡 weak: Enum `SOME_ENUM` should have CamelCase name, e.g. `SomeEnum` | ||
236 | "#, | ||
237 | ); | ||
238 | } | ||
239 | |||
240 | #[test] | ||
241 | fn no_diagnostic_for_camel_cased_acronyms_in_enum_name() { | ||
242 | check_diagnostics( | ||
243 | r#" | ||
244 | enum AABB {} | ||
245 | "#, | ||
246 | ); | ||
247 | } | ||
248 | |||
249 | #[test] | ||
250 | fn incorrect_enum_variant_name() { | ||
251 | check_diagnostics( | ||
252 | r#" | ||
253 | enum SomeEnum { SOME_VARIANT(u8) } | ||
254 | // ^^^^^^^^^^^^ 💡 weak: Variant `SOME_VARIANT` should have CamelCase name, e.g. `SomeVariant` | ||
255 | "#, | ||
256 | ); | ||
257 | } | ||
258 | |||
259 | #[test] | ||
260 | fn incorrect_const_name() { | ||
261 | check_diagnostics( | ||
262 | r#" | ||
263 | const some_weird_const: u8 = 10; | ||
264 | // ^^^^^^^^^^^^^^^^ 💡 weak: Constant `some_weird_const` should have UPPER_SNAKE_CASE name, e.g. `SOME_WEIRD_CONST` | ||
265 | "#, | ||
266 | ); | ||
267 | } | ||
268 | |||
269 | #[test] | ||
270 | fn incorrect_static_name() { | ||
271 | check_diagnostics( | ||
272 | r#" | ||
273 | static some_weird_const: u8 = 10; | ||
274 | // ^^^^^^^^^^^^^^^^ 💡 weak: Static variable `some_weird_const` should have UPPER_SNAKE_CASE name, e.g. `SOME_WEIRD_CONST` | ||
275 | "#, | ||
276 | ); | ||
277 | } | ||
278 | |||
279 | #[test] | ||
280 | fn fn_inside_impl_struct() { | ||
281 | check_diagnostics( | ||
282 | r#" | ||
283 | struct someStruct; | ||
284 | // ^^^^^^^^^^ 💡 weak: Structure `someStruct` should have CamelCase name, e.g. `SomeStruct` | ||
285 | |||
286 | impl someStruct { | ||
287 | fn SomeFunc(&self) { | ||
288 | // ^^^^^^^^ 💡 weak: Function `SomeFunc` should have snake_case name, e.g. `some_func` | ||
289 | let WHY_VAR_IS_CAPS = 10; | ||
290 | // ^^^^^^^^^^^^^^^ 💡 weak: Variable `WHY_VAR_IS_CAPS` should have snake_case name, e.g. `why_var_is_caps` | ||
291 | } | ||
292 | } | ||
293 | "#, | ||
294 | ); | ||
295 | } | ||
296 | |||
297 | #[test] | ||
298 | fn no_diagnostic_for_enum_varinats() { | ||
299 | check_diagnostics( | ||
300 | r#" | ||
301 | enum Option { Some, None } | ||
302 | |||
303 | fn main() { | ||
304 | match Option::None { | ||
305 | None => (), | ||
306 | Some => (), | ||
307 | } | ||
308 | } | ||
309 | "#, | ||
310 | ); | ||
311 | } | ||
312 | |||
313 | #[test] | ||
314 | fn non_let_bind() { | ||
315 | check_diagnostics( | ||
316 | r#" | ||
317 | enum Option { Some, None } | ||
318 | |||
319 | fn main() { | ||
320 | match Option::None { | ||
321 | SOME_VAR @ None => (), | ||
322 | // ^^^^^^^^ 💡 weak: Variable `SOME_VAR` should have snake_case name, e.g. `some_var` | ||
323 | Some => (), | ||
324 | } | ||
325 | } | ||
326 | "#, | ||
327 | ); | ||
328 | } | ||
329 | |||
330 | #[test] | ||
331 | fn allow_attributes_crate_attr() { | ||
332 | check_diagnostics( | ||
333 | r#" | ||
334 | #![allow(non_snake_case)] | ||
335 | |||
336 | mod F { | ||
337 | fn CheckItWorksWithCrateAttr(BAD_NAME_HI: u8) {} | ||
338 | } | ||
339 | "#, | ||
340 | ); | ||
341 | } | ||
342 | |||
343 | #[test] | ||
344 | fn complex_ignore() { | ||
345 | // FIXME: this should trigger errors for the second case. | ||
346 | check_diagnostics( | ||
347 | r#" | ||
348 | trait T { fn a(); } | ||
349 | struct U {} | ||
350 | impl T for U { | ||
351 | fn a() { | ||
352 | #[allow(non_snake_case)] | ||
353 | trait __BitFlagsOk { | ||
354 | const HiImAlsoBad: u8 = 2; | ||
355 | fn Dirty(&self) -> bool { false } | ||
356 | } | ||
357 | |||
358 | trait __BitFlagsBad { | ||
359 | const HiImAlsoBad: u8 = 2; | ||
360 | fn Dirty(&self) -> bool { false } | ||
361 | } | ||
362 | } | ||
363 | } | ||
364 | "#, | ||
365 | ); | ||
366 | } | ||
367 | |||
368 | #[test] | ||
369 | fn infinite_loop_inner_items() { | ||
370 | check_diagnostics( | ||
371 | r#" | ||
372 | fn qualify() { | ||
373 | mod foo { | ||
374 | use super::*; | ||
375 | } | ||
376 | } | ||
377 | "#, | ||
378 | ) | ||
379 | } | ||
380 | |||
381 | #[test] // Issue #8809. | ||
382 | fn parenthesized_parameter() { | ||
383 | check_diagnostics(r#"fn f((O): _) {}"#) | ||
384 | } | ||
385 | |||
386 | #[test] | ||
387 | fn ignores_extern_items() { | ||
388 | cov_mark::check!(extern_func_incorrect_case_ignored); | ||
389 | cov_mark::check!(extern_static_incorrect_case_ignored); | ||
390 | check_diagnostics( | ||
391 | r#" | ||
392 | extern { | ||
393 | fn NonSnakeCaseName(SOME_VAR: u8) -> u8; | ||
394 | pub static SomeStatic: u8 = 10; | ||
395 | } | ||
396 | "#, | ||
397 | ); | ||
398 | } | ||
399 | |||
400 | #[test] | ||
401 | fn bug_traits_arent_checked() { | ||
402 | // FIXME: Traits and functions in traits aren't currently checked by | ||
403 | // r-a, even though rustc will complain about them. | ||
404 | check_diagnostics( | ||
405 | r#" | ||
406 | trait BAD_TRAIT { | ||
407 | fn BAD_FUNCTION(); | ||
408 | fn BadFunction(); | ||
409 | } | ||
410 | "#, | ||
411 | ); | ||
412 | } | ||
413 | |||
414 | #[test] | ||
415 | fn allow_attributes() { | ||
416 | check_diagnostics( | ||
417 | r#" | ||
418 | #[allow(non_snake_case)] | ||
419 | fn NonSnakeCaseName(SOME_VAR: u8) -> u8{ | ||
420 | // cov_flags generated output from elsewhere in this file | ||
421 | extern "C" { | ||
422 | #[no_mangle] | ||
423 | static lower_case: u8; | ||
424 | } | ||
425 | |||
426 | let OtherVar = SOME_VAR + 1; | ||
427 | OtherVar | ||
428 | } | ||
429 | |||
430 | #[allow(nonstandard_style)] | ||
431 | mod CheckNonstandardStyle { | ||
432 | fn HiImABadFnName() {} | ||
433 | } | ||
434 | |||
435 | #[allow(bad_style)] | ||
436 | mod CheckBadStyle { | ||
437 | fn HiImABadFnName() {} | ||
438 | } | ||
439 | |||
440 | mod F { | ||
441 | #![allow(non_snake_case)] | ||
442 | fn CheckItWorksWithModAttr(BAD_NAME_HI: u8) {} | ||
443 | } | ||
444 | |||
445 | #[allow(non_snake_case, non_camel_case_types)] | ||
446 | pub struct some_type { | ||
447 | SOME_FIELD: u8, | ||
448 | SomeField: u16, | ||
449 | } | ||
450 | |||
451 | #[allow(non_upper_case_globals)] | ||
452 | pub const some_const: u8 = 10; | ||
453 | |||
454 | #[allow(non_upper_case_globals)] | ||
455 | pub static SomeStatic: u8 = 10; | ||
456 | "#, | ||
457 | ); | ||
458 | } | ||
459 | } | ||