aboutsummaryrefslogtreecommitdiff
path: root/crates/ide/src
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ide/src')
-rw-r--r--crates/ide/src/fn_references.rs95
-rw-r--r--crates/ide/src/lib.rs6
-rw-r--r--crates/ide/src/runnables.rs2
3 files changed, 102 insertions, 1 deletions
diff --git a/crates/ide/src/fn_references.rs b/crates/ide/src/fn_references.rs
new file mode 100644
index 000000000..1989a562b
--- /dev/null
+++ b/crates/ide/src/fn_references.rs
@@ -0,0 +1,95 @@
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
4use hir::Semantics;
5use ide_db::RootDatabase;
6use syntax::{ast, ast::NameOwner, AstNode, SyntaxNode};
7
8use crate::{runnables::has_test_related_attribute, FileId, FileRange};
9
10pub(crate) fn find_all_methods(db: &RootDatabase, file_id: FileId) -> Vec<FileRange> {
11 let sema = Semantics::new(db);
12 let source_file = sema.parse(file_id);
13 source_file.syntax().descendants().filter_map(|it| method_range(it, file_id)).collect()
14}
15
16fn method_range(item: SyntaxNode, file_id: FileId) -> Option<FileRange> {
17 ast::Fn::cast(item).and_then(|fn_def| {
18 if has_test_related_attribute(&fn_def) {
19 None
20 } else {
21 fn_def.name().map(|name| FileRange { file_id, range: name.syntax().text_range() })
22 }
23 })
24}
25
26#[cfg(test)]
27mod tests {
28 use crate::mock_analysis::analysis_and_position;
29 use crate::{FileRange, TextSize};
30 use std::ops::RangeInclusive;
31
32 #[test]
33 fn test_find_all_methods() {
34 let (analysis, pos) = analysis_and_position(
35 r#"
36 //- /lib.rs
37 fn private_fn() {<|>}
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) = analysis_and_position(
52 r#"
53 //- /lib.rs
54 trait Foo {
55 fn bar() {<|>}
56 fn baz() {}
57 }
58 "#,
59 );
60
61 let refs = analysis.find_all_methods(pos.file_id).unwrap();
62 check_result(&refs, &[19..=22, 35..=38]);
63 }
64
65 #[test]
66 fn test_skip_tests() {
67 let (analysis, pos) = analysis_and_position(
68 r#"
69 //- /lib.rs
70 #[test]
71 fn foo() {<|>}
72
73 pub fn pub_fn() {}
74
75 mod tests {
76 #[test]
77 fn bar() {}
78 }
79 "#,
80 );
81
82 let refs = analysis.find_all_methods(pos.file_id).unwrap();
83 check_result(&refs, &[28..=34]);
84 }
85
86 fn check_result(refs: &[FileRange], expected: &[RangeInclusive<u32>]) {
87 assert_eq!(refs.len(), expected.len());
88
89 for (i, item) in refs.iter().enumerate() {
90 let range = &expected[i];
91 assert_eq!(TextSize::from(*range.start()), item.range.start());
92 assert_eq!(TextSize::from(*range.end()), item.range.end());
93 }
94 }
95}
diff --git a/crates/ide/src/lib.rs b/crates/ide/src/lib.rs
index 4763c0aac..31f2bcba3 100644
--- a/crates/ide/src/lib.rs
+++ b/crates/ide/src/lib.rs
@@ -38,6 +38,7 @@ mod join_lines;
38mod matching_brace; 38mod matching_brace;
39mod parent_module; 39mod parent_module;
40mod references; 40mod references;
41mod fn_references;
41mod runnables; 42mod runnables;
42mod status; 43mod status;
43mod syntax_highlighting; 44mod syntax_highlighting;
@@ -369,6 +370,11 @@ impl Analysis {
369 }) 370 })
370 } 371 }
371 372
373 /// Finds all methods and free functions for the file. Does not return tests!
374 pub fn find_all_methods(&self, file_id: FileId) -> Cancelable<Vec<FileRange>> {
375 self.with_db(|db| fn_references::find_all_methods(db, file_id))
376 }
377
372 /// Returns a short text describing element at position. 378 /// Returns a short text describing element at position.
373 pub fn hover( 379 pub fn hover(
374 &self, 380 &self,
diff --git a/crates/ide/src/runnables.rs b/crates/ide/src/runnables.rs
index 989a63c09..cfeff40c1 100644
--- a/crates/ide/src/runnables.rs
+++ b/crates/ide/src/runnables.rs
@@ -203,7 +203,7 @@ impl TestAttr {
203/// 203///
204/// It may produce false positives, for example, `#[wasm_bindgen_test]` requires a different command to run the test, 204/// It may produce false positives, for example, `#[wasm_bindgen_test]` requires a different command to run the test,
205/// but it's better than not to have the runnables for the tests at all. 205/// but it's better than not to have the runnables for the tests at all.
206fn has_test_related_attribute(fn_def: &ast::Fn) -> bool { 206pub(crate) fn has_test_related_attribute(fn_def: &ast::Fn) -> bool {
207 fn_def 207 fn_def
208 .attrs() 208 .attrs()
209 .filter_map(|attr| attr.path()) 209 .filter_map(|attr| attr.path())