aboutsummaryrefslogtreecommitdiff
path: root/crates/ide/src/diagnostics/fixes/replace_with_find_map.rs
blob: b0ef7b44adc306a785bec0e28aeb9b24db8af9a1 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
use hir::{db::AstDatabase, diagnostics::ReplaceFilterMapNextWithFindMap, Semantics};
use ide_assists::{Assist, AssistResolveStrategy};
use ide_db::{source_change::SourceChange, RootDatabase};
use syntax::{
    ast::{self, ArgListOwner},
    AstNode, TextRange,
};
use text_edit::TextEdit;

use crate::diagnostics::{fix, DiagnosticWithFix};

impl DiagnosticWithFix for ReplaceFilterMapNextWithFindMap {
    fn fix(
        &self,
        sema: &Semantics<RootDatabase>,
        _resolve: &AssistResolveStrategy,
    ) -> Option<Assist> {
        let root = sema.db.parse_or_expand(self.file)?;
        let next_expr = self.next_expr.to_node(&root);
        let next_call = ast::MethodCallExpr::cast(next_expr.syntax().clone())?;

        let filter_map_call = ast::MethodCallExpr::cast(next_call.receiver()?.syntax().clone())?;
        let filter_map_name_range = filter_map_call.name_ref()?.ident_token()?.text_range();
        let filter_map_args = filter_map_call.arg_list()?;

        let range_to_replace =
            TextRange::new(filter_map_name_range.start(), next_expr.syntax().text_range().end());
        let replacement = format!("find_map{}", filter_map_args.syntax().text());
        let trigger_range = next_expr.syntax().text_range();

        let edit = TextEdit::replace(range_to_replace, replacement);

        let source_change = SourceChange::from_text_edit(self.file.original_file(sema.db), edit);

        Some(fix(
            "replace_with_find_map",
            "Replace filter_map(..).next() with find_map()",
            source_change,
            trigger_range,
        ))
    }
}

#[cfg(test)]
mod tests {
    use crate::diagnostics::tests::check_fix;

    #[test]
    fn replace_with_wind_map() {
        check_fix(
            r#"
//- /main.rs crate:main deps:core
use core::iter::Iterator;
use core::option::Option::{self, Some, None};
fn foo() {
    let m = [1, 2, 3].iter().$0filter_map(|x| if *x == 2 { Some (4) } else { None }).next();
}
//- /core/lib.rs crate:core
pub mod option {
    pub enum Option<T> { Some(T), None }
}
pub mod iter {
    pub trait Iterator {
        type Item;
        fn filter_map<B, F>(self, f: F) -> FilterMap where F: FnMut(Self::Item) -> Option<B> { FilterMap }
        fn next(&mut self) -> Option<Self::Item>;
    }
    pub struct FilterMap {}
    impl Iterator for FilterMap {
        type Item = i32;
        fn next(&mut self) -> i32 { 7 }
    }
}
"#,
            r#"
use core::iter::Iterator;
use core::option::Option::{self, Some, None};
fn foo() {
    let m = [1, 2, 3].iter().find_map(|x| if *x == 2 { Some (4) } else { None });
}
"#,
        )
    }
}