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