diff options
Diffstat (limited to 'src/tools/rust-analyzer/crates/ide/src/annotations/fn_references.rs')
-rw-r--r-- | src/tools/rust-analyzer/crates/ide/src/annotations/fn_references.rs | 103 |
1 files changed, 103 insertions, 0 deletions
diff --git a/src/tools/rust-analyzer/crates/ide/src/annotations/fn_references.rs b/src/tools/rust-analyzer/crates/ide/src/annotations/fn_references.rs new file mode 100644 index 000000000..0cadf125f --- /dev/null +++ b/src/tools/rust-analyzer/crates/ide/src/annotations/fn_references.rs @@ -0,0 +1,103 @@ +//! 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::HasName, AstNode, SyntaxNode, TextRange}; + +use crate::FileId; + +pub(super) fn find_all_methods( + db: &RootDatabase, + file_id: FileId, +) -> Vec<(TextRange, Option<TextRange>)> { + let sema = Semantics::new(db); + let source_file = sema.parse(file_id); + source_file.syntax().descendants().filter_map(|it| method_range(it)).collect() +} + +fn method_range(item: SyntaxNode) -> Option<(TextRange, Option<TextRange>)> { + ast::Fn::cast(item).and_then(|fn_def| { + if test_related_attribute(&fn_def).is_some() { + None + } else { + Some(( + fn_def.syntax().text_range(), + fn_def.name().map(|name| name.syntax().text_range()), + )) + } + }) +} + +#[cfg(test)] +mod tests { + use syntax::TextRange; + + use crate::fixture; + use crate::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 = super::find_all_methods(&analysis.db, pos.file_id); + 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 = super::find_all_methods(&analysis.db, pos.file_id); + 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 = super::find_all_methods(&analysis.db, pos.file_id); + check_result(&refs, &[28..=34]); + } + + fn check_result(refs: &[(TextRange, Option<TextRange>)], expected: &[RangeInclusive<u32>]) { + assert_eq!(refs.len(), expected.len()); + + for (i, &(full, focus)) in refs.iter().enumerate() { + let range = &expected[i]; + let item = focus.unwrap_or(full); + assert_eq!(TextSize::from(*range.start()), item.start()); + assert_eq!(TextSize::from(*range.end()), item.end()); + } + } +} |