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
85
86
87
88
89
90
91
92
93
94
|
//! This module implements a methods and free functions search in the specified file.
//! We have to skip tests, so cannot reuse file_structure module.
use hir::Semantics;
use ide_assists::utils::test_related_attribute;
use ide_db::RootDatabase;
use syntax::{ast, ast::NameOwner, AstNode, SyntaxNode};
use crate::{FileId, FileRange};
pub(crate) fn find_all_methods(db: &RootDatabase, file_id: FileId) -> Vec<FileRange> {
let sema = Semantics::new(db);
let source_file = sema.parse(file_id);
source_file.syntax().descendants().filter_map(|it| method_range(it, file_id)).collect()
}
fn method_range(item: SyntaxNode, file_id: FileId) -> Option<FileRange> {
ast::Fn::cast(item).and_then(|fn_def| {
if test_related_attribute(&fn_def).is_some() {
None
} else {
fn_def.name().map(|name| FileRange { file_id, range: name.syntax().text_range() })
}
})
}
#[cfg(test)]
mod tests {
use crate::fixture;
use crate::{FileRange, TextSize};
use std::ops::RangeInclusive;
#[test]
fn test_find_all_methods() {
let (analysis, pos) = fixture::position(
r#"
fn private_fn() {$0}
pub fn pub_fn() {}
pub fn generic_fn<T>(arg: T) {}
"#,
);
let refs = analysis.find_all_methods(pos.file_id).unwrap();
check_result(&refs, &[3..=13, 27..=33, 47..=57]);
}
#[test]
fn test_find_trait_methods() {
let (analysis, pos) = fixture::position(
r#"
trait Foo {
fn bar() {$0}
fn baz() {}
}
"#,
);
let refs = analysis.find_all_methods(pos.file_id).unwrap();
check_result(&refs, &[19..=22, 35..=38]);
}
#[test]
fn test_skip_tests() {
let (analysis, pos) = fixture::position(
r#"
//- /lib.rs
#[test]
fn foo() {$0}
pub fn pub_fn() {}
mod tests {
#[test]
fn bar() {}
}
"#,
);
let refs = analysis.find_all_methods(pos.file_id).unwrap();
check_result(&refs, &[28..=34]);
}
fn check_result(refs: &[FileRange], expected: &[RangeInclusive<u32>]) {
assert_eq!(refs.len(), expected.len());
for (i, item) in refs.iter().enumerate() {
let range = &expected[i];
assert_eq!(TextSize::from(*range.start()), item.range.start());
assert_eq!(TextSize::from(*range.end()), item.range.end());
}
}
}
|