From b7ed8150a348421ebdffa58cca06f3db164060da Mon Sep 17 00:00:00 2001 From: roife Date: Sun, 13 Oct 2024 04:43:53 +0800 Subject: [PATCH] feat: respect references.exclude_tests in call-hierarchy --- crates/ide/src/call_hierarchy.rs | 118 ++++++++++++++++++- crates/ide/src/lib.rs | 18 ++- crates/rust-analyzer/src/config.rs | 15 ++- crates/rust-analyzer/src/handlers/request.rs | 6 +- docs/user/generated_config.adoc | 2 +- editors/code/package.json | 2 +- 6 files changed, 141 insertions(+), 20 deletions(-) diff --git a/crates/ide/src/call_hierarchy.rs b/crates/ide/src/call_hierarchy.rs index 155259a138..1b82c00d1d 100644 --- a/crates/ide/src/call_hierarchy.rs +++ b/crates/ide/src/call_hierarchy.rs @@ -19,6 +19,12 @@ pub struct CallItem { pub ranges: Vec, } +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct CallHierarchyConfig { + /// Whether to exclude tests from the call hierarchy + pub exclude_tests: bool, +} + pub(crate) fn call_hierarchy( db: &RootDatabase, position: FilePosition, @@ -28,6 +34,7 @@ pub(crate) fn call_hierarchy( pub(crate) fn incoming_calls( db: &RootDatabase, + CallHierarchyConfig { exclude_tests }: CallHierarchyConfig, FilePosition { file_id, offset }: FilePosition, ) -> Option> { let sema = &Semantics::new(db); @@ -56,11 +63,18 @@ pub(crate) fn incoming_calls( references.iter().filter_map(|FileReference { name, .. }| name.as_name_ref()); for name in references { // This target is the containing function - let nav = sema.ancestors_with_macros(name.syntax().clone()).find_map(|node| { + let def_nav = sema.ancestors_with_macros(name.syntax().clone()).find_map(|node| { let def = ast::Fn::cast(node).and_then(|fn_| sema.to_def(&fn_))?; - def.try_to_nav(sema.db) + // We should return def before check if it is a test, so that we + // will not continue to search for outer fn in nested fns + def.try_to_nav(sema.db).map(|nav| (def, nav)) }); - if let Some(nav) = nav { + + if let Some((def, nav)) = def_nav { + if exclude_tests && def.is_test(db) { + continue; + } + let range = sema.original_range(name.syntax()); calls.add(nav.call_site, range.into()); if let Some(other) = nav.def_site { @@ -75,6 +89,7 @@ pub(crate) fn incoming_calls( pub(crate) fn outgoing_calls( db: &RootDatabase, + CallHierarchyConfig { exclude_tests }: CallHierarchyConfig, FilePosition { file_id, offset }: FilePosition, ) -> Option> { let sema = Semantics::new(db); @@ -103,7 +118,12 @@ pub(crate) fn outgoing_calls( let expr = call.expr()?; let callable = sema.type_of_expr(&expr)?.original.as_callable(db)?; match callable.kind() { - hir::CallableKind::Function(it) => it.try_to_nav(db), + hir::CallableKind::Function(it) => { + if exclude_tests && it.is_test(db) { + return None; + } + it.try_to_nav(db) + } hir::CallableKind::TupleEnumVariant(it) => it.try_to_nav(db), hir::CallableKind::TupleStruct(it) => it.try_to_nav(db), _ => None, @@ -112,6 +132,9 @@ pub(crate) fn outgoing_calls( } ast::CallableExpr::MethodCall(expr) => { let function = sema.resolve_method_call(&expr)?; + if exclude_tests && function.is_test(db) { + return None; + } function .try_to_nav(db) .zip(Some(sema.original_range(expr.name_ref()?.syntax()))) @@ -149,6 +172,7 @@ mod tests { use crate::fixture; fn check_hierarchy( + exclude_tests: bool, ra_fixture: &str, expected_nav: Expect, expected_incoming: Expect, @@ -172,18 +196,21 @@ mod tests { let nav = navs.pop().unwrap(); expected_nav.assert_eq(&nav.debug_render()); + let config = crate::CallHierarchyConfig { exclude_tests }; + let item_pos = FilePosition { file_id: nav.file_id, offset: nav.focus_or_full_range().start() }; - let incoming_calls = analysis.incoming_calls(item_pos).unwrap().unwrap(); + let incoming_calls = analysis.incoming_calls(config, item_pos).unwrap().unwrap(); expected_incoming.assert_eq(&incoming_calls.into_iter().map(debug_render).join("\n")); - let outgoing_calls = analysis.outgoing_calls(item_pos).unwrap().unwrap(); + let outgoing_calls = analysis.outgoing_calls(config, item_pos).unwrap().unwrap(); expected_outgoing.assert_eq(&outgoing_calls.into_iter().map(debug_render).join("\n")); } #[test] fn test_call_hierarchy_on_ref() { check_hierarchy( + false, r#" //- /lib.rs fn callee() {} @@ -200,6 +227,7 @@ fn caller() { #[test] fn test_call_hierarchy_on_def() { check_hierarchy( + false, r#" //- /lib.rs fn call$0ee() {} @@ -216,6 +244,7 @@ fn caller() { #[test] fn test_call_hierarchy_in_same_fn() { check_hierarchy( + false, r#" //- /lib.rs fn callee() {} @@ -233,6 +262,7 @@ fn caller() { #[test] fn test_call_hierarchy_in_different_fn() { check_hierarchy( + false, r#" //- /lib.rs fn callee() {} @@ -255,6 +285,7 @@ fn caller2() { #[test] fn test_call_hierarchy_in_tests_mod() { check_hierarchy( + false, r#" //- /lib.rs cfg:test fn callee() {} @@ -283,6 +314,7 @@ mod tests { #[test] fn test_call_hierarchy_in_different_files() { check_hierarchy( + false, r#" //- /lib.rs mod foo; @@ -304,6 +336,7 @@ pub fn callee() {} #[test] fn test_call_hierarchy_outgoing() { check_hierarchy( + false, r#" //- /lib.rs fn callee() {} @@ -321,6 +354,7 @@ fn call$0er() { #[test] fn test_call_hierarchy_outgoing_in_different_files() { check_hierarchy( + false, r#" //- /lib.rs mod foo; @@ -342,6 +376,7 @@ pub fn callee() {} #[test] fn test_call_hierarchy_incoming_outgoing() { check_hierarchy( + false, r#" //- /lib.rs fn caller1() { @@ -365,6 +400,7 @@ fn caller3() { #[test] fn test_call_hierarchy_issue_5103() { check_hierarchy( + false, r#" fn a() { b() @@ -382,6 +418,7 @@ fn main() { ); check_hierarchy( + false, r#" fn a() { b$0() @@ -402,6 +439,7 @@ fn main() { #[test] fn test_call_hierarchy_in_macros_incoming() { check_hierarchy( + false, r#" macro_rules! define { ($ident:ident) => { @@ -423,6 +461,7 @@ fn caller() { expect![[]], ); check_hierarchy( + false, r#" macro_rules! define { ($ident:ident) => { @@ -448,6 +487,7 @@ fn caller() { #[test] fn test_call_hierarchy_in_macros_outgoing() { check_hierarchy( + false, r#" macro_rules! define { ($ident:ident) => { @@ -473,6 +513,7 @@ fn caller$0() { #[test] fn test_call_hierarchy_in_macros_incoming_different_files() { check_hierarchy( + false, r#" //- /lib.rs #[macro_use] @@ -498,6 +539,7 @@ macro_rules! call { expect![[]], ); check_hierarchy( + false, r#" //- /lib.rs #[macro_use] @@ -523,6 +565,7 @@ macro_rules! call { expect![[]], ); check_hierarchy( + false, r#" //- /lib.rs #[macro_use] @@ -558,6 +601,7 @@ macro_rules! call { #[test] fn test_call_hierarchy_in_macros_outgoing_different_files() { check_hierarchy( + false, r#" //- /lib.rs #[macro_use] @@ -585,6 +629,7 @@ macro_rules! call { expect![[]], ); check_hierarchy( + false, r#" //- /lib.rs #[macro_use] @@ -616,6 +661,7 @@ macro_rules! call { #[test] fn test_trait_method_call_hierarchy() { check_hierarchy( + false, r#" trait T1 { fn call$0ee(); @@ -636,4 +682,64 @@ fn caller() { expect![[]], ); } + + #[test] + fn test_call_hierarchy_excluding_tests() { + check_hierarchy( + false, + r#" +fn main() { + f1(); +} + +fn f1$0() { + f2(); f3(); +} + +fn f2() { + f1(); f3(); +} + +#[test] +fn f3() { + f1(); f2(); +} +"#, + expect!["f1 Function FileId(0) 25..52 28..30"], + expect![[r#" + main Function FileId(0) 0..23 3..7 : FileId(0):16..18 + f2 Function FileId(0) 54..81 57..59 : FileId(0):68..70 + f3 Function FileId(0) 83..118 94..96 : FileId(0):105..107"#]], + expect![[r#" + f2 Function FileId(0) 54..81 57..59 : FileId(0):39..41 + f3 Function FileId(0) 83..118 94..96 : FileId(0):45..47"#]], + ); + + check_hierarchy( + true, + r#" +fn main() { + f1(); +} + +fn f1$0() { + f2(); f3(); +} + +fn f2() { + f1(); f3(); +} + +#[test] +fn f3() { + f1(); f2(); +} +"#, + expect!["f1 Function FileId(0) 25..52 28..30"], + expect![[r#" + main Function FileId(0) 0..23 3..7 : FileId(0):16..18 + f2 Function FileId(0) 54..81 57..59 : FileId(0):68..70"#]], + expect!["f2 Function FileId(0) 54..81 57..59 : FileId(0):39..41"], + ); + } } diff --git a/crates/ide/src/lib.rs b/crates/ide/src/lib.rs index c46c4c8ce9..3a8f7bb7a1 100644 --- a/crates/ide/src/lib.rs +++ b/crates/ide/src/lib.rs @@ -79,7 +79,7 @@ use crate::navigation_target::ToNav; pub use crate::{ annotations::{Annotation, AnnotationConfig, AnnotationKind, AnnotationLocation}, - call_hierarchy::CallItem, + call_hierarchy::{CallHierarchyConfig, CallItem}, expand_macro::ExpandedMacro, file_structure::{StructureNode, StructureNodeKind}, folding_ranges::{Fold, FoldKind}, @@ -564,13 +564,21 @@ impl Analysis { } /// Computes incoming calls for the given file position. - pub fn incoming_calls(&self, position: FilePosition) -> Cancellable>> { - self.with_db(|db| call_hierarchy::incoming_calls(db, position)) + pub fn incoming_calls( + &self, + config: CallHierarchyConfig, + position: FilePosition, + ) -> Cancellable>> { + self.with_db(|db| call_hierarchy::incoming_calls(db, config, position)) } /// Computes outgoing calls for the given file position. - pub fn outgoing_calls(&self, position: FilePosition) -> Cancellable>> { - self.with_db(|db| call_hierarchy::outgoing_calls(db, position)) + pub fn outgoing_calls( + &self, + config: CallHierarchyConfig, + position: FilePosition, + ) -> Cancellable>> { + self.with_db(|db| call_hierarchy::outgoing_calls(db, config, position)) } /// Returns a `mod name;` declaration which created the current module. diff --git a/crates/rust-analyzer/src/config.rs b/crates/rust-analyzer/src/config.rs index ef2e542cf2..518b588cb7 100644 --- a/crates/rust-analyzer/src/config.rs +++ b/crates/rust-analyzer/src/config.rs @@ -12,10 +12,11 @@ use std::{ use cfg::{CfgAtom, CfgDiff}; use hir::Symbol; use ide::{ - AssistConfig, CallableSnippets, CompletionConfig, CompletionFieldsToResolve, DiagnosticsConfig, - ExprFillDefaultMode, GenericParameterHints, HighlightConfig, HighlightRelatedConfig, - HoverConfig, HoverDocFormat, InlayFieldsToResolve, InlayHintsConfig, JoinLinesConfig, - MemoryLayoutHoverConfig, MemoryLayoutHoverRenderKind, Snippet, SnippetScope, SourceRootId, + AssistConfig, CallHierarchyConfig, CallableSnippets, CompletionConfig, + CompletionFieldsToResolve, DiagnosticsConfig, ExprFillDefaultMode, GenericParameterHints, + HighlightConfig, HighlightRelatedConfig, HoverConfig, HoverDocFormat, InlayFieldsToResolve, + InlayHintsConfig, JoinLinesConfig, MemoryLayoutHoverConfig, MemoryLayoutHoverRenderKind, + Snippet, SnippetScope, SourceRootId, }; use ide_db::{ imports::insert_use::{ImportGranularity, InsertUseConfig, PrefixKind}, @@ -262,7 +263,7 @@ config_data! { /// Exclude imports from find-all-references. references_excludeImports: bool = false, - /// Exclude tests from find-all-references. + /// Exclude tests from find-all-references and call-hierarchy. references_excludeTests: bool = false, /// Inject additional highlighting into doc comments. @@ -1392,6 +1393,10 @@ impl Config { } } + pub fn call_hierarchy(&self) -> CallHierarchyConfig { + CallHierarchyConfig { exclude_tests: self.references_excludeTests().to_owned() } + } + pub fn completion(&self, source_root: Option) -> CompletionConfig { let client_capability_fields = self.completion_resolve_support_properties(); CompletionConfig { diff --git a/crates/rust-analyzer/src/handlers/request.rs b/crates/rust-analyzer/src/handlers/request.rs index 9773d8dbce..5094c1fa5f 100644 --- a/crates/rust-analyzer/src/handlers/request.rs +++ b/crates/rust-analyzer/src/handlers/request.rs @@ -1708,7 +1708,8 @@ pub(crate) fn handle_call_hierarchy_incoming( let frange = from_proto::file_range(&snap, &doc, item.selection_range)?; let fpos = FilePosition { file_id: frange.file_id, offset: frange.range.start() }; - let call_items = match snap.analysis.incoming_calls(fpos)? { + let config = snap.config.call_hierarchy(); + let call_items = match snap.analysis.incoming_calls(config, fpos)? { None => return Ok(None), Some(it) => it, }; @@ -1745,7 +1746,8 @@ pub(crate) fn handle_call_hierarchy_outgoing( let frange = from_proto::file_range(&snap, &doc, item.selection_range)?; let fpos = FilePosition { file_id: frange.file_id, offset: frange.range.start() }; - let call_items = match snap.analysis.outgoing_calls(fpos)? { + let config = snap.config.call_hierarchy(); + let call_items = match snap.analysis.outgoing_calls(config, fpos)? { None => return Ok(None), Some(it) => it, }; diff --git a/docs/user/generated_config.adoc b/docs/user/generated_config.adoc index 708fc2b789..babeb4272b 100644 --- a/docs/user/generated_config.adoc +++ b/docs/user/generated_config.adoc @@ -852,7 +852,7 @@ Exclude imports from find-all-references. [[rust-analyzer.references.excludeTests]]rust-analyzer.references.excludeTests (default: `false`):: + -- -Exclude tests from find-all-references. +Exclude tests from find-all-references and call-hierarchy. -- [[rust-analyzer.runnables.command]]rust-analyzer.runnables.command (default: `null`):: + diff --git a/editors/code/package.json b/editors/code/package.json index a823e5bb96..a52b3d1ec5 100644 --- a/editors/code/package.json +++ b/editors/code/package.json @@ -2333,7 +2333,7 @@ "title": "references", "properties": { "rust-analyzer.references.excludeTests": { - "markdownDescription": "Exclude tests from find-all-references.", + "markdownDescription": "Exclude tests from find-all-references and call-hierarchy.", "default": false, "type": "boolean" }