about summary refs log tree commit diff
diff options
context:
space:
mode:
authorJonas Schievink <jonas.schievink@ferrous-systems.com>2022-03-22 16:05:24 +0100
committerJonas Schievink <jonas.schievink@ferrous-systems.com>2022-03-22 16:08:46 +0100
commit4bb5df0ce54544df5cf89a08067f7b295b242020 (patch)
tree86736bea4cefd32386dcb1aa03e07d660a8b04eb
parentc9cefb9916143abcba6c743a200b27b1a519d3ef (diff)
downloadrust-4bb5df0ce54544df5cf89a08067f7b295b242020.tar.gz
rust-4bb5df0ce54544df5cf89a08067f7b295b242020.zip
Avoid signature help inside multiline expressions
Fixes #11768
-rw-r--r--crates/ide/src/signature_help.rs89
-rw-r--r--crates/ide_db/src/active_parameter.rs10
2 files changed, 88 insertions, 11 deletions
diff --git a/crates/ide/src/signature_help.rs b/crates/ide/src/signature_help.rs
index 030790e470e..06fdc641f84 100644
--- a/crates/ide/src/signature_help.rs
+++ b/crates/ide/src/signature_help.rs
@@ -4,11 +4,15 @@
 use either::Either;
 use hir::{HasAttrs, HirDisplay, Semantics};
 use ide_db::{
-    active_parameter::{callable_for_token, generics_for_token},
+    active_parameter::{callable_for_node, generics_for_token},
     base_db::FilePosition,
 };
 use stdx::format_to;
-use syntax::{algo, AstNode, Direction, TextRange, TextSize};
+use syntax::{
+    algo,
+    ast::{self, HasArgList},
+    AstNode, Direction, SyntaxToken, TextRange, TextSize,
+};
 
 use crate::RootDatabase;
 
@@ -65,8 +69,8 @@ pub(crate) fn signature_help(db: &RootDatabase, position: FilePosition) -> Optio
         .and_then(|tok| algo::skip_trivia_token(tok, Direction::Prev))?;
     let token = sema.descend_into_macros_single(token);
 
-    if let Some((callable, active_parameter)) = callable_for_token(&sema, token.clone()) {
-        return Some(signature_help_for_callable(db, callable, active_parameter));
+    if let Some(help) = signature_help_for_call(&sema, &token) {
+        return Some(help);
     }
 
     if let Some((generic_def, active_parameter)) = generics_for_token(&sema, token.clone()) {
@@ -76,14 +80,39 @@ pub(crate) fn signature_help(db: &RootDatabase, position: FilePosition) -> Optio
     None
 }
 
-fn signature_help_for_callable(
-    db: &RootDatabase,
-    callable: hir::Callable,
-    active_parameter: Option<usize>,
-) -> SignatureHelp {
+fn signature_help_for_call(
+    sema: &Semantics<RootDatabase>,
+    token: &SyntaxToken,
+) -> Option<SignatureHelp> {
+    // Find the calling expression and its NameRef
+    let mut node = token.parent()?;
+    let calling_node = loop {
+        if let Some(callable) = ast::CallableExpr::cast(node.clone()) {
+            if callable
+                .arg_list()
+                .map_or(false, |it| it.syntax().text_range().contains(token.text_range().start()))
+            {
+                break callable;
+            }
+        }
+
+        // Stop at multi-line expressions, since the signature of the outer call is not very
+        // helpful inside them.
+        if let Some(expr) = ast::Expr::cast(node.clone()) {
+            if expr.syntax().text().contains_char('\n') {
+                return None;
+            }
+        }
+
+        node = node.parent()?;
+    };
+
+    let (callable, active_parameter) = callable_for_node(sema, &calling_node, token)?;
+
     let mut res =
         SignatureHelp { doc: None, signature: String::new(), parameters: vec![], active_parameter };
 
+    let db = sema.db;
     match callable.kind() {
         hir::CallableKind::Function(func) => {
             res.doc = func.docs(db).map(|it| it.into());
@@ -134,7 +163,7 @@ fn signature_help_for_callable(
         }
         hir::CallableKind::TupleStruct(_) | hir::CallableKind::TupleEnumVariant(_) => {}
     }
-    res
+    Some(res)
 }
 
 fn signature_help_for_generics(
@@ -787,6 +816,46 @@ fn main() {
     }
 
     #[test]
+    fn test_multiline_argument() {
+        check(
+            r#"
+fn callee(a: u8, b: u8) {}
+fn main() {
+    callee(match 0 {
+        0 => 1,$0
+    })
+}"#,
+            expect![[r#""#]],
+        );
+        check(
+            r#"
+fn callee(a: u8, b: u8) {}
+fn main() {
+    callee(match 0 {
+        0 => 1,
+    },$0)
+}"#,
+            expect![[r#"
+                fn callee(a: u8, b: u8)
+                          -----  ^^^^^
+            "#]],
+        );
+        check(
+            r#"
+fn callee(a: u8, b: u8) {}
+fn main() {
+    callee($0match 0 {
+        0 => 1,
+    })
+}"#,
+            expect![[r#"
+                fn callee(a: u8, b: u8)
+                          ^^^^^  -----
+            "#]],
+        );
+    }
+
+    #[test]
     fn test_generics_simple() {
         check(
             r#"
diff --git a/crates/ide_db/src/active_parameter.rs b/crates/ide_db/src/active_parameter.rs
index 67b819c5a5d..ed0c010f836 100644
--- a/crates/ide_db/src/active_parameter.rs
+++ b/crates/ide_db/src/active_parameter.rs
@@ -43,13 +43,21 @@ pub fn callable_for_token(
     sema: &Semantics<RootDatabase>,
     token: SyntaxToken,
 ) -> Option<(hir::Callable, Option<usize>)> {
-    // Find the calling expression and it's NameRef
+    // Find the calling expression and its NameRef
     let parent = token.parent()?;
     let calling_node = parent.ancestors().filter_map(ast::CallableExpr::cast).find(|it| {
         it.arg_list()
             .map_or(false, |it| it.syntax().text_range().contains(token.text_range().start()))
     })?;
 
+    callable_for_node(sema, &calling_node, &token)
+}
+
+pub fn callable_for_node(
+    sema: &Semantics<RootDatabase>,
+    calling_node: &ast::CallableExpr,
+    token: &SyntaxToken,
+) -> Option<(hir::Callable, Option<usize>)> {
     let callable = match &calling_node {
         ast::CallableExpr::Call(call) => {
             let expr = call.expr()?;