aboutsummaryrefslogtreecommitdiff
path: root/crates/hir_ty/src/diagnostics.rs
diff options
context:
space:
mode:
Diffstat (limited to 'crates/hir_ty/src/diagnostics.rs')
-rw-r--r--crates/hir_ty/src/diagnostics.rs177
1 files changed, 170 insertions, 7 deletions
diff --git a/crates/hir_ty/src/diagnostics.rs b/crates/hir_ty/src/diagnostics.rs
index c67a289f2..6bca7aa0d 100644
--- a/crates/hir_ty/src/diagnostics.rs
+++ b/crates/hir_ty/src/diagnostics.rs
@@ -247,7 +247,7 @@ impl Diagnostic for RemoveThisSemicolon {
247 247
248// Diagnostic: break-outside-of-loop 248// Diagnostic: break-outside-of-loop
249// 249//
250// This diagnostic is triggered if `break` keyword is used outside of a loop. 250// This diagnostic is triggered if the `break` keyword is used outside of a loop.
251#[derive(Debug)] 251#[derive(Debug)]
252pub struct BreakOutsideOfLoop { 252pub struct BreakOutsideOfLoop {
253 pub file: HirFileId, 253 pub file: HirFileId,
@@ -271,7 +271,7 @@ impl Diagnostic for BreakOutsideOfLoop {
271 271
272// Diagnostic: missing-unsafe 272// Diagnostic: missing-unsafe
273// 273//
274// This diagnostic is triggered if operation marked as `unsafe` is used outside of `unsafe` function or block. 274// This diagnostic is triggered if an operation marked as `unsafe` is used outside of an `unsafe` function or block.
275#[derive(Debug)] 275#[derive(Debug)]
276pub struct MissingUnsafe { 276pub struct MissingUnsafe {
277 pub file: HirFileId, 277 pub file: HirFileId,
@@ -295,7 +295,7 @@ impl Diagnostic for MissingUnsafe {
295 295
296// Diagnostic: mismatched-arg-count 296// Diagnostic: mismatched-arg-count
297// 297//
298// This diagnostic is triggered if function is invoked with an incorrect amount of arguments. 298// This diagnostic is triggered if a function is invoked with an incorrect amount of arguments.
299#[derive(Debug)] 299#[derive(Debug)]
300pub struct MismatchedArgCount { 300pub struct MismatchedArgCount {
301 pub file: HirFileId, 301 pub file: HirFileId,
@@ -345,15 +345,46 @@ impl fmt::Display for CaseType {
345 } 345 }
346} 346}
347 347
348#[derive(Debug)]
349pub enum IdentType {
350 Argument,
351 Constant,
352 Enum,
353 Field,
354 Function,
355 StaticVariable,
356 Structure,
357 Variable,
358 Variant,
359}
360
361impl fmt::Display for IdentType {
362 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
363 let repr = match self {
364 IdentType::Argument => "Argument",
365 IdentType::Constant => "Constant",
366 IdentType::Enum => "Enum",
367 IdentType::Field => "Field",
368 IdentType::Function => "Function",
369 IdentType::StaticVariable => "Static variable",
370 IdentType::Structure => "Structure",
371 IdentType::Variable => "Variable",
372 IdentType::Variant => "Variant",
373 };
374
375 write!(f, "{}", repr)
376 }
377}
378
348// Diagnostic: incorrect-ident-case 379// Diagnostic: incorrect-ident-case
349// 380//
350// This diagnostic is triggered if item name doesn't follow https://doc.rust-lang.org/1.0.0/style/style/naming/README.html[Rust naming convention]. 381// This diagnostic is triggered if an item name doesn't follow https://doc.rust-lang.org/1.0.0/style/style/naming/README.html[Rust naming convention].
351#[derive(Debug)] 382#[derive(Debug)]
352pub struct IncorrectCase { 383pub struct IncorrectCase {
353 pub file: HirFileId, 384 pub file: HirFileId,
354 pub ident: AstPtr<ast::Name>, 385 pub ident: AstPtr<ast::Name>,
355 pub expected_case: CaseType, 386 pub expected_case: CaseType,
356 pub ident_type: String, 387 pub ident_type: IdentType,
357 pub ident_text: String, 388 pub ident_text: String,
358 pub suggested_text: String, 389 pub suggested_text: String,
359} 390}
@@ -386,6 +417,31 @@ impl Diagnostic for IncorrectCase {
386 } 417 }
387} 418}
388 419
420// Diagnostic: replace-filter-map-next-with-find-map
421//
422// This diagnostic is triggered when `.filter_map(..).next()` is used, rather than the more concise `.find_map(..)`.
423#[derive(Debug)]
424pub struct ReplaceFilterMapNextWithFindMap {
425 pub file: HirFileId,
426 /// This expression is the whole method chain up to and including `.filter_map(..).next()`.
427 pub next_expr: AstPtr<ast::Expr>,
428}
429
430impl Diagnostic for ReplaceFilterMapNextWithFindMap {
431 fn code(&self) -> DiagnosticCode {
432 DiagnosticCode("replace-filter-map-next-with-find-map")
433 }
434 fn message(&self) -> String {
435 "replace filter_map(..).next() with find_map(..)".to_string()
436 }
437 fn display_source(&self) -> InFile<SyntaxNodePtr> {
438 InFile { file_id: self.file, value: self.next_expr.clone().into() }
439 }
440 fn as_any(&self) -> &(dyn Any + Send + 'static) {
441 self
442 }
443}
444
389#[cfg(test)] 445#[cfg(test)]
390mod tests { 446mod tests {
391 use base_db::{fixture::WithFixture, FileId, SourceDatabase, SourceDatabaseExt}; 447 use base_db::{fixture::WithFixture, FileId, SourceDatabase, SourceDatabaseExt};
@@ -409,7 +465,7 @@ mod tests {
409 let crate_def_map = self.crate_def_map(krate); 465 let crate_def_map = self.crate_def_map(krate);
410 466
411 let mut fns = Vec::new(); 467 let mut fns = Vec::new();
412 for (module_id, _) in crate_def_map.modules.iter() { 468 for (module_id, _) in crate_def_map.modules() {
413 for decl in crate_def_map[module_id].scope.declarations() { 469 for decl in crate_def_map[module_id].scope.declarations() {
414 let mut sink = DiagnosticSinkBuilder::new().build(&mut cb); 470 let mut sink = DiagnosticSinkBuilder::new().build(&mut cb);
415 validate_module_item(self, krate, decl, &mut sink); 471 validate_module_item(self, krate, decl, &mut sink);
@@ -455,7 +511,7 @@ mod tests {
455 // FIXME: macros... 511 // FIXME: macros...
456 let file_id = src.file_id.original_file(&db); 512 let file_id = src.file_id.original_file(&db);
457 let range = src.value.to_node(&root).text_range(); 513 let range = src.value.to_node(&root).text_range();
458 let message = d.message().to_owned(); 514 let message = d.message();
459 actual.entry(file_id).or_default().push((range, message)); 515 actual.entry(file_id).or_default().push((range, message));
460 }); 516 });
461 517
@@ -626,6 +682,30 @@ fn baz(s: S) -> i32 {
626 } 682 }
627 683
628 #[test] 684 #[test]
685 fn missing_record_pat_field_box() {
686 check_diagnostics(
687 r"
688struct S { s: Box<u32> }
689fn x(a: S) {
690 let S { box s } = a;
691}
692",
693 )
694 }
695
696 #[test]
697 fn missing_record_pat_field_ref() {
698 check_diagnostics(
699 r"
700struct S { s: u32 }
701fn x(a: S) {
702 let S { ref s } = a;
703}
704",
705 )
706 }
707
708 #[test]
629 fn break_outside_of_loop() { 709 fn break_outside_of_loop() {
630 check_diagnostics( 710 check_diagnostics(
631 r#" 711 r#"
@@ -644,4 +724,87 @@ fn foo() { break; }
644 "#, 724 "#,
645 ); 725 );
646 } 726 }
727
728 // Register the required standard library types to make the tests work
729 fn add_filter_map_with_find_next_boilerplate(body: &str) -> String {
730 let prefix = r#"
731 //- /main.rs crate:main deps:core
732 use core::iter::Iterator;
733 use core::option::Option::{self, Some, None};
734 "#;
735 let suffix = r#"
736 //- /core/lib.rs crate:core
737 pub mod option {
738 pub enum Option<T> { Some(T), None }
739 }
740 pub mod iter {
741 pub trait Iterator {
742 type Item;
743 fn filter_map<B, F>(self, f: F) -> FilterMap where F: FnMut(Self::Item) -> Option<B> { FilterMap }
744 fn next(&mut self) -> Option<Self::Item>;
745 }
746 pub struct FilterMap {}
747 impl Iterator for FilterMap {
748 type Item = i32;
749 fn next(&mut self) -> i32 { 7 }
750 }
751 }
752 "#;
753 format!("{}{}{}", prefix, body, suffix)
754 }
755
756 #[test]
757 fn replace_filter_map_next_with_find_map2() {
758 check_diagnostics(&add_filter_map_with_find_next_boilerplate(
759 r#"
760 fn foo() {
761 let m = [1, 2, 3].iter().filter_map(|x| if *x == 2 { Some (4) } else { None }).next();
762 //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ replace filter_map(..).next() with find_map(..)
763 }
764 "#,
765 ));
766 }
767
768 #[test]
769 fn replace_filter_map_next_with_find_map_no_diagnostic_without_next() {
770 check_diagnostics(&add_filter_map_with_find_next_boilerplate(
771 r#"
772 fn foo() {
773 let m = [1, 2, 3]
774 .iter()
775 .filter_map(|x| if *x == 2 { Some (4) } else { None })
776 .len();
777 }
778 "#,
779 ));
780 }
781
782 #[test]
783 fn replace_filter_map_next_with_find_map_no_diagnostic_with_intervening_methods() {
784 check_diagnostics(&add_filter_map_with_find_next_boilerplate(
785 r#"
786 fn foo() {
787 let m = [1, 2, 3]
788 .iter()
789 .filter_map(|x| if *x == 2 { Some (4) } else { None })
790 .map(|x| x + 2)
791 .len();
792 }
793 "#,
794 ));
795 }
796
797 #[test]
798 fn replace_filter_map_next_with_find_map_no_diagnostic_if_not_in_chain() {
799 check_diagnostics(&add_filter_map_with_find_next_boilerplate(
800 r#"
801 fn foo() {
802 let m = [1, 2, 3]
803 .iter()
804 .filter_map(|x| if *x == 2 { Some (4) } else { None });
805 let n = m.next();
806 }
807 "#,
808 ));
809 }
647} 810}