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.rs116
1 files changed, 112 insertions, 4 deletions
diff --git a/crates/hir_ty/src/diagnostics.rs b/crates/hir_ty/src/diagnostics.rs
index 247da43f2..323c5f963 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,
@@ -347,7 +347,7 @@ impl fmt::Display for CaseType {
347 347
348// Diagnostic: incorrect-ident-case 348// Diagnostic: incorrect-ident-case
349// 349//
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]. 350// 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)] 351#[derive(Debug)]
352pub struct IncorrectCase { 352pub struct IncorrectCase {
353 pub file: HirFileId, 353 pub file: HirFileId,
@@ -386,6 +386,31 @@ impl Diagnostic for IncorrectCase {
386 } 386 }
387} 387}
388 388
389// Diagnostic: replace-filter-map-next-with-find-map
390//
391// This diagnostic is triggered when `.filter_map(..).next()` is used, rather than the more concise `.find_map(..)`.
392#[derive(Debug)]
393pub struct ReplaceFilterMapNextWithFindMap {
394 pub file: HirFileId,
395 /// This expression is the whole method chain up to and including `.filter_map(..).next()`.
396 pub next_expr: AstPtr<ast::Expr>,
397}
398
399impl Diagnostic for ReplaceFilterMapNextWithFindMap {
400 fn code(&self) -> DiagnosticCode {
401 DiagnosticCode("replace-filter-map-next-with-find-map")
402 }
403 fn message(&self) -> String {
404 "replace filter_map(..).next() with find_map(..)".to_string()
405 }
406 fn display_source(&self) -> InFile<SyntaxNodePtr> {
407 InFile { file_id: self.file, value: self.next_expr.clone().into() }
408 }
409 fn as_any(&self) -> &(dyn Any + Send + 'static) {
410 self
411 }
412}
413
389#[cfg(test)] 414#[cfg(test)]
390mod tests { 415mod tests {
391 use base_db::{fixture::WithFixture, FileId, SourceDatabase, SourceDatabaseExt}; 416 use base_db::{fixture::WithFixture, FileId, SourceDatabase, SourceDatabaseExt};
@@ -644,4 +669,87 @@ fn foo() { break; }
644 "#, 669 "#,
645 ); 670 );
646 } 671 }
672
673 // Register the required standard library types to make the tests work
674 fn add_filter_map_with_find_next_boilerplate(body: &str) -> String {
675 let prefix = r#"
676 //- /main.rs crate:main deps:core
677 use core::iter::Iterator;
678 use core::option::Option::{self, Some, None};
679 "#;
680 let suffix = r#"
681 //- /core/lib.rs crate:core
682 pub mod option {
683 pub enum Option<T> { Some(T), None }
684 }
685 pub mod iter {
686 pub trait Iterator {
687 type Item;
688 fn filter_map<B, F>(self, f: F) -> FilterMap where F: FnMut(Self::Item) -> Option<B> { FilterMap }
689 fn next(&mut self) -> Option<Self::Item>;
690 }
691 pub struct FilterMap {}
692 impl Iterator for FilterMap {
693 type Item = i32;
694 fn next(&mut self) -> i32 { 7 }
695 }
696 }
697 "#;
698 format!("{}{}{}", prefix, body, suffix)
699 }
700
701 #[test]
702 fn replace_filter_map_next_with_find_map2() {
703 check_diagnostics(&add_filter_map_with_find_next_boilerplate(
704 r#"
705 fn foo() {
706 let m = [1, 2, 3].iter().filter_map(|x| if *x == 2 { Some (4) } else { None }).next();
707 //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ replace filter_map(..).next() with find_map(..)
708 }
709 "#,
710 ));
711 }
712
713 #[test]
714 fn replace_filter_map_next_with_find_map_no_diagnostic_without_next() {
715 check_diagnostics(&add_filter_map_with_find_next_boilerplate(
716 r#"
717 fn foo() {
718 let m = [1, 2, 3]
719 .iter()
720 .filter_map(|x| if *x == 2 { Some (4) } else { None })
721 .len();
722 }
723 "#,
724 ));
725 }
726
727 #[test]
728 fn replace_filter_map_next_with_find_map_no_diagnostic_with_intervening_methods() {
729 check_diagnostics(&add_filter_map_with_find_next_boilerplate(
730 r#"
731 fn foo() {
732 let m = [1, 2, 3]
733 .iter()
734 .filter_map(|x| if *x == 2 { Some (4) } else { None })
735 .map(|x| x + 2)
736 .len();
737 }
738 "#,
739 ));
740 }
741
742 #[test]
743 fn replace_filter_map_next_with_find_map_no_diagnostic_if_not_in_chain() {
744 check_diagnostics(&add_filter_map_with_find_next_boilerplate(
745 r#"
746 fn foo() {
747 let m = [1, 2, 3]
748 .iter()
749 .filter_map(|x| if *x == 2 { Some (4) } else { None });
750 let n = m.next();
751 }
752 "#,
753 ));
754 }
647} 755}