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, TextRange};
8
9 use crate::FileId;
10
find_all_methods( db: &RootDatabase, file_id: FileId, ) -> Vec<(TextRange, Option<TextRange>)>11 pub(super) fn find_all_methods(
12 db: &RootDatabase,
13 file_id: FileId,
14 ) -> Vec<(TextRange, Option<TextRange>)> {
15 let sema = Semantics::new(db);
16 let source_file = sema.parse(file_id);
17 source_file.syntax().descendants().filter_map(|it| method_range(it)).collect()
18 }
19
method_range(item: SyntaxNode) -> Option<(TextRange, Option<TextRange>)>20 fn method_range(item: SyntaxNode) -> Option<(TextRange, Option<TextRange>)> {
21 ast::Fn::cast(item).and_then(|fn_def| {
22 if test_related_attribute(&fn_def).is_some() {
23 None
24 } else {
25 Some((
26 fn_def.syntax().text_range(),
27 fn_def.name().map(|name| name.syntax().text_range()),
28 ))
29 }
30 })
31 }
32
33 #[cfg(test)]
34 mod tests {
35 use syntax::TextRange;
36
37 use crate::fixture;
38 use crate::TextSize;
39 use std::ops::RangeInclusive;
40
41 #[test]
test_find_all_methods()42 fn test_find_all_methods() {
43 let (analysis, pos) = fixture::position(
44 r#"
45 fn private_fn() {$0}
46
47 pub fn pub_fn() {}
48
49 pub fn generic_fn<T>(arg: T) {}
50 "#,
51 );
52
53 let refs = super::find_all_methods(&analysis.db, pos.file_id);
54 check_result(&refs, &[3..=13, 27..=33, 47..=57]);
55 }
56
57 #[test]
test_find_trait_methods()58 fn test_find_trait_methods() {
59 let (analysis, pos) = fixture::position(
60 r#"
61 trait Foo {
62 fn bar() {$0}
63 fn baz() {}
64 }
65 "#,
66 );
67
68 let refs = super::find_all_methods(&analysis.db, pos.file_id);
69 check_result(&refs, &[19..=22, 35..=38]);
70 }
71
72 #[test]
test_skip_tests()73 fn test_skip_tests() {
74 let (analysis, pos) = fixture::position(
75 r#"
76 //- /lib.rs
77 #[test]
78 fn foo() {$0}
79
80 pub fn pub_fn() {}
81
82 mod tests {
83 #[test]
84 fn bar() {}
85 }
86 "#,
87 );
88
89 let refs = super::find_all_methods(&analysis.db, pos.file_id);
90 check_result(&refs, &[28..=34]);
91 }
92
check_result(refs: &[(TextRange, Option<TextRange>)], expected: &[RangeInclusive<u32>])93 fn check_result(refs: &[(TextRange, Option<TextRange>)], expected: &[RangeInclusive<u32>]) {
94 assert_eq!(refs.len(), expected.len());
95
96 for (i, &(full, focus)) in refs.iter().enumerate() {
97 let range = &expected[i];
98 let item = focus.unwrap_or(full);
99 assert_eq!(TextSize::from(*range.start()), item.start());
100 assert_eq!(TextSize::from(*range.end()), item.end());
101 }
102 }
103 }
104