]> git.lizzy.rs Git - rust.git/blob - src/tools/rust-analyzer/crates/ide/src/fn_references.rs
Rollup merge of #100228 - luqmana:suggestion-ice, r=estebank
[rust.git] / src / tools / rust-analyzer / crates / ide / src / fn_references.rs
1 //! This module implements a methods and free functions search in the specified file.
2 //! We have to skip tests, so cannot reuse file_structure module.
3
4 use hir::Semantics;
5 use ide_assists::utils::test_related_attribute;
6 use ide_db::RootDatabase;
7 use syntax::{ast, ast::HasName, AstNode, SyntaxNode};
8
9 use crate::{FileId, FileRange};
10
11 pub(crate) fn find_all_methods(db: &RootDatabase, file_id: FileId) -> Vec<FileRange> {
12     let sema = Semantics::new(db);
13     let source_file = sema.parse(file_id);
14     source_file.syntax().descendants().filter_map(|it| method_range(it, file_id)).collect()
15 }
16
17 fn method_range(item: SyntaxNode, file_id: FileId) -> Option<FileRange> {
18     ast::Fn::cast(item).and_then(|fn_def| {
19         if test_related_attribute(&fn_def).is_some() {
20             None
21         } else {
22             fn_def.name().map(|name| FileRange { file_id, range: name.syntax().text_range() })
23         }
24     })
25 }
26
27 #[cfg(test)]
28 mod tests {
29     use crate::fixture;
30     use crate::{FileRange, TextSize};
31     use std::ops::RangeInclusive;
32
33     #[test]
34     fn test_find_all_methods() {
35         let (analysis, pos) = fixture::position(
36             r#"
37             fn private_fn() {$0}
38
39             pub fn pub_fn() {}
40
41             pub fn generic_fn<T>(arg: T) {}
42         "#,
43         );
44
45         let refs = analysis.find_all_methods(pos.file_id).unwrap();
46         check_result(&refs, &[3..=13, 27..=33, 47..=57]);
47     }
48
49     #[test]
50     fn test_find_trait_methods() {
51         let (analysis, pos) = fixture::position(
52             r#"
53             trait Foo {
54                 fn bar() {$0}
55                 fn baz() {}
56             }
57         "#,
58         );
59
60         let refs = analysis.find_all_methods(pos.file_id).unwrap();
61         check_result(&refs, &[19..=22, 35..=38]);
62     }
63
64     #[test]
65     fn test_skip_tests() {
66         let (analysis, pos) = fixture::position(
67             r#"
68             //- /lib.rs
69             #[test]
70             fn foo() {$0}
71
72             pub fn pub_fn() {}
73
74             mod tests {
75                 #[test]
76                 fn bar() {}
77             }
78         "#,
79         );
80
81         let refs = analysis.find_all_methods(pos.file_id).unwrap();
82         check_result(&refs, &[28..=34]);
83     }
84
85     fn check_result(refs: &[FileRange], expected: &[RangeInclusive<u32>]) {
86         assert_eq!(refs.len(), expected.len());
87
88         for (i, item) in refs.iter().enumerate() {
89             let range = &expected[i];
90             assert_eq!(TextSize::from(*range.start()), item.range.start());
91             assert_eq!(TextSize::from(*range.end()), item.range.end());
92         }
93     }
94 }