]> git.lizzy.rs Git - rust.git/blob - crates/ide/src/diagnostics.rs
Merge #6785
[rust.git] / crates / ide / src / diagnostics.rs
1 //! Collects diagnostics & fixits  for a single file.
2 //!
3 //! The tricky bit here is that diagnostics are produced by hir in terms of
4 //! macro-expanded files, but we need to present them to the users in terms of
5 //! original files. So we need to map the ranges.
6
7 mod fixes;
8 mod field_shorthand;
9
10 use std::cell::RefCell;
11
12 use hir::{
13     diagnostics::{Diagnostic as _, DiagnosticCode, DiagnosticSinkBuilder},
14     Semantics,
15 };
16 use ide_db::base_db::SourceDatabase;
17 use ide_db::RootDatabase;
18 use itertools::Itertools;
19 use rustc_hash::FxHashSet;
20 use syntax::{
21     ast::{self, AstNode},
22     SyntaxNode, TextRange, T,
23 };
24 use text_edit::TextEdit;
25
26 use crate::{FileId, Label, SourceChange, SourceFileEdit};
27
28 use self::fixes::DiagnosticWithFix;
29
30 #[derive(Debug)]
31 pub struct Diagnostic {
32     // pub name: Option<String>,
33     pub message: String,
34     pub range: TextRange,
35     pub severity: Severity,
36     pub fix: Option<Fix>,
37     pub unused: bool,
38     pub code: Option<DiagnosticCode>,
39 }
40
41 impl Diagnostic {
42     fn error(range: TextRange, message: String) -> Self {
43         Self { message, range, severity: Severity::Error, fix: None, unused: false, code: None }
44     }
45
46     fn hint(range: TextRange, message: String) -> Self {
47         Self {
48             message,
49             range,
50             severity: Severity::WeakWarning,
51             fix: None,
52             unused: false,
53             code: None,
54         }
55     }
56
57     fn with_fix(self, fix: Option<Fix>) -> Self {
58         Self { fix, ..self }
59     }
60
61     fn with_unused(self, unused: bool) -> Self {
62         Self { unused, ..self }
63     }
64
65     fn with_code(self, code: Option<DiagnosticCode>) -> Self {
66         Self { code, ..self }
67     }
68 }
69
70 #[derive(Debug)]
71 pub struct Fix {
72     pub label: Label,
73     pub source_change: SourceChange,
74     /// Allows to trigger the fix only when the caret is in the range given
75     pub fix_trigger_range: TextRange,
76 }
77
78 impl Fix {
79     fn new(label: &str, source_change: SourceChange, fix_trigger_range: TextRange) -> Self {
80         let label = Label::new(label);
81         Self { label, source_change, fix_trigger_range }
82     }
83 }
84
85 #[derive(Debug, Copy, Clone)]
86 pub enum Severity {
87     Error,
88     WeakWarning,
89 }
90
91 #[derive(Default, Debug, Clone)]
92 pub struct DiagnosticsConfig {
93     pub disable_experimental: bool,
94     pub disabled: FxHashSet<String>,
95 }
96
97 pub(crate) fn diagnostics(
98     db: &RootDatabase,
99     config: &DiagnosticsConfig,
100     file_id: FileId,
101 ) -> Vec<Diagnostic> {
102     let _p = profile::span("diagnostics");
103     let sema = Semantics::new(db);
104     let parse = db.parse(file_id);
105     let mut res = Vec::new();
106
107     // [#34344] Only take first 128 errors to prevent slowing down editor/ide, the number 128 is chosen arbitrarily.
108     res.extend(
109         parse
110             .errors()
111             .iter()
112             .take(128)
113             .map(|err| Diagnostic::error(err.range(), format!("Syntax Error: {}", err))),
114     );
115
116     for node in parse.tree().syntax().descendants() {
117         check_unnecessary_braces_in_use_statement(&mut res, file_id, &node);
118         field_shorthand::check(&mut res, file_id, &node);
119     }
120     let res = RefCell::new(res);
121     let sink_builder = DiagnosticSinkBuilder::new()
122         .on::<hir::diagnostics::UnresolvedModule, _>(|d| {
123             res.borrow_mut().push(diagnostic_with_fix(d, &sema));
124         })
125         .on::<hir::diagnostics::MissingFields, _>(|d| {
126             res.borrow_mut().push(diagnostic_with_fix(d, &sema));
127         })
128         .on::<hir::diagnostics::MissingOkInTailExpr, _>(|d| {
129             res.borrow_mut().push(diagnostic_with_fix(d, &sema));
130         })
131         .on::<hir::diagnostics::NoSuchField, _>(|d| {
132             res.borrow_mut().push(diagnostic_with_fix(d, &sema));
133         })
134         .on::<hir::diagnostics::IncorrectCase, _>(|d| {
135             res.borrow_mut().push(warning_with_fix(d, &sema));
136         })
137         .on::<hir::diagnostics::InactiveCode, _>(|d| {
138             // If there's inactive code somewhere in a macro, don't propagate to the call-site.
139             if d.display_source().file_id.expansion_info(db).is_some() {
140                 return;
141             }
142
143             // Override severity and mark as unused.
144             res.borrow_mut().push(
145                 Diagnostic::hint(sema.diagnostics_display_range(d).range, d.message())
146                     .with_unused(true)
147                     .with_code(Some(d.code())),
148             );
149         })
150         .on::<hir::diagnostics::UnresolvedProcMacro, _>(|d| {
151             // Use more accurate position if available.
152             let display_range =
153                 d.precise_location.unwrap_or_else(|| sema.diagnostics_display_range(d).range);
154
155             // FIXME: it would be nice to tell the user whether proc macros are currently disabled
156             res.borrow_mut()
157                 .push(Diagnostic::hint(display_range, d.message()).with_code(Some(d.code())));
158         })
159         // Only collect experimental diagnostics when they're enabled.
160         .filter(|diag| !(diag.is_experimental() && config.disable_experimental))
161         .filter(|diag| !config.disabled.contains(diag.code().as_str()));
162
163     // Finalize the `DiagnosticSink` building process.
164     let mut sink = sink_builder
165         // Diagnostics not handled above get no fix and default treatment.
166         .build(|d| {
167             res.borrow_mut().push(
168                 Diagnostic::error(sema.diagnostics_display_range(d).range, d.message())
169                     .with_code(Some(d.code())),
170             );
171         });
172
173     if let Some(m) = sema.to_module_def(file_id) {
174         m.diagnostics(db, &mut sink);
175     };
176     drop(sink);
177     res.into_inner()
178 }
179
180 fn diagnostic_with_fix<D: DiagnosticWithFix>(d: &D, sema: &Semantics<RootDatabase>) -> Diagnostic {
181     Diagnostic::error(sema.diagnostics_display_range(d).range, d.message())
182         .with_fix(d.fix(&sema))
183         .with_code(Some(d.code()))
184 }
185
186 fn warning_with_fix<D: DiagnosticWithFix>(d: &D, sema: &Semantics<RootDatabase>) -> Diagnostic {
187     Diagnostic::hint(sema.diagnostics_display_range(d).range, d.message())
188         .with_fix(d.fix(&sema))
189         .with_code(Some(d.code()))
190 }
191
192 fn check_unnecessary_braces_in_use_statement(
193     acc: &mut Vec<Diagnostic>,
194     file_id: FileId,
195     node: &SyntaxNode,
196 ) -> Option<()> {
197     let use_tree_list = ast::UseTreeList::cast(node.clone())?;
198     if let Some((single_use_tree,)) = use_tree_list.use_trees().collect_tuple() {
199         let use_range = use_tree_list.syntax().text_range();
200         let edit =
201             text_edit_for_remove_unnecessary_braces_with_self_in_use_statement(&single_use_tree)
202                 .unwrap_or_else(|| {
203                     let to_replace = single_use_tree.syntax().text().to_string();
204                     let mut edit_builder = TextEdit::builder();
205                     edit_builder.delete(use_range);
206                     edit_builder.insert(use_range.start(), to_replace);
207                     edit_builder.finish()
208                 });
209
210         acc.push(
211             Diagnostic::hint(use_range, "Unnecessary braces in use statement".to_string())
212                 .with_fix(Some(Fix::new(
213                     "Remove unnecessary braces",
214                     SourceFileEdit { file_id, edit }.into(),
215                     use_range,
216                 ))),
217         );
218     }
219
220     Some(())
221 }
222
223 fn text_edit_for_remove_unnecessary_braces_with_self_in_use_statement(
224     single_use_tree: &ast::UseTree,
225 ) -> Option<TextEdit> {
226     let use_tree_list_node = single_use_tree.syntax().parent()?;
227     if single_use_tree.path()?.segment()?.syntax().first_child_or_token()?.kind() == T![self] {
228         let start = use_tree_list_node.prev_sibling_or_token()?.text_range().start();
229         let end = use_tree_list_node.text_range().end();
230         return Some(TextEdit::delete(TextRange::new(start, end)));
231     }
232     None
233 }
234
235 #[cfg(test)]
236 mod tests {
237     use expect_test::{expect, Expect};
238     use stdx::trim_indent;
239     use test_utils::assert_eq_text;
240
241     use crate::{fixture, DiagnosticsConfig};
242
243     /// Takes a multi-file input fixture with annotated cursor positions,
244     /// and checks that:
245     ///  * a diagnostic is produced
246     ///  * this diagnostic fix trigger range touches the input cursor position
247     ///  * that the contents of the file containing the cursor match `after` after the diagnostic fix is applied
248     pub(super) fn check_fix(ra_fixture_before: &str, ra_fixture_after: &str) {
249         let after = trim_indent(ra_fixture_after);
250
251         let (analysis, file_position) = fixture::position(ra_fixture_before);
252         let diagnostic = analysis
253             .diagnostics(&DiagnosticsConfig::default(), file_position.file_id)
254             .unwrap()
255             .pop()
256             .unwrap();
257         let mut fix = diagnostic.fix.unwrap();
258         let edit = fix.source_change.source_file_edits.pop().unwrap().edit;
259         let target_file_contents = analysis.file_text(file_position.file_id).unwrap();
260         let actual = {
261             let mut actual = target_file_contents.to_string();
262             edit.apply(&mut actual);
263             actual
264         };
265
266         assert_eq_text!(&after, &actual);
267         assert!(
268             fix.fix_trigger_range.contains_inclusive(file_position.offset),
269             "diagnostic fix range {:?} does not touch cursor position {:?}",
270             fix.fix_trigger_range,
271             file_position.offset
272         );
273     }
274
275     /// Similar to `check_fix`, but applies all the available fixes.
276     fn check_fixes(ra_fixture_before: &str, ra_fixture_after: &str) {
277         let after = trim_indent(ra_fixture_after);
278
279         let (analysis, file_position) = fixture::position(ra_fixture_before);
280         let diagnostic = analysis
281             .diagnostics(&DiagnosticsConfig::default(), file_position.file_id)
282             .unwrap()
283             .pop()
284             .unwrap();
285         let fix = diagnostic.fix.unwrap();
286         let target_file_contents = analysis.file_text(file_position.file_id).unwrap();
287         let actual = {
288             let mut actual = target_file_contents.to_string();
289             // Go from the last one to the first one, so that ranges won't be affected by previous edits.
290             for edit in fix.source_change.source_file_edits.iter().rev() {
291                 edit.edit.apply(&mut actual);
292             }
293             actual
294         };
295
296         assert_eq_text!(&after, &actual);
297         assert!(
298             fix.fix_trigger_range.contains_inclusive(file_position.offset),
299             "diagnostic fix range {:?} does not touch cursor position {:?}",
300             fix.fix_trigger_range,
301             file_position.offset
302         );
303     }
304
305     /// Checks that a diagnostic applies to the file containing the `<|>` cursor marker
306     /// which has a fix that can apply to other files.
307     fn check_apply_diagnostic_fix_in_other_file(ra_fixture_before: &str, ra_fixture_after: &str) {
308         let ra_fixture_after = &trim_indent(ra_fixture_after);
309         let (analysis, file_pos) = fixture::position(ra_fixture_before);
310         let current_file_id = file_pos.file_id;
311         let diagnostic = analysis
312             .diagnostics(&DiagnosticsConfig::default(), current_file_id)
313             .unwrap()
314             .pop()
315             .unwrap();
316         let mut fix = diagnostic.fix.unwrap();
317         let edit = fix.source_change.source_file_edits.pop().unwrap();
318         let changed_file_id = edit.file_id;
319         let before = analysis.file_text(changed_file_id).unwrap();
320         let actual = {
321             let mut actual = before.to_string();
322             edit.edit.apply(&mut actual);
323             actual
324         };
325         assert_eq_text!(ra_fixture_after, &actual);
326     }
327
328     /// Takes a multi-file input fixture with annotated cursor position and checks that no diagnostics
329     /// apply to the file containing the cursor.
330     pub(crate) fn check_no_diagnostics(ra_fixture: &str) {
331         let (analysis, files) = fixture::files(ra_fixture);
332         let diagnostics = files
333             .into_iter()
334             .flat_map(|file_id| {
335                 analysis.diagnostics(&DiagnosticsConfig::default(), file_id).unwrap()
336             })
337             .collect::<Vec<_>>();
338         assert_eq!(diagnostics.len(), 0, "unexpected diagnostics:\n{:#?}", diagnostics);
339     }
340
341     fn check_expect(ra_fixture: &str, expect: Expect) {
342         let (analysis, file_id) = fixture::file(ra_fixture);
343         let diagnostics = analysis.diagnostics(&DiagnosticsConfig::default(), file_id).unwrap();
344         expect.assert_debug_eq(&diagnostics)
345     }
346
347     #[test]
348     fn test_wrap_return_type() {
349         check_fix(
350             r#"
351 //- /main.rs crate:main deps:core
352 use core::result::Result::{self, Ok, Err};
353
354 fn div(x: i32, y: i32) -> Result<i32, ()> {
355     if y == 0 {
356         return Err(());
357     }
358     x / y<|>
359 }
360 //- /core/lib.rs crate:core
361 pub mod result {
362     pub enum Result<T, E> { Ok(T), Err(E) }
363 }
364 "#,
365             r#"
366 use core::result::Result::{self, Ok, Err};
367
368 fn div(x: i32, y: i32) -> Result<i32, ()> {
369     if y == 0 {
370         return Err(());
371     }
372     Ok(x / y)
373 }
374 "#,
375         );
376     }
377
378     #[test]
379     fn test_wrap_return_type_handles_generic_functions() {
380         check_fix(
381             r#"
382 //- /main.rs crate:main deps:core
383 use core::result::Result::{self, Ok, Err};
384
385 fn div<T>(x: T) -> Result<T, i32> {
386     if x == 0 {
387         return Err(7);
388     }
389     <|>x
390 }
391 //- /core/lib.rs crate:core
392 pub mod result {
393     pub enum Result<T, E> { Ok(T), Err(E) }
394 }
395 "#,
396             r#"
397 use core::result::Result::{self, Ok, Err};
398
399 fn div<T>(x: T) -> Result<T, i32> {
400     if x == 0 {
401         return Err(7);
402     }
403     Ok(x)
404 }
405 "#,
406         );
407     }
408
409     #[test]
410     fn test_wrap_return_type_handles_type_aliases() {
411         check_fix(
412             r#"
413 //- /main.rs crate:main deps:core
414 use core::result::Result::{self, Ok, Err};
415
416 type MyResult<T> = Result<T, ()>;
417
418 fn div(x: i32, y: i32) -> MyResult<i32> {
419     if y == 0 {
420         return Err(());
421     }
422     x <|>/ y
423 }
424 //- /core/lib.rs crate:core
425 pub mod result {
426     pub enum Result<T, E> { Ok(T), Err(E) }
427 }
428 "#,
429             r#"
430 use core::result::Result::{self, Ok, Err};
431
432 type MyResult<T> = Result<T, ()>;
433
434 fn div(x: i32, y: i32) -> MyResult<i32> {
435     if y == 0 {
436         return Err(());
437     }
438     Ok(x / y)
439 }
440 "#,
441         );
442     }
443
444     #[test]
445     fn test_wrap_return_type_not_applicable_when_expr_type_does_not_match_ok_type() {
446         check_no_diagnostics(
447             r#"
448 //- /main.rs crate:main deps:core
449 use core::result::Result::{self, Ok, Err};
450
451 fn foo() -> Result<(), i32> { 0 }
452
453 //- /core/lib.rs crate:core
454 pub mod result {
455     pub enum Result<T, E> { Ok(T), Err(E) }
456 }
457 "#,
458         );
459     }
460
461     #[test]
462     fn test_wrap_return_type_not_applicable_when_return_type_is_not_result() {
463         check_no_diagnostics(
464             r#"
465 //- /main.rs crate:main deps:core
466 use core::result::Result::{self, Ok, Err};
467
468 enum SomeOtherEnum { Ok(i32), Err(String) }
469
470 fn foo() -> SomeOtherEnum { 0 }
471
472 //- /core/lib.rs crate:core
473 pub mod result {
474     pub enum Result<T, E> { Ok(T), Err(E) }
475 }
476 "#,
477         );
478     }
479
480     #[test]
481     fn test_fill_struct_fields_empty() {
482         check_fix(
483             r#"
484 struct TestStruct { one: i32, two: i64 }
485
486 fn test_fn() {
487     let s = TestStruct {<|>};
488 }
489 "#,
490             r#"
491 struct TestStruct { one: i32, two: i64 }
492
493 fn test_fn() {
494     let s = TestStruct { one: (), two: ()};
495 }
496 "#,
497         );
498     }
499
500     #[test]
501     fn test_fill_struct_fields_self() {
502         check_fix(
503             r#"
504 struct TestStruct { one: i32 }
505
506 impl TestStruct {
507     fn test_fn() { let s = Self {<|>}; }
508 }
509 "#,
510             r#"
511 struct TestStruct { one: i32 }
512
513 impl TestStruct {
514     fn test_fn() { let s = Self { one: ()}; }
515 }
516 "#,
517         );
518     }
519
520     #[test]
521     fn test_fill_struct_fields_enum() {
522         check_fix(
523             r#"
524 enum Expr {
525     Bin { lhs: Box<Expr>, rhs: Box<Expr> }
526 }
527
528 impl Expr {
529     fn new_bin(lhs: Box<Expr>, rhs: Box<Expr>) -> Expr {
530         Expr::Bin {<|> }
531     }
532 }
533 "#,
534             r#"
535 enum Expr {
536     Bin { lhs: Box<Expr>, rhs: Box<Expr> }
537 }
538
539 impl Expr {
540     fn new_bin(lhs: Box<Expr>, rhs: Box<Expr>) -> Expr {
541         Expr::Bin { lhs: (), rhs: () }
542     }
543 }
544 "#,
545         );
546     }
547
548     #[test]
549     fn test_fill_struct_fields_partial() {
550         check_fix(
551             r#"
552 struct TestStruct { one: i32, two: i64 }
553
554 fn test_fn() {
555     let s = TestStruct{ two: 2<|> };
556 }
557 "#,
558             r"
559 struct TestStruct { one: i32, two: i64 }
560
561 fn test_fn() {
562     let s = TestStruct{ two: 2, one: () };
563 }
564 ",
565         );
566     }
567
568     #[test]
569     fn test_fill_struct_fields_no_diagnostic() {
570         check_no_diagnostics(
571             r"
572             struct TestStruct { one: i32, two: i64 }
573
574             fn test_fn() {
575                 let one = 1;
576                 let s = TestStruct{ one, two: 2 };
577             }
578         ",
579         );
580     }
581
582     #[test]
583     fn test_fill_struct_fields_no_diagnostic_on_spread() {
584         check_no_diagnostics(
585             r"
586             struct TestStruct { one: i32, two: i64 }
587
588             fn test_fn() {
589                 let one = 1;
590                 let s = TestStruct{ ..a };
591             }
592         ",
593         );
594     }
595
596     #[test]
597     fn test_unresolved_module_diagnostic() {
598         check_expect(
599             r#"mod foo;"#,
600             expect![[r#"
601                 [
602                     Diagnostic {
603                         message: "unresolved module",
604                         range: 0..8,
605                         severity: Error,
606                         fix: Some(
607                             Fix {
608                                 label: "Create module",
609                                 source_change: SourceChange {
610                                     source_file_edits: [],
611                                     file_system_edits: [
612                                         CreateFile {
613                                             dst: AnchoredPathBuf {
614                                                 anchor: FileId(
615                                                     0,
616                                                 ),
617                                                 path: "foo.rs",
618                                             },
619                                         },
620                                     ],
621                                     is_snippet: false,
622                                 },
623                                 fix_trigger_range: 0..8,
624                             },
625                         ),
626                         unused: false,
627                         code: Some(
628                             DiagnosticCode(
629                                 "unresolved-module",
630                             ),
631                         ),
632                     },
633                 ]
634             "#]],
635         );
636     }
637
638     #[test]
639     fn range_mapping_out_of_macros() {
640         // FIXME: this is very wrong, but somewhat tricky to fix.
641         check_fix(
642             r#"
643 fn some() {}
644 fn items() {}
645 fn here() {}
646
647 macro_rules! id { ($($tt:tt)*) => { $($tt)*}; }
648
649 fn main() {
650     let _x = id![Foo { a: <|>42 }];
651 }
652
653 pub struct Foo { pub a: i32, pub b: i32 }
654 "#,
655             r#"
656 fn some(, b: ()) {}
657 fn items() {}
658 fn here() {}
659
660 macro_rules! id { ($($tt:tt)*) => { $($tt)*}; }
661
662 fn main() {
663     let _x = id![Foo { a: 42 }];
664 }
665
666 pub struct Foo { pub a: i32, pub b: i32 }
667 "#,
668         );
669     }
670
671     #[test]
672     fn test_check_unnecessary_braces_in_use_statement() {
673         check_no_diagnostics(
674             r#"
675 use a;
676 use a::{c, d::e};
677
678 mod a {
679     mod c {}
680     mod d {
681         mod e {}
682     }
683 }
684 "#,
685         );
686         check_fix(
687             r"
688             mod b {}
689             use {<|>b};
690             ",
691             r"
692             mod b {}
693             use b;
694             ",
695         );
696         check_fix(
697             r"
698             mod b {}
699             use {b<|>};
700             ",
701             r"
702             mod b {}
703             use b;
704             ",
705         );
706         check_fix(
707             r"
708             mod a { mod c {} }
709             use a::{c<|>};
710             ",
711             r"
712             mod a { mod c {} }
713             use a::c;
714             ",
715         );
716         check_fix(
717             r"
718             mod a {}
719             use a::{self<|>};
720             ",
721             r"
722             mod a {}
723             use a;
724             ",
725         );
726         check_fix(
727             r"
728             mod a { mod c {} mod d { mod e {} } }
729             use a::{c, d::{e<|>}};
730             ",
731             r"
732             mod a { mod c {} mod d { mod e {} } }
733             use a::{c, d::e};
734             ",
735         );
736     }
737
738     #[test]
739     fn test_add_field_from_usage() {
740         check_fix(
741             r"
742 fn main() {
743     Foo { bar: 3, baz<|>: false};
744 }
745 struct Foo {
746     bar: i32
747 }
748 ",
749             r"
750 fn main() {
751     Foo { bar: 3, baz: false};
752 }
753 struct Foo {
754     bar: i32,
755     baz: bool
756 }
757 ",
758         )
759     }
760
761     #[test]
762     fn test_add_field_in_other_file_from_usage() {
763         check_apply_diagnostic_fix_in_other_file(
764             r"
765             //- /main.rs
766             mod foo;
767
768             fn main() {
769                 <|>foo::Foo { bar: 3, baz: false};
770             }
771             //- /foo.rs
772             struct Foo {
773                 bar: i32
774             }
775             ",
776             r"
777             struct Foo {
778                 bar: i32,
779                 pub(crate) baz: bool
780             }
781             ",
782         )
783     }
784
785     #[test]
786     fn test_disabled_diagnostics() {
787         let mut config = DiagnosticsConfig::default();
788         config.disabled.insert("unresolved-module".into());
789
790         let (analysis, file_id) = fixture::file(r#"mod foo;"#);
791
792         let diagnostics = analysis.diagnostics(&config, file_id).unwrap();
793         assert!(diagnostics.is_empty());
794
795         let diagnostics = analysis.diagnostics(&DiagnosticsConfig::default(), file_id).unwrap();
796         assert!(!diagnostics.is_empty());
797     }
798
799     #[test]
800     fn test_rename_incorrect_case() {
801         check_fixes(
802             r#"
803 pub struct test_struct<|> { one: i32 }
804
805 pub fn some_fn(val: test_struct) -> test_struct {
806     test_struct { one: val.one + 1 }
807 }
808 "#,
809             r#"
810 pub struct TestStruct { one: i32 }
811
812 pub fn some_fn(val: TestStruct) -> TestStruct {
813     TestStruct { one: val.one + 1 }
814 }
815 "#,
816         );
817
818         check_fixes(
819             r#"
820 pub fn some_fn(NonSnakeCase<|>: u8) -> u8 {
821     NonSnakeCase
822 }
823 "#,
824             r#"
825 pub fn some_fn(non_snake_case: u8) -> u8 {
826     non_snake_case
827 }
828 "#,
829         );
830
831         check_fixes(
832             r#"
833 pub fn SomeFn<|>(val: u8) -> u8 {
834     if val != 0 { SomeFn(val - 1) } else { val }
835 }
836 "#,
837             r#"
838 pub fn some_fn(val: u8) -> u8 {
839     if val != 0 { some_fn(val - 1) } else { val }
840 }
841 "#,
842         );
843
844         check_fixes(
845             r#"
846 fn some_fn() {
847     let whatAWeird_Formatting<|> = 10;
848     another_func(whatAWeird_Formatting);
849 }
850 "#,
851             r#"
852 fn some_fn() {
853     let what_a_weird_formatting = 10;
854     another_func(what_a_weird_formatting);
855 }
856 "#,
857         );
858     }
859
860     #[test]
861     fn test_uppercase_const_no_diagnostics() {
862         check_no_diagnostics(
863             r#"
864 fn foo() {
865     const ANOTHER_ITEM<|>: &str = "some_item";
866 }
867 "#,
868         );
869     }
870
871     #[test]
872     fn test_rename_incorrect_case_struct_method() {
873         check_fixes(
874             r#"
875 pub struct TestStruct;
876
877 impl TestStruct {
878     pub fn SomeFn<|>() -> TestStruct {
879         TestStruct
880     }
881 }
882 "#,
883             r#"
884 pub struct TestStruct;
885
886 impl TestStruct {
887     pub fn some_fn() -> TestStruct {
888         TestStruct
889     }
890 }
891 "#,
892         );
893     }
894 }