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].
incorrect_case(ctx: &DiagnosticsContext<'_>, d: &hir::IncorrectCase) -> Diagnostic16 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
fixes(ctx: &DiagnosticsContext<'_>, d: &hir::IncorrectCase) -> Option<Vec<Assist>>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()?;
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]
test_rename_incorrect_case()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_aweird_formatting = 10;
106 another_func(what_aweird_formatting);
107 }
108 "#,
109 );
110 }
111
112 #[test]
test_uppercase_const_no_diagnostics()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]
test_rename_incorrect_case_struct_method()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]
test_single_incorrect_case_diagnostic_in_function_name_issue_6970()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]
incorrect_function_name()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]
incorrect_function_params()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]
incorrect_variable_names()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]
incorrect_struct_names()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]
no_diagnostic_for_camel_cased_acronyms_in_struct_name()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]
incorrect_struct_field()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]
incorrect_enum_names()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]
no_diagnostic_for_camel_cased_acronyms_in_enum_name()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]
incorrect_enum_variant_name()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]
incorrect_const_name()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]
incorrect_static_name()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]
fn_inside_impl_struct()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]
no_diagnostic_for_enum_varinats()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]
non_let_bind()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]
allow_attributes_crate_attr()331 fn allow_attributes_crate_attr() {
332 check_diagnostics(
333 r#"
334 #![allow(non_snake_case)]
335 #![allow(non_camel_case_types)]
336
337 struct S {
338 fooBar: bool,
339 }
340
341 enum E {
342 fooBar,
343 }
344
345 mod F {
346 fn CheckItWorksWithCrateAttr(BAD_NAME_HI: u8) {}
347 }
348 "#,
349 );
350 }
351
352 #[test]
complex_ignore()353 fn complex_ignore() {
354 // FIXME: this should trigger errors for the second case.
355 check_diagnostics(
356 r#"
357 trait T { fn a(); }
358 struct U {}
359 impl T for U {
360 fn a() {
361 #[allow(non_snake_case)]
362 trait __BitFlagsOk {
363 const HiImAlsoBad: u8 = 2;
364 fn Dirty(&self) -> bool { false }
365 }
366
367 trait __BitFlagsBad {
368 const HiImAlsoBad: u8 = 2;
369 fn Dirty(&self) -> bool { false }
370 }
371 }
372 }
373 "#,
374 );
375 }
376
377 #[test]
infinite_loop_inner_items()378 fn infinite_loop_inner_items() {
379 check_diagnostics(
380 r#"
381 fn qualify() {
382 mod foo {
383 use super::*;
384 }
385 }
386 "#,
387 )
388 }
389
390 #[test] // Issue #8809.
parenthesized_parameter()391 fn parenthesized_parameter() {
392 check_diagnostics(r#"fn f((O): _) {}"#)
393 }
394
395 #[test]
ignores_extern_items()396 fn ignores_extern_items() {
397 cov_mark::check!(extern_func_incorrect_case_ignored);
398 cov_mark::check!(extern_static_incorrect_case_ignored);
399 check_diagnostics(
400 r#"
401 extern {
402 fn NonSnakeCaseName(SOME_VAR: u8) -> u8;
403 pub static SomeStatic: u8 = 10;
404 }
405 "#,
406 );
407 }
408
409 #[test]
ignores_extern_items_from_macro()410 fn ignores_extern_items_from_macro() {
411 check_diagnostics(
412 r#"
413 macro_rules! m {
414 () => {
415 fn NonSnakeCaseName(SOME_VAR: u8) -> u8;
416 pub static SomeStatic: u8 = 10;
417 }
418 }
419
420 extern {
421 m!();
422 }
423 "#,
424 );
425 }
426
427 #[test]
bug_traits_arent_checked()428 fn bug_traits_arent_checked() {
429 // FIXME: Traits and functions in traits aren't currently checked by
430 // r-a, even though rustc will complain about them.
431 check_diagnostics(
432 r#"
433 trait BAD_TRAIT {
434 fn BAD_FUNCTION();
435 fn BadFunction();
436 }
437 "#,
438 );
439 }
440
441 #[test]
allow_attributes()442 fn allow_attributes() {
443 check_diagnostics(
444 r#"
445 #[allow(non_snake_case)]
446 fn NonSnakeCaseName(SOME_VAR: u8) -> u8{
447 // cov_flags generated output from elsewhere in this file
448 extern "C" {
449 #[no_mangle]
450 static lower_case: u8;
451 }
452
453 let OtherVar = SOME_VAR + 1;
454 OtherVar
455 }
456
457 #[allow(nonstandard_style)]
458 mod CheckNonstandardStyle {
459 fn HiImABadFnName() {}
460 }
461
462 #[allow(bad_style)]
463 mod CheckBadStyle {
464 fn HiImABadFnName() {}
465 }
466
467 mod F {
468 #![allow(non_snake_case)]
469 fn CheckItWorksWithModAttr(BAD_NAME_HI: u8) {}
470 }
471
472 #[allow(non_snake_case, non_camel_case_types)]
473 pub struct some_type {
474 SOME_FIELD: u8,
475 SomeField: u16,
476 }
477
478 #[allow(non_upper_case_globals)]
479 pub const some_const: u8 = 10;
480
481 #[allow(non_upper_case_globals)]
482 pub static SomeStatic: u8 = 10;
483 "#,
484 );
485 }
486 }
487