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