]> git.lizzy.rs Git - rust.git/blob - crates/ide/src/diagnostics.rs
minor
[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 break_outside_of_loop;
8 mod inactive_code;
9 mod macro_error;
10 mod mismatched_arg_count;
11 mod missing_fields;
12 mod missing_ok_or_some_in_tail_expr;
13 mod missing_unsafe;
14 mod no_such_field;
15 mod remove_this_semicolon;
16 mod unimplemented_builtin_macro;
17 mod unresolved_extern_crate;
18 mod unresolved_import;
19 mod unresolved_macro_call;
20 mod unresolved_module;
21 mod unresolved_proc_macro;
22
23 mod fixes;
24 mod field_shorthand;
25 mod unlinked_file;
26
27 use std::cell::RefCell;
28
29 use hir::{
30     diagnostics::{AnyDiagnostic, Diagnostic as _, DiagnosticCode, DiagnosticSinkBuilder},
31     Semantics,
32 };
33 use ide_assists::AssistResolveStrategy;
34 use ide_db::{base_db::SourceDatabase, RootDatabase};
35 use itertools::Itertools;
36 use rustc_hash::FxHashSet;
37 use syntax::{
38     ast::{self, AstNode},
39     SyntaxNode, SyntaxNodePtr, TextRange, TextSize,
40 };
41 use text_edit::TextEdit;
42 use unlinked_file::UnlinkedFile;
43
44 use crate::{Assist, AssistId, AssistKind, FileId, Label, SourceChange};
45
46 use self::fixes::DiagnosticWithFixes;
47
48 #[derive(Debug)]
49 pub struct Diagnostic {
50     // pub name: Option<String>,
51     pub message: String,
52     pub range: TextRange,
53     pub severity: Severity,
54     pub fixes: Option<Vec<Assist>>,
55     pub unused: bool,
56     pub code: Option<DiagnosticCode>,
57     pub experimental: bool,
58 }
59
60 impl Diagnostic {
61     fn new(code: &'static str, message: impl Into<String>, range: TextRange) -> Diagnostic {
62         let message = message.into();
63         let code = Some(DiagnosticCode(code));
64         Self {
65             message,
66             range,
67             severity: Severity::Error,
68             fixes: None,
69             unused: false,
70             code,
71             experimental: false,
72         }
73     }
74
75     fn experimental(mut self) -> Diagnostic {
76         self.experimental = true;
77         self
78     }
79
80     fn severity(mut self, severity: Severity) -> Diagnostic {
81         self.severity = severity;
82         self
83     }
84
85     fn error(range: TextRange, message: String) -> Self {
86         Self {
87             message,
88             range,
89             severity: Severity::Error,
90             fixes: None,
91             unused: false,
92             code: None,
93             experimental: false,
94         }
95     }
96
97     fn hint(range: TextRange, message: String) -> Self {
98         Self {
99             message,
100             range,
101             severity: Severity::WeakWarning,
102             fixes: None,
103             unused: false,
104             code: None,
105             experimental: false,
106         }
107     }
108
109     fn with_fixes(self, fixes: Option<Vec<Assist>>) -> Self {
110         Self { fixes, ..self }
111     }
112
113     fn with_unused(self, unused: bool) -> Self {
114         Self { unused, ..self }
115     }
116
117     fn with_code(self, code: Option<DiagnosticCode>) -> Self {
118         Self { code, ..self }
119     }
120 }
121
122 #[derive(Debug, Copy, Clone)]
123 pub enum Severity {
124     Error,
125     WeakWarning,
126 }
127
128 #[derive(Default, Debug, Clone)]
129 pub struct DiagnosticsConfig {
130     pub disable_experimental: bool,
131     pub disabled: FxHashSet<String>,
132 }
133
134 struct DiagnosticsContext<'a> {
135     config: &'a DiagnosticsConfig,
136     sema: Semantics<'a, RootDatabase>,
137     #[allow(unused)]
138     resolve: &'a AssistResolveStrategy,
139 }
140
141 pub(crate) fn diagnostics(
142     db: &RootDatabase,
143     config: &DiagnosticsConfig,
144     resolve: &AssistResolveStrategy,
145     file_id: FileId,
146 ) -> Vec<Diagnostic> {
147     let _p = profile::span("diagnostics");
148     let sema = Semantics::new(db);
149     let parse = db.parse(file_id);
150     let mut res = Vec::new();
151
152     // [#34344] Only take first 128 errors to prevent slowing down editor/ide, the number 128 is chosen arbitrarily.
153     res.extend(
154         parse
155             .errors()
156             .iter()
157             .take(128)
158             .map(|err| Diagnostic::error(err.range(), format!("Syntax Error: {}", err))),
159     );
160
161     for node in parse.tree().syntax().descendants() {
162         check_unnecessary_braces_in_use_statement(&mut res, file_id, &node);
163         field_shorthand::check(&mut res, file_id, &node);
164     }
165     let res = RefCell::new(res);
166     let sink_builder = DiagnosticSinkBuilder::new()
167         .on::<hir::diagnostics::IncorrectCase, _>(|d| {
168             res.borrow_mut().push(warning_with_fix(d, &sema, resolve));
169         })
170         .on::<hir::diagnostics::ReplaceFilterMapNextWithFindMap, _>(|d| {
171             res.borrow_mut().push(warning_with_fix(d, &sema, resolve));
172         })
173         .on::<UnlinkedFile, _>(|d| {
174             // Limit diagnostic to the first few characters in the file. This matches how VS Code
175             // renders it with the full span, but on other editors, and is less invasive.
176             let range = sema.diagnostics_display_range(d.display_source()).range;
177             let range = range.intersect(TextRange::up_to(TextSize::of("..."))).unwrap_or(range);
178
179             // Override severity and mark as unused.
180             res.borrow_mut().push(
181                 Diagnostic::hint(range, d.message())
182                     .with_fixes(d.fixes(&sema, resolve))
183                     .with_code(Some(d.code())),
184             );
185         })
186         // Only collect experimental diagnostics when they're enabled.
187         .filter(|diag| !(diag.is_experimental() && config.disable_experimental))
188         .filter(|diag| !config.disabled.contains(diag.code().as_str()));
189
190     // Finalize the `DiagnosticSink` building process.
191     let mut sink = sink_builder
192         // Diagnostics not handled above get no fix and default treatment.
193         .build(|d| {
194             res.borrow_mut().push(
195                 Diagnostic::error(
196                     sema.diagnostics_display_range(d.display_source()).range,
197                     d.message(),
198                 )
199                 .with_code(Some(d.code())),
200             );
201         });
202
203     let mut diags = Vec::new();
204     let internal_diagnostics = cfg!(test);
205     match sema.to_module_def(file_id) {
206         Some(m) => diags = m.diagnostics(db, &mut sink, internal_diagnostics),
207         None => {
208             sink.push(UnlinkedFile { file_id, node: SyntaxNodePtr::new(parse.tree().syntax()) });
209         }
210     }
211
212     drop(sink);
213
214     let mut res = res.into_inner();
215
216     let ctx = DiagnosticsContext { config, sema, resolve };
217     for diag in diags {
218         #[rustfmt::skip]
219         let d = match diag {
220             AnyDiagnostic::BreakOutsideOfLoop(d) => break_outside_of_loop::break_outside_of_loop(&ctx, &d),
221             AnyDiagnostic::MacroError(d) => macro_error::macro_error(&ctx, &d),
222             AnyDiagnostic::MismatchedArgCount(d) => mismatched_arg_count::mismatched_arg_count(&ctx, &d),
223             AnyDiagnostic::MissingFields(d) => missing_fields::missing_fields(&ctx, &d),
224             AnyDiagnostic::MissingOkOrSomeInTailExpr(d) => missing_ok_or_some_in_tail_expr::missing_ok_or_some_in_tail_expr(&ctx, &d),
225             AnyDiagnostic::MissingUnsafe(d) => missing_unsafe::missing_unsafe(&ctx, &d),
226             AnyDiagnostic::NoSuchField(d) => no_such_field::no_such_field(&ctx, &d),
227             AnyDiagnostic::RemoveThisSemicolon(d) => remove_this_semicolon::remove_this_semicolon(&ctx, &d),
228             AnyDiagnostic::UnimplementedBuiltinMacro(d) => unimplemented_builtin_macro::unimplemented_builtin_macro(&ctx, &d),
229             AnyDiagnostic::UnresolvedExternCrate(d) => unresolved_extern_crate::unresolved_extern_crate(&ctx, &d),
230             AnyDiagnostic::UnresolvedImport(d) => unresolved_import::unresolved_import(&ctx, &d),
231             AnyDiagnostic::UnresolvedMacroCall(d) => unresolved_macro_call::unresolved_macro_call(&ctx, &d),
232             AnyDiagnostic::UnresolvedModule(d) => unresolved_module::unresolved_module(&ctx, &d),
233             AnyDiagnostic::UnresolvedProcMacro(d) => unresolved_proc_macro::unresolved_proc_macro(&ctx, &d),
234
235             AnyDiagnostic::InactiveCode(d) => match inactive_code::inactive_code(&ctx, &d) {
236                 Some(it) => it,
237                 None => continue,
238             }
239         };
240         if let Some(code) = d.code {
241             if ctx.config.disabled.contains(code.as_str()) {
242                 continue;
243             }
244         }
245         if ctx.config.disable_experimental && d.experimental {
246             continue;
247         }
248         res.push(d)
249     }
250
251     res
252 }
253
254 fn warning_with_fix<D: DiagnosticWithFixes>(
255     d: &D,
256     sema: &Semantics<RootDatabase>,
257     resolve: &AssistResolveStrategy,
258 ) -> Diagnostic {
259     Diagnostic::hint(sema.diagnostics_display_range(d.display_source()).range, d.message())
260         .with_fixes(d.fixes(sema, resolve))
261         .with_code(Some(d.code()))
262 }
263
264 fn check_unnecessary_braces_in_use_statement(
265     acc: &mut Vec<Diagnostic>,
266     file_id: FileId,
267     node: &SyntaxNode,
268 ) -> Option<()> {
269     let use_tree_list = ast::UseTreeList::cast(node.clone())?;
270     if let Some((single_use_tree,)) = use_tree_list.use_trees().collect_tuple() {
271         // If there is a comment inside the bracketed `use`,
272         // assume it is a commented out module path and don't show diagnostic.
273         if use_tree_list.has_inner_comment() {
274             return Some(());
275         }
276
277         let use_range = use_tree_list.syntax().text_range();
278         let edit =
279             text_edit_for_remove_unnecessary_braces_with_self_in_use_statement(&single_use_tree)
280                 .unwrap_or_else(|| {
281                     let to_replace = single_use_tree.syntax().text().to_string();
282                     let mut edit_builder = TextEdit::builder();
283                     edit_builder.delete(use_range);
284                     edit_builder.insert(use_range.start(), to_replace);
285                     edit_builder.finish()
286                 });
287
288         acc.push(
289             Diagnostic::hint(use_range, "Unnecessary braces in use statement".to_string())
290                 .with_fixes(Some(vec![fix(
291                     "remove_braces",
292                     "Remove unnecessary braces",
293                     SourceChange::from_text_edit(file_id, edit),
294                     use_range,
295                 )])),
296         );
297     }
298
299     Some(())
300 }
301
302 fn text_edit_for_remove_unnecessary_braces_with_self_in_use_statement(
303     single_use_tree: &ast::UseTree,
304 ) -> Option<TextEdit> {
305     let use_tree_list_node = single_use_tree.syntax().parent()?;
306     if single_use_tree.path()?.segment()?.self_token().is_some() {
307         let start = use_tree_list_node.prev_sibling_or_token()?.text_range().start();
308         let end = use_tree_list_node.text_range().end();
309         return Some(TextEdit::delete(TextRange::new(start, end)));
310     }
311     None
312 }
313
314 fn fix(id: &'static str, label: &str, source_change: SourceChange, target: TextRange) -> Assist {
315     let mut res = unresolved_fix(id, label, target);
316     res.source_change = Some(source_change);
317     res
318 }
319
320 fn unresolved_fix(id: &'static str, label: &str, target: TextRange) -> Assist {
321     assert!(!id.contains(' '));
322     Assist {
323         id: AssistId(id, AssistKind::QuickFix),
324         label: Label::new(label),
325         group: None,
326         target,
327         source_change: None,
328     }
329 }
330
331 #[cfg(test)]
332 mod tests {
333     use expect_test::Expect;
334     use ide_assists::AssistResolveStrategy;
335     use stdx::trim_indent;
336     use test_utils::{assert_eq_text, extract_annotations};
337
338     use crate::{fixture, DiagnosticsConfig};
339
340     /// Takes a multi-file input fixture with annotated cursor positions,
341     /// and checks that:
342     ///  * a diagnostic is produced
343     ///  * the first diagnostic fix trigger range touches the input cursor position
344     ///  * that the contents of the file containing the cursor match `after` after the diagnostic fix is applied
345     #[track_caller]
346     pub(crate) fn check_fix(ra_fixture_before: &str, ra_fixture_after: &str) {
347         check_nth_fix(0, ra_fixture_before, ra_fixture_after);
348     }
349     /// Takes a multi-file input fixture with annotated cursor positions,
350     /// and checks that:
351     ///  * a diagnostic is produced
352     ///  * every diagnostic fixes trigger range touches the input cursor position
353     ///  * that the contents of the file containing the cursor match `after` after each diagnostic fix is applied
354     pub(crate) fn check_fixes(ra_fixture_before: &str, ra_fixtures_after: Vec<&str>) {
355         for (i, ra_fixture_after) in ra_fixtures_after.iter().enumerate() {
356             check_nth_fix(i, ra_fixture_before, ra_fixture_after)
357         }
358     }
359
360     #[track_caller]
361     fn check_nth_fix(nth: usize, ra_fixture_before: &str, ra_fixture_after: &str) {
362         let after = trim_indent(ra_fixture_after);
363
364         let (analysis, file_position) = fixture::position(ra_fixture_before);
365         let diagnostic = analysis
366             .diagnostics(
367                 &DiagnosticsConfig::default(),
368                 AssistResolveStrategy::All,
369                 file_position.file_id,
370             )
371             .unwrap()
372             .pop()
373             .unwrap();
374         let fix = &diagnostic.fixes.unwrap()[nth];
375         let actual = {
376             let source_change = fix.source_change.as_ref().unwrap();
377             let file_id = *source_change.source_file_edits.keys().next().unwrap();
378             let mut actual = analysis.file_text(file_id).unwrap().to_string();
379
380             for edit in source_change.source_file_edits.values() {
381                 edit.apply(&mut actual);
382             }
383             actual
384         };
385
386         assert_eq_text!(&after, &actual);
387         assert!(
388             fix.target.contains_inclusive(file_position.offset),
389             "diagnostic fix range {:?} does not touch cursor position {:?}",
390             fix.target,
391             file_position.offset
392         );
393     }
394     /// Checks that there's a diagnostic *without* fix at `$0`.
395     fn check_no_fix(ra_fixture: &str) {
396         let (analysis, file_position) = fixture::position(ra_fixture);
397         let diagnostic = analysis
398             .diagnostics(
399                 &DiagnosticsConfig::default(),
400                 AssistResolveStrategy::All,
401                 file_position.file_id,
402             )
403             .unwrap()
404             .pop()
405             .unwrap();
406         assert!(diagnostic.fixes.is_none(), "got a fix when none was expected: {:?}", diagnostic);
407     }
408
409     pub(crate) fn check_expect(ra_fixture: &str, expect: Expect) {
410         let (analysis, file_id) = fixture::file(ra_fixture);
411         let diagnostics = analysis
412             .diagnostics(&DiagnosticsConfig::default(), AssistResolveStrategy::All, file_id)
413             .unwrap();
414         expect.assert_debug_eq(&diagnostics)
415     }
416
417     #[track_caller]
418     pub(crate) fn check_diagnostics(ra_fixture: &str) {
419         let mut config = DiagnosticsConfig::default();
420         config.disabled.insert("inactive-code".to_string());
421         check_diagnostics_with_config(config, ra_fixture)
422     }
423
424     #[track_caller]
425     pub(crate) fn check_diagnostics_with_config(config: DiagnosticsConfig, ra_fixture: &str) {
426         let (analysis, files) = fixture::files(ra_fixture);
427         for file_id in files {
428             let diagnostics =
429                 analysis.diagnostics(&config, AssistResolveStrategy::All, file_id).unwrap();
430
431             let expected = extract_annotations(&*analysis.file_text(file_id).unwrap());
432             let mut actual =
433                 diagnostics.into_iter().map(|d| (d.range, d.message)).collect::<Vec<_>>();
434             actual.sort_by_key(|(range, _)| range.start());
435             assert_eq!(expected, actual);
436         }
437     }
438
439     #[test]
440     fn test_check_unnecessary_braces_in_use_statement() {
441         check_diagnostics(
442             r#"
443 use a;
444 use a::{c, d::e};
445
446 mod a {
447     mod c {}
448     mod d {
449         mod e {}
450     }
451 }
452 "#,
453         );
454         check_diagnostics(
455             r#"
456 use a;
457 use a::{
458     c,
459     // d::e
460 };
461
462 mod a {
463     mod c {}
464     mod d {
465         mod e {}
466     }
467 }
468 "#,
469         );
470         check_fix(
471             r"
472             mod b {}
473             use {$0b};
474             ",
475             r"
476             mod b {}
477             use b;
478             ",
479         );
480         check_fix(
481             r"
482             mod b {}
483             use {b$0};
484             ",
485             r"
486             mod b {}
487             use b;
488             ",
489         );
490         check_fix(
491             r"
492             mod a { mod c {} }
493             use a::{c$0};
494             ",
495             r"
496             mod a { mod c {} }
497             use a::c;
498             ",
499         );
500         check_fix(
501             r"
502             mod a {}
503             use a::{self$0};
504             ",
505             r"
506             mod a {}
507             use a;
508             ",
509         );
510         check_fix(
511             r"
512             mod a { mod c {} mod d { mod e {} } }
513             use a::{c, d::{e$0}};
514             ",
515             r"
516             mod a { mod c {} mod d { mod e {} } }
517             use a::{c, d::e};
518             ",
519         );
520     }
521
522     #[test]
523     fn test_disabled_diagnostics() {
524         let mut config = DiagnosticsConfig::default();
525         config.disabled.insert("unresolved-module".into());
526
527         let (analysis, file_id) = fixture::file(r#"mod foo;"#);
528
529         let diagnostics =
530             analysis.diagnostics(&config, AssistResolveStrategy::All, file_id).unwrap();
531         assert!(diagnostics.is_empty());
532
533         let diagnostics = analysis
534             .diagnostics(&DiagnosticsConfig::default(), AssistResolveStrategy::All, file_id)
535             .unwrap();
536         assert!(!diagnostics.is_empty());
537     }
538
539     #[test]
540     fn unlinked_file_prepend_first_item() {
541         cov_mark::check!(unlinked_file_prepend_before_first_item);
542         // Only tests the first one for `pub mod` since the rest are the same
543         check_fixes(
544             r#"
545 //- /main.rs
546 fn f() {}
547 //- /foo.rs
548 $0
549 "#,
550             vec![
551                 r#"
552 mod foo;
553
554 fn f() {}
555 "#,
556                 r#"
557 pub mod foo;
558
559 fn f() {}
560 "#,
561             ],
562         );
563     }
564
565     #[test]
566     fn unlinked_file_append_mod() {
567         cov_mark::check!(unlinked_file_append_to_existing_mods);
568         check_fix(
569             r#"
570 //- /main.rs
571 //! Comment on top
572
573 mod preexisting;
574
575 mod preexisting2;
576
577 struct S;
578
579 mod preexisting_bottom;)
580 //- /foo.rs
581 $0
582 "#,
583             r#"
584 //! Comment on top
585
586 mod preexisting;
587
588 mod preexisting2;
589 mod foo;
590
591 struct S;
592
593 mod preexisting_bottom;)
594 "#,
595         );
596     }
597
598     #[test]
599     fn unlinked_file_insert_in_empty_file() {
600         cov_mark::check!(unlinked_file_empty_file);
601         check_fix(
602             r#"
603 //- /main.rs
604 //- /foo.rs
605 $0
606 "#,
607             r#"
608 mod foo;
609 "#,
610         );
611     }
612
613     #[test]
614     fn unlinked_file_old_style_modrs() {
615         check_fix(
616             r#"
617 //- /main.rs
618 mod submod;
619 //- /submod/mod.rs
620 // in mod.rs
621 //- /submod/foo.rs
622 $0
623 "#,
624             r#"
625 // in mod.rs
626 mod foo;
627 "#,
628         );
629     }
630
631     #[test]
632     fn unlinked_file_new_style_mod() {
633         check_fix(
634             r#"
635 //- /main.rs
636 mod submod;
637 //- /submod.rs
638 //- /submod/foo.rs
639 $0
640 "#,
641             r#"
642 mod foo;
643 "#,
644         );
645     }
646
647     #[test]
648     fn unlinked_file_with_cfg_off() {
649         cov_mark::check!(unlinked_file_skip_fix_when_mod_already_exists);
650         check_no_fix(
651             r#"
652 //- /main.rs
653 #[cfg(never)]
654 mod foo;
655
656 //- /foo.rs
657 $0
658 "#,
659         );
660     }
661
662     #[test]
663     fn unlinked_file_with_cfg_on() {
664         check_diagnostics(
665             r#"
666 //- /main.rs
667 #[cfg(not(never))]
668 mod foo;
669
670 //- /foo.rs
671 "#,
672         );
673     }
674
675     // Register the required standard library types to make the tests work
676     fn add_filter_map_with_find_next_boilerplate(body: &str) -> String {
677         let prefix = r#"
678         //- /main.rs crate:main deps:core
679         use core::iter::Iterator;
680         use core::option::Option::{self, Some, None};
681         "#;
682         let suffix = r#"
683         //- /core/lib.rs crate:core
684         pub mod option {
685             pub enum Option<T> { Some(T), None }
686         }
687         pub mod iter {
688             pub trait Iterator {
689                 type Item;
690                 fn filter_map<B, F>(self, f: F) -> FilterMap where F: FnMut(Self::Item) -> Option<B> { FilterMap }
691                 fn next(&mut self) -> Option<Self::Item>;
692             }
693             pub struct FilterMap {}
694             impl Iterator for FilterMap {
695                 type Item = i32;
696                 fn next(&mut self) -> i32 { 7 }
697             }
698         }
699         "#;
700         format!("{}{}{}", prefix, body, suffix)
701     }
702
703     #[test]
704     fn replace_filter_map_next_with_find_map2() {
705         check_diagnostics(&add_filter_map_with_find_next_boilerplate(
706             r#"
707             fn foo() {
708                 let m = [1, 2, 3].iter().filter_map(|x| if *x == 2 { Some (4) } else { None }).next();
709                       //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ replace filter_map(..).next() with find_map(..)
710             }
711         "#,
712         ));
713     }
714
715     #[test]
716     fn replace_filter_map_next_with_find_map_no_diagnostic_without_next() {
717         check_diagnostics(&add_filter_map_with_find_next_boilerplate(
718             r#"
719             fn foo() {
720                 let m = [1, 2, 3]
721                     .iter()
722                     .filter_map(|x| if *x == 2 { Some (4) } else { None })
723                     .len();
724             }
725             "#,
726         ));
727     }
728
729     #[test]
730     fn replace_filter_map_next_with_find_map_no_diagnostic_with_intervening_methods() {
731         check_diagnostics(&add_filter_map_with_find_next_boilerplate(
732             r#"
733             fn foo() {
734                 let m = [1, 2, 3]
735                     .iter()
736                     .filter_map(|x| if *x == 2 { Some (4) } else { None })
737                     .map(|x| x + 2)
738                     .len();
739             }
740             "#,
741         ));
742     }
743
744     #[test]
745     fn replace_filter_map_next_with_find_map_no_diagnostic_if_not_in_chain() {
746         check_diagnostics(&add_filter_map_with_find_next_boilerplate(
747             r#"
748             fn foo() {
749                 let m = [1, 2, 3]
750                     .iter()
751                     .filter_map(|x| if *x == 2 { Some (4) } else { None });
752                 let n = m.next();
753             }
754             "#,
755         ));
756     }
757
758     #[test]
759     fn missing_record_pat_field_no_diagnostic_if_not_exhaustive() {
760         check_diagnostics(
761             r"
762 struct S { foo: i32, bar: () }
763 fn baz(s: S) -> i32 {
764     match s {
765         S { foo, .. } => foo,
766     }
767 }
768 ",
769         )
770     }
771
772     #[test]
773     fn missing_record_pat_field_box() {
774         check_diagnostics(
775             r"
776 struct S { s: Box<u32> }
777 fn x(a: S) {
778     let S { box s } = a;
779 }
780 ",
781         )
782     }
783
784     #[test]
785     fn missing_record_pat_field_ref() {
786         check_diagnostics(
787             r"
788 struct S { s: u32 }
789 fn x(a: S) {
790     let S { ref s } = a;
791 }
792 ",
793         )
794     }
795
796     #[test]
797     fn import_extern_crate_clash_with_inner_item() {
798         // This is more of a resolver test, but doesn't really work with the hir_def testsuite.
799
800         check_diagnostics(
801             r#"
802 //- /lib.rs crate:lib deps:jwt
803 mod permissions;
804
805 use permissions::jwt;
806
807 fn f() {
808     fn inner() {}
809     jwt::Claims {}; // should resolve to the local one with 0 fields, and not get a diagnostic
810 }
811
812 //- /permissions.rs
813 pub mod jwt  {
814     pub struct Claims {}
815 }
816
817 //- /jwt/lib.rs crate:jwt
818 pub struct Claims {
819     field: u8,
820 }
821         "#,
822         );
823     }
824 }
825
826 #[cfg(test)]
827 pub(super) mod match_check_tests {
828     use crate::diagnostics::tests::check_diagnostics;
829
830     #[test]
831     fn empty_tuple() {
832         check_diagnostics(
833             r#"
834 fn main() {
835     match () { }
836         //^^ Missing match arm
837     match (()) { }
838         //^^^^ Missing match arm
839
840     match () { _ => (), }
841     match () { () => (), }
842     match (()) { (()) => (), }
843 }
844 "#,
845         );
846     }
847
848     #[test]
849     fn tuple_of_two_empty_tuple() {
850         check_diagnostics(
851             r#"
852 fn main() {
853     match ((), ()) { }
854         //^^^^^^^^ Missing match arm
855
856     match ((), ()) { ((), ()) => (), }
857 }
858 "#,
859         );
860     }
861
862     #[test]
863     fn boolean() {
864         check_diagnostics(
865             r#"
866 fn test_main() {
867     match false { }
868         //^^^^^ Missing match arm
869     match false { true => (), }
870         //^^^^^ Missing match arm
871     match (false, true) {}
872         //^^^^^^^^^^^^^ Missing match arm
873     match (false, true) { (true, true) => (), }
874         //^^^^^^^^^^^^^ Missing match arm
875     match (false, true) {
876         //^^^^^^^^^^^^^ Missing match arm
877         (false, true) => (),
878         (false, false) => (),
879         (true, false) => (),
880     }
881     match (false, true) { (true, _x) => (), }
882         //^^^^^^^^^^^^^ Missing match arm
883
884     match false { true => (), false => (), }
885     match (false, true) {
886         (false, _) => (),
887         (true, false) => (),
888         (_, true) => (),
889     }
890     match (false, true) {
891         (true, true) => (),
892         (true, false) => (),
893         (false, true) => (),
894         (false, false) => (),
895     }
896     match (false, true) {
897         (true, _x) => (),
898         (false, true) => (),
899         (false, false) => (),
900     }
901     match (false, true, false) {
902         (false, ..) => (),
903         (true, ..) => (),
904     }
905     match (false, true, false) {
906         (.., false) => (),
907         (.., true) => (),
908     }
909     match (false, true, false) { (..) => (), }
910 }
911 "#,
912         );
913     }
914
915     #[test]
916     fn tuple_of_tuple_and_bools() {
917         check_diagnostics(
918             r#"
919 fn main() {
920     match (false, ((), false)) {}
921         //^^^^^^^^^^^^^^^^^^^^ Missing match arm
922     match (false, ((), false)) { (true, ((), true)) => (), }
923         //^^^^^^^^^^^^^^^^^^^^ Missing match arm
924     match (false, ((), false)) { (true, _) => (), }
925         //^^^^^^^^^^^^^^^^^^^^ Missing match arm
926
927     match (false, ((), false)) {
928         (true, ((), true)) => (),
929         (true, ((), false)) => (),
930         (false, ((), true)) => (),
931         (false, ((), false)) => (),
932     }
933     match (false, ((), false)) {
934         (true, ((), true)) => (),
935         (true, ((), false)) => (),
936         (false, _) => (),
937     }
938 }
939 "#,
940         );
941     }
942
943     #[test]
944     fn enums() {
945         check_diagnostics(
946             r#"
947 enum Either { A, B, }
948
949 fn main() {
950     match Either::A { }
951         //^^^^^^^^^ Missing match arm
952     match Either::B { Either::A => (), }
953         //^^^^^^^^^ Missing match arm
954
955     match &Either::B {
956         //^^^^^^^^^^ Missing match arm
957         Either::A => (),
958     }
959
960     match Either::B {
961         Either::A => (), Either::B => (),
962     }
963     match &Either::B {
964         Either::A => (), Either::B => (),
965     }
966 }
967 "#,
968         );
969     }
970
971     #[test]
972     fn enum_containing_bool() {
973         check_diagnostics(
974             r#"
975 enum Either { A(bool), B }
976
977 fn main() {
978     match Either::B { }
979         //^^^^^^^^^ Missing match arm
980     match Either::B {
981         //^^^^^^^^^ Missing match arm
982         Either::A(true) => (), Either::B => ()
983     }
984
985     match Either::B {
986         Either::A(true) => (),
987         Either::A(false) => (),
988         Either::B => (),
989     }
990     match Either::B {
991         Either::B => (),
992         _ => (),
993     }
994     match Either::B {
995         Either::A(_) => (),
996         Either::B => (),
997     }
998
999 }
1000         "#,
1001         );
1002     }
1003
1004     #[test]
1005     fn enum_different_sizes() {
1006         check_diagnostics(
1007             r#"
1008 enum Either { A(bool), B(bool, bool) }
1009
1010 fn main() {
1011     match Either::A(false) {
1012         //^^^^^^^^^^^^^^^^ Missing match arm
1013         Either::A(_) => (),
1014         Either::B(false, _) => (),
1015     }
1016
1017     match Either::A(false) {
1018         Either::A(_) => (),
1019         Either::B(true, _) => (),
1020         Either::B(false, _) => (),
1021     }
1022     match Either::A(false) {
1023         Either::A(true) | Either::A(false) => (),
1024         Either::B(true, _) => (),
1025         Either::B(false, _) => (),
1026     }
1027 }
1028 "#,
1029         );
1030     }
1031
1032     #[test]
1033     fn tuple_of_enum_no_diagnostic() {
1034         check_diagnostics(
1035             r#"
1036 enum Either { A(bool), B(bool, bool) }
1037 enum Either2 { C, D }
1038
1039 fn main() {
1040     match (Either::A(false), Either2::C) {
1041         (Either::A(true), _) | (Either::A(false), _) => (),
1042         (Either::B(true, _), Either2::C) => (),
1043         (Either::B(false, _), Either2::C) => (),
1044         (Either::B(_, _), Either2::D) => (),
1045     }
1046 }
1047 "#,
1048         );
1049     }
1050
1051     #[test]
1052     fn or_pattern_no_diagnostic() {
1053         check_diagnostics(
1054             r#"
1055 enum Either {A, B}
1056
1057 fn main() {
1058     match (Either::A, Either::B) {
1059         (Either::A | Either::B, _) => (),
1060     }
1061 }"#,
1062         )
1063     }
1064
1065     #[test]
1066     fn mismatched_types() {
1067         // Match statements with arms that don't match the
1068         // expression pattern do not fire this diagnostic.
1069         check_diagnostics(
1070             r#"
1071 enum Either { A, B }
1072 enum Either2 { C, D }
1073
1074 fn main() {
1075     match Either::A {
1076         Either2::C => (),
1077     //  ^^^^^^^^^^ Internal: match check bailed out
1078         Either2::D => (),
1079     }
1080     match (true, false) {
1081         (true, false, true) => (),
1082     //  ^^^^^^^^^^^^^^^^^^^ Internal: match check bailed out
1083         (true) => (),
1084     }
1085     match (true, false) { (true,) => {} }
1086     //                    ^^^^^^^ Internal: match check bailed out
1087     match (0) { () => () }
1088             //  ^^ Internal: match check bailed out
1089     match Unresolved::Bar { Unresolved::Baz => () }
1090 }
1091         "#,
1092         );
1093     }
1094
1095     #[test]
1096     fn mismatched_types_in_or_patterns() {
1097         check_diagnostics(
1098             r#"
1099 fn main() {
1100     match false { true | () => {} }
1101     //            ^^^^^^^^^ Internal: match check bailed out
1102     match (false,) { (true | (),) => {} }
1103     //               ^^^^^^^^^^^^ Internal: match check bailed out
1104 }
1105 "#,
1106         );
1107     }
1108
1109     #[test]
1110     fn malformed_match_arm_tuple_enum_missing_pattern() {
1111         // We are testing to be sure we don't panic here when the match
1112         // arm `Either::B` is missing its pattern.
1113         check_diagnostics(
1114             r#"
1115 enum Either { A, B(u32) }
1116
1117 fn main() {
1118     match Either::A {
1119         Either::A => (),
1120         Either::B() => (),
1121     }
1122 }
1123 "#,
1124         );
1125     }
1126
1127     #[test]
1128     fn malformed_match_arm_extra_fields() {
1129         check_diagnostics(
1130             r#"
1131 enum A { B(isize, isize), C }
1132 fn main() {
1133     match A::B(1, 2) {
1134         A::B(_, _, _) => (),
1135     //  ^^^^^^^^^^^^^ Internal: match check bailed out
1136     }
1137     match A::B(1, 2) {
1138         A::C(_) => (),
1139     //  ^^^^^^^ Internal: match check bailed out
1140     }
1141 }
1142 "#,
1143         );
1144     }
1145
1146     #[test]
1147     fn expr_diverges() {
1148         check_diagnostics(
1149             r#"
1150 enum Either { A, B }
1151
1152 fn main() {
1153     match loop {} {
1154         Either::A => (),
1155     //  ^^^^^^^^^ Internal: match check bailed out
1156         Either::B => (),
1157     }
1158     match loop {} {
1159         Either::A => (),
1160     //  ^^^^^^^^^ Internal: match check bailed out
1161     }
1162     match loop { break Foo::A } {
1163         //^^^^^^^^^^^^^^^^^^^^^ Missing match arm
1164         Either::A => (),
1165     }
1166     match loop { break Foo::A } {
1167         Either::A => (),
1168         Either::B => (),
1169     }
1170 }
1171 "#,
1172         );
1173     }
1174
1175     #[test]
1176     fn expr_partially_diverges() {
1177         check_diagnostics(
1178             r#"
1179 enum Either<T> { A(T), B }
1180
1181 fn foo() -> Either<!> { Either::B }
1182 fn main() -> u32 {
1183     match foo() {
1184         Either::A(val) => val,
1185         Either::B => 0,
1186     }
1187 }
1188 "#,
1189         );
1190     }
1191
1192     #[test]
1193     fn enum_record() {
1194         check_diagnostics(
1195             r#"
1196 enum Either { A { foo: bool }, B }
1197
1198 fn main() {
1199     let a = Either::A { foo: true };
1200     match a { }
1201         //^ Missing match arm
1202     match a { Either::A { foo: true } => () }
1203         //^ Missing match arm
1204     match a {
1205         Either::A { } => (),
1206       //^^^^^^^^^ Missing structure fields:
1207       //        | - foo
1208         Either::B => (),
1209     }
1210     match a {
1211         //^ Missing match arm
1212         Either::A { } => (),
1213     } //^^^^^^^^^ Missing structure fields:
1214       //        | - foo
1215
1216     match a {
1217         Either::A { foo: true } => (),
1218         Either::A { foo: false } => (),
1219         Either::B => (),
1220     }
1221     match a {
1222         Either::A { foo: _ } => (),
1223         Either::B => (),
1224     }
1225 }
1226 "#,
1227         );
1228     }
1229
1230     #[test]
1231     fn enum_record_fields_out_of_order() {
1232         check_diagnostics(
1233             r#"
1234 enum Either {
1235     A { foo: bool, bar: () },
1236     B,
1237 }
1238
1239 fn main() {
1240     let a = Either::A { foo: true, bar: () };
1241     match a {
1242         //^ Missing match arm
1243         Either::A { bar: (), foo: false } => (),
1244         Either::A { foo: true, bar: () } => (),
1245     }
1246
1247     match a {
1248         Either::A { bar: (), foo: false } => (),
1249         Either::A { foo: true, bar: () } => (),
1250         Either::B => (),
1251     }
1252 }
1253 "#,
1254         );
1255     }
1256
1257     #[test]
1258     fn enum_record_ellipsis() {
1259         check_diagnostics(
1260             r#"
1261 enum Either {
1262     A { foo: bool, bar: bool },
1263     B,
1264 }
1265
1266 fn main() {
1267     let a = Either::B;
1268     match a {
1269         //^ Missing match arm
1270         Either::A { foo: true, .. } => (),
1271         Either::B => (),
1272     }
1273     match a {
1274         //^ Missing match arm
1275         Either::A { .. } => (),
1276     }
1277
1278     match a {
1279         Either::A { foo: true, .. } => (),
1280         Either::A { foo: false, .. } => (),
1281         Either::B => (),
1282     }
1283
1284     match a {
1285         Either::A { .. } => (),
1286         Either::B => (),
1287     }
1288 }
1289 "#,
1290         );
1291     }
1292
1293     #[test]
1294     fn enum_tuple_partial_ellipsis() {
1295         check_diagnostics(
1296             r#"
1297 enum Either {
1298     A(bool, bool, bool, bool),
1299     B,
1300 }
1301
1302 fn main() {
1303     match Either::B {
1304         //^^^^^^^^^ Missing match arm
1305         Either::A(true, .., true) => (),
1306         Either::A(true, .., false) => (),
1307         Either::A(false, .., false) => (),
1308         Either::B => (),
1309     }
1310     match Either::B {
1311         //^^^^^^^^^ Missing match arm
1312         Either::A(true, .., true) => (),
1313         Either::A(true, .., false) => (),
1314         Either::A(.., true) => (),
1315         Either::B => (),
1316     }
1317
1318     match Either::B {
1319         Either::A(true, .., true) => (),
1320         Either::A(true, .., false) => (),
1321         Either::A(false, .., true) => (),
1322         Either::A(false, .., false) => (),
1323         Either::B => (),
1324     }
1325     match Either::B {
1326         Either::A(true, .., true) => (),
1327         Either::A(true, .., false) => (),
1328         Either::A(.., true) => (),
1329         Either::A(.., false) => (),
1330         Either::B => (),
1331     }
1332 }
1333 "#,
1334         );
1335     }
1336
1337     #[test]
1338     fn never() {
1339         check_diagnostics(
1340             r#"
1341 enum Never {}
1342
1343 fn enum_(never: Never) {
1344     match never {}
1345 }
1346 fn enum_ref(never: &Never) {
1347     match never {}
1348         //^^^^^ Missing match arm
1349 }
1350 fn bang(never: !) {
1351     match never {}
1352 }
1353 "#,
1354         );
1355     }
1356
1357     #[test]
1358     fn unknown_type() {
1359         check_diagnostics(
1360             r#"
1361 enum Option<T> { Some(T), None }
1362
1363 fn main() {
1364     // `Never` is deliberately not defined so that it's an uninferred type.
1365     match Option::<Never>::None {
1366         None => (),
1367         Some(never) => match never {},
1368     //  ^^^^^^^^^^^ Internal: match check bailed out
1369     }
1370     match Option::<Never>::None {
1371         //^^^^^^^^^^^^^^^^^^^^^ Missing match arm
1372         Option::Some(_never) => {},
1373     }
1374 }
1375 "#,
1376         );
1377     }
1378
1379     #[test]
1380     fn tuple_of_bools_with_ellipsis_at_end_missing_arm() {
1381         check_diagnostics(
1382             r#"
1383 fn main() {
1384     match (false, true, false) {
1385         //^^^^^^^^^^^^^^^^^^^^ Missing match arm
1386         (false, ..) => (),
1387     }
1388 }"#,
1389         );
1390     }
1391
1392     #[test]
1393     fn tuple_of_bools_with_ellipsis_at_beginning_missing_arm() {
1394         check_diagnostics(
1395             r#"
1396 fn main() {
1397     match (false, true, false) {
1398         //^^^^^^^^^^^^^^^^^^^^ Missing match arm
1399         (.., false) => (),
1400     }
1401 }"#,
1402         );
1403     }
1404
1405     #[test]
1406     fn tuple_of_bools_with_ellipsis_in_middle_missing_arm() {
1407         check_diagnostics(
1408             r#"
1409 fn main() {
1410     match (false, true, false) {
1411         //^^^^^^^^^^^^^^^^^^^^ Missing match arm
1412         (true, .., false) => (),
1413     }
1414 }"#,
1415         );
1416     }
1417
1418     #[test]
1419     fn record_struct() {
1420         check_diagnostics(
1421             r#"struct Foo { a: bool }
1422 fn main(f: Foo) {
1423     match f {}
1424         //^ Missing match arm
1425     match f { Foo { a: true } => () }
1426         //^ Missing match arm
1427     match &f { Foo { a: true } => () }
1428         //^^ Missing match arm
1429     match f { Foo { a: _ } => () }
1430     match f {
1431         Foo { a: true } => (),
1432         Foo { a: false } => (),
1433     }
1434     match &f {
1435         Foo { a: true } => (),
1436         Foo { a: false } => (),
1437     }
1438 }
1439 "#,
1440         );
1441     }
1442
1443     #[test]
1444     fn tuple_struct() {
1445         check_diagnostics(
1446             r#"struct Foo(bool);
1447 fn main(f: Foo) {
1448     match f {}
1449         //^ Missing match arm
1450     match f { Foo(true) => () }
1451         //^ Missing match arm
1452     match f {
1453         Foo(true) => (),
1454         Foo(false) => (),
1455     }
1456 }
1457 "#,
1458         );
1459     }
1460
1461     #[test]
1462     fn unit_struct() {
1463         check_diagnostics(
1464             r#"struct Foo;
1465 fn main(f: Foo) {
1466     match f {}
1467         //^ Missing match arm
1468     match f { Foo => () }
1469 }
1470 "#,
1471         );
1472     }
1473
1474     #[test]
1475     fn record_struct_ellipsis() {
1476         check_diagnostics(
1477             r#"struct Foo { foo: bool, bar: bool }
1478 fn main(f: Foo) {
1479     match f { Foo { foo: true, .. } => () }
1480         //^ Missing match arm
1481     match f {
1482         //^ Missing match arm
1483         Foo { foo: true, .. } => (),
1484         Foo { bar: false, .. } => ()
1485     }
1486     match f { Foo { .. } => () }
1487     match f {
1488         Foo { foo: true, .. } => (),
1489         Foo { foo: false, .. } => ()
1490     }
1491 }
1492 "#,
1493         );
1494     }
1495
1496     #[test]
1497     fn internal_or() {
1498         check_diagnostics(
1499             r#"
1500 fn main() {
1501     enum Either { A(bool), B }
1502     match Either::B {
1503         //^^^^^^^^^ Missing match arm
1504         Either::A(true | false) => (),
1505     }
1506 }
1507 "#,
1508         );
1509     }
1510
1511     #[test]
1512     fn no_panic_at_unimplemented_subpattern_type() {
1513         check_diagnostics(
1514             r#"
1515 struct S { a: char}
1516 fn main(v: S) {
1517     match v { S{ a }      => {} }
1518     match v { S{ a: _x }  => {} }
1519     match v { S{ a: 'a' } => {} }
1520             //^^^^^^^^^^^ Internal: match check bailed out
1521     match v { S{..}       => {} }
1522     match v { _           => {} }
1523     match v { }
1524         //^ Missing match arm
1525 }
1526 "#,
1527         );
1528     }
1529
1530     #[test]
1531     fn binding() {
1532         check_diagnostics(
1533             r#"
1534 fn main() {
1535     match true {
1536         _x @ true => {}
1537         false     => {}
1538     }
1539     match true { _x @ true => {} }
1540         //^^^^ Missing match arm
1541 }
1542 "#,
1543         );
1544     }
1545
1546     #[test]
1547     fn binding_ref_has_correct_type() {
1548         // Asserts `PatKind::Binding(ref _x): bool`, not &bool.
1549         // If that's not true match checking will panic with "incompatible constructors"
1550         // FIXME: make facilities to test this directly like `tests::check_infer(..)`
1551         check_diagnostics(
1552             r#"
1553 enum Foo { A }
1554 fn main() {
1555     // FIXME: this should not bail out but current behavior is such as the old algorithm.
1556     // ExprValidator::validate_match(..) checks types of top level patterns incorrecly.
1557     match Foo::A {
1558         ref _x => {}
1559     //  ^^^^^^ Internal: match check bailed out
1560         Foo::A => {}
1561     }
1562     match (true,) {
1563         (ref _x,) => {}
1564         (true,) => {}
1565     }
1566 }
1567 "#,
1568         );
1569     }
1570
1571     #[test]
1572     fn enum_non_exhaustive() {
1573         check_diagnostics(
1574             r#"
1575 //- /lib.rs crate:lib
1576 #[non_exhaustive]
1577 pub enum E { A, B }
1578 fn _local() {
1579     match E::A { _ => {} }
1580     match E::A {
1581         E::A => {}
1582         E::B => {}
1583     }
1584     match E::A {
1585         E::A | E::B => {}
1586     }
1587 }
1588
1589 //- /main.rs crate:main deps:lib
1590 use lib::E;
1591 fn main() {
1592     match E::A { _ => {} }
1593     match E::A {
1594         //^^^^ Missing match arm
1595         E::A => {}
1596         E::B => {}
1597     }
1598     match E::A {
1599         //^^^^ Missing match arm
1600         E::A | E::B => {}
1601     }
1602 }
1603 "#,
1604         );
1605     }
1606
1607     #[test]
1608     fn match_guard() {
1609         check_diagnostics(
1610             r#"
1611 fn main() {
1612     match true {
1613         true if false => {}
1614         true          => {}
1615         false         => {}
1616     }
1617     match true {
1618         //^^^^ Missing match arm
1619         true if false => {}
1620         false         => {}
1621     }
1622 }
1623 "#,
1624         );
1625     }
1626
1627     #[test]
1628     fn pattern_type_is_of_substitution() {
1629         cov_mark::check!(match_check_wildcard_expanded_to_substitutions);
1630         check_diagnostics(
1631             r#"
1632 struct Foo<T>(T);
1633 struct Bar;
1634 fn main() {
1635     match Foo(Bar) {
1636         _ | Foo(Bar) => {}
1637     }
1638 }
1639 "#,
1640         );
1641     }
1642
1643     #[test]
1644     fn record_struct_no_such_field() {
1645         check_diagnostics(
1646             r#"
1647 struct Foo { }
1648 fn main(f: Foo) {
1649     match f { Foo { bar } => () }
1650     //        ^^^^^^^^^^^ Internal: match check bailed out
1651 }
1652 "#,
1653         );
1654     }
1655
1656     #[test]
1657     fn match_ergonomics_issue_9095() {
1658         check_diagnostics(
1659             r#"
1660 enum Foo<T> { A(T) }
1661 fn main() {
1662     match &Foo::A(true) {
1663         _ => {}
1664         Foo::A(_) => {}
1665     }
1666 }
1667 "#,
1668         );
1669     }
1670
1671     mod false_negatives {
1672         //! The implementation of match checking here is a work in progress. As we roll this out, we
1673         //! prefer false negatives to false positives (ideally there would be no false positives). This
1674         //! test module should document known false negatives. Eventually we will have a complete
1675         //! implementation of match checking and this module will be empty.
1676         //!
1677         //! The reasons for documenting known false negatives:
1678         //!
1679         //!   1. It acts as a backlog of work that can be done to improve the behavior of the system.
1680         //!   2. It ensures the code doesn't panic when handling these cases.
1681         use super::*;
1682
1683         #[test]
1684         fn integers() {
1685             // We don't currently check integer exhaustiveness.
1686             check_diagnostics(
1687                 r#"
1688 fn main() {
1689     match 5 {
1690         10 => (),
1691     //  ^^ Internal: match check bailed out
1692         11..20 => (),
1693     }
1694 }
1695 "#,
1696             );
1697         }
1698
1699         #[test]
1700         fn reference_patterns_at_top_level() {
1701             check_diagnostics(
1702                 r#"
1703 fn main() {
1704     match &false {
1705         &true => {}
1706     //  ^^^^^ Internal: match check bailed out
1707     }
1708 }
1709             "#,
1710             );
1711         }
1712
1713         #[test]
1714         fn reference_patterns_in_fields() {
1715             check_diagnostics(
1716                 r#"
1717 fn main() {
1718     match (&false,) {
1719         (true,) => {}
1720     //  ^^^^^^^ Internal: match check bailed out
1721     }
1722     match (&false,) {
1723         (&true,) => {}
1724     //  ^^^^^^^^ Internal: match check bailed out
1725     }
1726 }
1727             "#,
1728             );
1729         }
1730     }
1731 }
1732
1733 #[cfg(test)]
1734 mod decl_check_tests {
1735     use crate::diagnostics::tests::check_diagnostics;
1736
1737     #[test]
1738     fn incorrect_function_name() {
1739         check_diagnostics(
1740             r#"
1741 fn NonSnakeCaseName() {}
1742 // ^^^^^^^^^^^^^^^^ Function `NonSnakeCaseName` should have snake_case name, e.g. `non_snake_case_name`
1743 "#,
1744         );
1745     }
1746
1747     #[test]
1748     fn incorrect_function_params() {
1749         check_diagnostics(
1750             r#"
1751 fn foo(SomeParam: u8) {}
1752     // ^^^^^^^^^ Parameter `SomeParam` should have snake_case name, e.g. `some_param`
1753
1754 fn foo2(ok_param: &str, CAPS_PARAM: u8) {}
1755                      // ^^^^^^^^^^ Parameter `CAPS_PARAM` should have snake_case name, e.g. `caps_param`
1756 "#,
1757         );
1758     }
1759
1760     #[test]
1761     fn incorrect_variable_names() {
1762         check_diagnostics(
1763             r#"
1764 fn foo() {
1765     let SOME_VALUE = 10;
1766      // ^^^^^^^^^^ Variable `SOME_VALUE` should have snake_case name, e.g. `some_value`
1767     let AnotherValue = 20;
1768      // ^^^^^^^^^^^^ Variable `AnotherValue` should have snake_case name, e.g. `another_value`
1769 }
1770 "#,
1771         );
1772     }
1773
1774     #[test]
1775     fn incorrect_struct_names() {
1776         check_diagnostics(
1777             r#"
1778 struct non_camel_case_name {}
1779     // ^^^^^^^^^^^^^^^^^^^ Structure `non_camel_case_name` should have CamelCase name, e.g. `NonCamelCaseName`
1780
1781 struct SCREAMING_CASE {}
1782     // ^^^^^^^^^^^^^^ Structure `SCREAMING_CASE` should have CamelCase name, e.g. `ScreamingCase`
1783 "#,
1784         );
1785     }
1786
1787     #[test]
1788     fn no_diagnostic_for_camel_cased_acronyms_in_struct_name() {
1789         check_diagnostics(
1790             r#"
1791 struct AABB {}
1792 "#,
1793         );
1794     }
1795
1796     #[test]
1797     fn incorrect_struct_field() {
1798         check_diagnostics(
1799             r#"
1800 struct SomeStruct { SomeField: u8 }
1801                  // ^^^^^^^^^ Field `SomeField` should have snake_case name, e.g. `some_field`
1802 "#,
1803         );
1804     }
1805
1806     #[test]
1807     fn incorrect_enum_names() {
1808         check_diagnostics(
1809             r#"
1810 enum some_enum { Val(u8) }
1811   // ^^^^^^^^^ Enum `some_enum` should have CamelCase name, e.g. `SomeEnum`
1812
1813 enum SOME_ENUM {}
1814   // ^^^^^^^^^ Enum `SOME_ENUM` should have CamelCase name, e.g. `SomeEnum`
1815 "#,
1816         );
1817     }
1818
1819     #[test]
1820     fn no_diagnostic_for_camel_cased_acronyms_in_enum_name() {
1821         check_diagnostics(
1822             r#"
1823 enum AABB {}
1824 "#,
1825         );
1826     }
1827
1828     #[test]
1829     fn incorrect_enum_variant_name() {
1830         check_diagnostics(
1831             r#"
1832 enum SomeEnum { SOME_VARIANT(u8) }
1833              // ^^^^^^^^^^^^ Variant `SOME_VARIANT` should have CamelCase name, e.g. `SomeVariant`
1834 "#,
1835         );
1836     }
1837
1838     #[test]
1839     fn incorrect_const_name() {
1840         check_diagnostics(
1841             r#"
1842 const some_weird_const: u8 = 10;
1843    // ^^^^^^^^^^^^^^^^ Constant `some_weird_const` should have UPPER_SNAKE_CASE name, e.g. `SOME_WEIRD_CONST`
1844 "#,
1845         );
1846     }
1847
1848     #[test]
1849     fn incorrect_static_name() {
1850         check_diagnostics(
1851             r#"
1852 static some_weird_const: u8 = 10;
1853     // ^^^^^^^^^^^^^^^^ Static variable `some_weird_const` should have UPPER_SNAKE_CASE name, e.g. `SOME_WEIRD_CONST`
1854 "#,
1855         );
1856     }
1857
1858     #[test]
1859     fn fn_inside_impl_struct() {
1860         check_diagnostics(
1861             r#"
1862 struct someStruct;
1863     // ^^^^^^^^^^ Structure `someStruct` should have CamelCase name, e.g. `SomeStruct`
1864
1865 impl someStruct {
1866     fn SomeFunc(&self) {
1867     // ^^^^^^^^ Function `SomeFunc` should have snake_case name, e.g. `some_func`
1868         let WHY_VAR_IS_CAPS = 10;
1869          // ^^^^^^^^^^^^^^^ Variable `WHY_VAR_IS_CAPS` should have snake_case name, e.g. `why_var_is_caps`
1870     }
1871 }
1872 "#,
1873         );
1874     }
1875
1876     #[test]
1877     fn no_diagnostic_for_enum_varinats() {
1878         check_diagnostics(
1879             r#"
1880 enum Option { Some, None }
1881
1882 fn main() {
1883     match Option::None {
1884         None => (),
1885         Some => (),
1886     }
1887 }
1888 "#,
1889         );
1890     }
1891
1892     #[test]
1893     fn non_let_bind() {
1894         check_diagnostics(
1895             r#"
1896 enum Option { Some, None }
1897
1898 fn main() {
1899     match Option::None {
1900         SOME_VAR @ None => (),
1901      // ^^^^^^^^ Variable `SOME_VAR` should have snake_case name, e.g. `some_var`
1902         Some => (),
1903     }
1904 }
1905 "#,
1906         );
1907     }
1908
1909     #[test]
1910     fn allow_attributes() {
1911         check_diagnostics(
1912             r#"
1913 #[allow(non_snake_case)]
1914 fn NonSnakeCaseName(SOME_VAR: u8) -> u8{
1915     // cov_flags generated output from elsewhere in this file
1916     extern "C" {
1917         #[no_mangle]
1918         static lower_case: u8;
1919     }
1920
1921     let OtherVar = SOME_VAR + 1;
1922     OtherVar
1923 }
1924
1925 #[allow(nonstandard_style)]
1926 mod CheckNonstandardStyle {
1927     fn HiImABadFnName() {}
1928 }
1929
1930 #[allow(bad_style)]
1931 mod CheckBadStyle {
1932     fn HiImABadFnName() {}
1933 }
1934
1935 mod F {
1936     #![allow(non_snake_case)]
1937     fn CheckItWorksWithModAttr(BAD_NAME_HI: u8) {}
1938 }
1939
1940 #[allow(non_snake_case, non_camel_case_types)]
1941 pub struct some_type {
1942     SOME_FIELD: u8,
1943     SomeField: u16,
1944 }
1945
1946 #[allow(non_upper_case_globals)]
1947 pub const some_const: u8 = 10;
1948
1949 #[allow(non_upper_case_globals)]
1950 pub static SomeStatic: u8 = 10;
1951     "#,
1952         );
1953     }
1954
1955     #[test]
1956     fn allow_attributes_crate_attr() {
1957         check_diagnostics(
1958             r#"
1959 #![allow(non_snake_case)]
1960
1961 mod F {
1962     fn CheckItWorksWithCrateAttr(BAD_NAME_HI: u8) {}
1963 }
1964     "#,
1965         );
1966     }
1967
1968     #[test]
1969     #[ignore]
1970     fn bug_trait_inside_fn() {
1971         // FIXME:
1972         // This is broken, and in fact, should not even be looked at by this
1973         // lint in the first place. There's weird stuff going on in the
1974         // collection phase.
1975         // It's currently being brought in by:
1976         // * validate_func on `a` recursing into modules
1977         // * then it finds the trait and then the function while iterating
1978         //   through modules
1979         // * then validate_func is called on Dirty
1980         // * ... which then proceeds to look at some unknown module taking no
1981         //   attrs from either the impl or the fn a, and then finally to the root
1982         //   module
1983         //
1984         // It should find the attribute on the trait, but it *doesn't even see
1985         // the trait* as far as I can tell.
1986
1987         check_diagnostics(
1988             r#"
1989 trait T { fn a(); }
1990 struct U {}
1991 impl T for U {
1992     fn a() {
1993         // this comes out of bitflags, mostly
1994         #[allow(non_snake_case)]
1995         trait __BitFlags {
1996             const HiImAlsoBad: u8 = 2;
1997             #[inline]
1998             fn Dirty(&self) -> bool {
1999                 false
2000             }
2001         }
2002
2003     }
2004 }
2005     "#,
2006         );
2007     }
2008
2009     #[test]
2010     #[ignore]
2011     fn bug_traits_arent_checked() {
2012         // FIXME: Traits and functions in traits aren't currently checked by
2013         // r-a, even though rustc will complain about them.
2014         check_diagnostics(
2015             r#"
2016 trait BAD_TRAIT {
2017     // ^^^^^^^^^ Trait `BAD_TRAIT` should have CamelCase name, e.g. `BadTrait`
2018     fn BAD_FUNCTION();
2019     // ^^^^^^^^^^^^ Function `BAD_FUNCTION` should have snake_case name, e.g. `bad_function`
2020     fn BadFunction();
2021     // ^^^^^^^^^^^^ Function `BadFunction` should have snake_case name, e.g. `bad_function`
2022 }
2023     "#,
2024         );
2025     }
2026
2027     #[test]
2028     fn ignores_extern_items() {
2029         cov_mark::check!(extern_func_incorrect_case_ignored);
2030         cov_mark::check!(extern_static_incorrect_case_ignored);
2031         check_diagnostics(
2032             r#"
2033 extern {
2034     fn NonSnakeCaseName(SOME_VAR: u8) -> u8;
2035     pub static SomeStatic: u8 = 10;
2036 }
2037             "#,
2038         );
2039     }
2040
2041     #[test]
2042     fn infinite_loop_inner_items() {
2043         check_diagnostics(
2044             r#"
2045 fn qualify() {
2046     mod foo {
2047         use super::*;
2048     }
2049 }
2050             "#,
2051         )
2052     }
2053
2054     #[test] // Issue #8809.
2055     fn parenthesized_parameter() {
2056         check_diagnostics(r#"fn f((O): _) {}"#)
2057     }
2058 }