Unnamed repository; edit this file 'description' to name the repository.
Diffstat (limited to 'crates/ide/src/signature_help.rs')
-rw-r--r--crates/ide/src/signature_help.rs502
1 files changed, 455 insertions, 47 deletions
diff --git a/crates/ide/src/signature_help.rs b/crates/ide/src/signature_help.rs
index 4b2c139f6f..7795be54e2 100644
--- a/crates/ide/src/signature_help.rs
+++ b/crates/ide/src/signature_help.rs
@@ -15,8 +15,9 @@ use ide_db::{
use stdx::format_to;
use syntax::{
algo,
- ast::{self, HasArgList},
- match_ast, AstNode, Direction, SyntaxElementChildren, SyntaxToken, TextRange, TextSize,
+ ast::{self, AstChildren, HasArgList},
+ match_ast, AstNode, Direction, NodeOrToken, SyntaxElementChildren, SyntaxNode, SyntaxToken,
+ TextRange, TextSize, T,
};
use crate::RootDatabase;
@@ -116,6 +117,20 @@ pub(crate) fn signature_help(db: &RootDatabase, position: FilePosition) -> Optio
}
return signature_help_for_tuple_struct_pat(&sema, tuple_pat, token);
},
+ ast::TuplePat(tuple_pat) => {
+ let cursor_outside = tuple_pat.r_paren_token().as_ref() == Some(&token);
+ if cursor_outside {
+ continue;
+ }
+ return signature_help_for_tuple_pat(&sema, tuple_pat, token);
+ },
+ ast::TupleExpr(tuple_expr) => {
+ let cursor_outside = tuple_expr.r_paren_token().as_ref() == Some(&token);
+ if cursor_outside {
+ continue;
+ }
+ return signature_help_for_tuple_expr(&sema, tuple_expr, token);
+ },
_ => (),
}
}
@@ -162,7 +177,7 @@ fn signature_help_for_call(
match callable.kind() {
hir::CallableKind::Function(func) => {
res.doc = func.docs(db).map(|it| it.into());
- format_to!(res.signature, "fn {}", func.name(db));
+ format_to!(res.signature, "fn {}", func.name(db).display(db));
fn_params = Some(match callable.receiver_param(db) {
Some(_self) => func.params_without_self(db),
None => func.assoc_fn_params(db),
@@ -170,15 +185,15 @@ fn signature_help_for_call(
}
hir::CallableKind::TupleStruct(strukt) => {
res.doc = strukt.docs(db).map(|it| it.into());
- format_to!(res.signature, "struct {}", strukt.name(db));
+ format_to!(res.signature, "struct {}", strukt.name(db).display(db));
}
hir::CallableKind::TupleEnumVariant(variant) => {
res.doc = variant.docs(db).map(|it| it.into());
format_to!(
res.signature,
"enum {}::{}",
- variant.parent_enum(db).name(db),
- variant.name(db)
+ variant.parent_enum(db).name(db).display(db),
+ variant.name(db).display(db)
);
}
hir::CallableKind::Closure | hir::CallableKind::FnPtr | hir::CallableKind::Other => (),
@@ -248,31 +263,31 @@ fn signature_help_for_generics(
match generics_def {
hir::GenericDef::Function(it) => {
res.doc = it.docs(db).map(|it| it.into());
- format_to!(res.signature, "fn {}", it.name(db));
+ format_to!(res.signature, "fn {}", it.name(db).display(db));
}
hir::GenericDef::Adt(hir::Adt::Enum(it)) => {
res.doc = it.docs(db).map(|it| it.into());
- format_to!(res.signature, "enum {}", it.name(db));
+ format_to!(res.signature, "enum {}", it.name(db).display(db));
}
hir::GenericDef::Adt(hir::Adt::Struct(it)) => {
res.doc = it.docs(db).map(|it| it.into());
- format_to!(res.signature, "struct {}", it.name(db));
+ format_to!(res.signature, "struct {}", it.name(db).display(db));
}
hir::GenericDef::Adt(hir::Adt::Union(it)) => {
res.doc = it.docs(db).map(|it| it.into());
- format_to!(res.signature, "union {}", it.name(db));
+ format_to!(res.signature, "union {}", it.name(db).display(db));
}
hir::GenericDef::Trait(it) => {
res.doc = it.docs(db).map(|it| it.into());
- format_to!(res.signature, "trait {}", it.name(db));
+ format_to!(res.signature, "trait {}", it.name(db).display(db));
}
hir::GenericDef::TraitAlias(it) => {
res.doc = it.docs(db).map(|it| it.into());
- format_to!(res.signature, "trait {}", it.name(db));
+ format_to!(res.signature, "trait {}", it.name(db).display(db));
}
hir::GenericDef::TypeAlias(it) => {
res.doc = it.docs(db).map(|it| it.into());
- format_to!(res.signature, "type {}", it.name(db));
+ format_to!(res.signature, "type {}", it.name(db).display(db));
}
hir::GenericDef::Variant(it) => {
// In paths, generics of an enum can be specified *after* one of its variants.
@@ -280,7 +295,7 @@ fn signature_help_for_generics(
// We'll use the signature of the enum, but include the docs of the variant.
res.doc = it.docs(db).map(|it| it.into());
let enum_ = it.parent_enum(db);
- format_to!(res.signature, "enum {}", enum_.name(db));
+ format_to!(res.signature, "enum {}", enum_.name(db).display(db));
generics_def = enum_.into();
}
// These don't have generic args that can be specified
@@ -395,24 +410,26 @@ fn signature_help_for_tuple_struct_pat(
pat: ast::TupleStructPat,
token: SyntaxToken,
) -> Option<SignatureHelp> {
- let rest_pat = pat.fields().find(|it| matches!(it, ast::Pat::RestPat(_)));
- let is_left_of_rest_pat =
- rest_pat.map_or(true, |it| token.text_range().start() < it.syntax().text_range().end());
-
+ let path = pat.path()?;
+ let path_res = sema.resolve_path(&path)?;
let mut res = SignatureHelp {
doc: None,
signature: String::new(),
parameters: vec![],
active_parameter: None,
};
-
let db = sema.db;
- let path_res = sema.resolve_path(&pat.path()?)?;
+
let fields: Vec<_> = if let PathResolution::Def(ModuleDef::Variant(variant)) = path_res {
let en = variant.parent_enum(db);
res.doc = en.docs(db).map(|it| it.into());
- format_to!(res.signature, "enum {}::{} (", en.name(db), variant.name(db));
+ format_to!(
+ res.signature,
+ "enum {}::{} (",
+ en.name(db).display(db),
+ variant.name(db).display(db)
+ );
variant.fields(db)
} else {
let adt = match path_res {
@@ -424,36 +441,78 @@ fn signature_help_for_tuple_struct_pat(
match adt {
hir::Adt::Struct(it) => {
res.doc = it.docs(db).map(|it| it.into());
- format_to!(res.signature, "struct {} (", it.name(db));
+ format_to!(res.signature, "struct {} (", it.name(db).display(db));
it.fields(db)
}
_ => return None,
}
};
- let commas = pat
- .syntax()
- .children_with_tokens()
- .filter_map(syntax::NodeOrToken::into_token)
- .filter(|t| t.kind() == syntax::T![,]);
- res.active_parameter = Some(if is_left_of_rest_pat {
- commas.take_while(|t| t.text_range().start() <= token.text_range().start()).count()
- } else {
- let n_commas = commas
- .collect::<Vec<_>>()
- .into_iter()
- .rev()
- .take_while(|t| t.text_range().start() > token.text_range().start())
- .count();
- fields.len().saturating_sub(1).saturating_sub(n_commas)
- });
+ Some(signature_help_for_tuple_pat_ish(
+ db,
+ res,
+ pat.syntax(),
+ token,
+ pat.fields(),
+ fields.into_iter().map(|it| it.ty(db)),
+ ))
+}
+
+fn signature_help_for_tuple_pat(
+ sema: &Semantics<'_, RootDatabase>,
+ pat: ast::TuplePat,
+ token: SyntaxToken,
+) -> Option<SignatureHelp> {
+ let db = sema.db;
+ let field_pats = pat.fields();
+ let pat = pat.into();
+ let ty = sema.type_of_pat(&pat)?;
+ let fields = ty.original.tuple_fields(db);
+
+ Some(signature_help_for_tuple_pat_ish(
+ db,
+ SignatureHelp {
+ doc: None,
+ signature: String::from('('),
+ parameters: vec![],
+ active_parameter: None,
+ },
+ pat.syntax(),
+ token,
+ field_pats,
+ fields.into_iter(),
+ ))
+}
+fn signature_help_for_tuple_expr(
+ sema: &Semantics<'_, RootDatabase>,
+ expr: ast::TupleExpr,
+ token: SyntaxToken,
+) -> Option<SignatureHelp> {
+ let active_parameter = Some(
+ expr.syntax()
+ .children_with_tokens()
+ .filter_map(NodeOrToken::into_token)
+ .filter(|t| t.kind() == T![,])
+ .take_while(|t| t.text_range().start() <= token.text_range().start())
+ .count(),
+ );
+
+ let db = sema.db;
+ let mut res = SignatureHelp {
+ doc: None,
+ signature: String::from('('),
+ parameters: vec![],
+ active_parameter,
+ };
+ let expr = sema.type_of_expr(&expr.into())?;
+ let fields = expr.original.tuple_fields(db);
let mut buf = String::new();
- for ty in fields.into_iter().map(|it| it.ty(db)) {
+ for ty in fields {
format_to!(buf, "{}", ty.display_truncated(db, Some(20)));
res.push_call_param(&buf);
buf.clear();
}
- res.signature.push_str(")");
+ res.signature.push(')');
Some(res)
}
@@ -465,8 +524,8 @@ fn signature_help_for_record_(
token: SyntaxToken,
) -> Option<SignatureHelp> {
let active_parameter = field_list_children
- .filter_map(syntax::NodeOrToken::into_token)
- .filter(|t| t.kind() == syntax::T![,])
+ .filter_map(NodeOrToken::into_token)
+ .filter(|t| t.kind() == T![,])
.take_while(|t| t.text_range().start() <= token.text_range().start())
.count();
@@ -486,7 +545,12 @@ fn signature_help_for_record_(
let en = variant.parent_enum(db);
res.doc = en.docs(db).map(|it| it.into());
- format_to!(res.signature, "enum {}::{} {{ ", en.name(db), variant.name(db));
+ format_to!(
+ res.signature,
+ "enum {}::{} {{ ",
+ en.name(db).display(db),
+ variant.name(db).display(db)
+ );
} else {
let adt = match path_res {
PathResolution::SelfType(imp) => imp.self_ty(db).as_adt()?,
@@ -498,12 +562,12 @@ fn signature_help_for_record_(
hir::Adt::Struct(it) => {
fields = it.fields(db);
res.doc = it.docs(db).map(|it| it.into());
- format_to!(res.signature, "struct {} {{ ", it.name(db));
+ format_to!(res.signature, "struct {} {{ ", it.name(db).display(db));
}
hir::Adt::Union(it) => {
fields = it.fields(db);
res.doc = it.docs(db).map(|it| it.into());
- format_to!(res.signature, "union {} {{ ", it.name(db));
+ format_to!(res.signature, "union {} {{ ", it.name(db).display(db));
}
_ => return None,
}
@@ -514,7 +578,7 @@ fn signature_help_for_record_(
let mut buf = String::new();
for (field, ty) in fields2 {
let name = field.name(db);
- format_to!(buf, "{name}: {}", ty.display_truncated(db, Some(20)));
+ format_to!(buf, "{}: {}", name.display(db), ty.display_truncated(db, Some(20)));
res.push_record_field(&buf);
buf.clear();
@@ -524,7 +588,7 @@ fn signature_help_for_record_(
}
for (name, field) in fields {
let Some(field) = field else { continue };
- format_to!(buf, "{name}: {}", field.ty(db).display_truncated(db, Some(20)));
+ format_to!(buf, "{}: {}", name.display(db), field.ty(db).display_truncated(db, Some(20)));
res.push_record_field(&buf);
buf.clear();
}
@@ -532,6 +596,46 @@ fn signature_help_for_record_(
Some(res)
}
+fn signature_help_for_tuple_pat_ish(
+ db: &RootDatabase,
+ mut res: SignatureHelp,
+ pat: &SyntaxNode,
+ token: SyntaxToken,
+ mut field_pats: AstChildren<ast::Pat>,
+ fields: impl ExactSizeIterator<Item = hir::Type>,
+) -> SignatureHelp {
+ let rest_pat = field_pats.find(|it| matches!(it, ast::Pat::RestPat(_)));
+ let is_left_of_rest_pat =
+ rest_pat.map_or(true, |it| token.text_range().start() < it.syntax().text_range().end());
+
+ let commas = pat
+ .children_with_tokens()
+ .filter_map(NodeOrToken::into_token)
+ .filter(|t| t.kind() == T![,]);
+
+ res.active_parameter = {
+ Some(if is_left_of_rest_pat {
+ commas.take_while(|t| t.text_range().start() <= token.text_range().start()).count()
+ } else {
+ let n_commas = commas
+ .collect::<Vec<_>>()
+ .into_iter()
+ .rev()
+ .take_while(|t| t.text_range().start() > token.text_range().start())
+ .count();
+ fields.len().saturating_sub(1).saturating_sub(n_commas)
+ })
+ };
+
+ let mut buf = String::new();
+ for ty in fields {
+ format_to!(buf, "{}", ty.display_truncated(db, Some(20)));
+ res.push_call_param(&buf);
+ buf.clear();
+ }
+ res.signature.push_str(")");
+ res
+}
#[cfg(test)]
mod tests {
use std::iter;
@@ -1228,6 +1332,24 @@ fn main() {
}
#[test]
+ fn call_info_for_fn_def_over_reference() {
+ check(
+ r#"
+struct S;
+fn foo(s: S) -> i32 { 92 }
+fn main() {
+ let bar = &&&&&foo;
+ bar($0);
+}
+ "#,
+ expect![[r#"
+ fn foo(s: S) -> i32
+ ^^^^
+ "#]],
+ )
+ }
+
+ #[test]
fn call_info_for_fn_ptr() {
check(
r#"
@@ -1823,4 +1945,290 @@ fn main() {
"#]],
);
}
+
+ #[test]
+ fn test_tuple_expr_free() {
+ check(
+ r#"
+fn main() {
+ (0$0, 1, 3);
+}
+"#,
+ expect![[r#"
+ (i32, i32, i32)
+ ^^^ --- ---
+ "#]],
+ );
+ check(
+ r#"
+fn main() {
+ ($0 1, 3);
+}
+"#,
+ expect![[r#"
+ (i32, i32)
+ ^^^ ---
+ "#]],
+ );
+ check(
+ r#"
+fn main() {
+ (1, 3 $0);
+}
+"#,
+ expect![[r#"
+ (i32, i32)
+ --- ^^^
+ "#]],
+ );
+ check(
+ r#"
+fn main() {
+ (1, 3 $0,);
+}
+"#,
+ expect![[r#"
+ (i32, i32)
+ --- ^^^
+ "#]],
+ );
+ }
+
+ #[test]
+ fn test_tuple_expr_expected() {
+ check(
+ r#"
+fn main() {
+ let _: (&str, u32, u32)= ($0, 1, 3);
+}
+"#,
+ expect![[r#"
+ (&str, u32, u32)
+ ^^^^ --- ---
+ "#]],
+ );
+ // FIXME: Should typeck report a 4-ary tuple for the expression here?
+ check(
+ r#"
+fn main() {
+ let _: (&str, u32, u32, u32) = ($0, 1, 3);
+}
+"#,
+ expect![[r#"
+ (&str, u32, u32)
+ ^^^^ --- ---
+ "#]],
+ );
+ check(
+ r#"
+fn main() {
+ let _: (&str, u32, u32)= ($0, 1, 3, 5);
+}
+"#,
+ expect![[r#"
+ (&str, u32, u32, i32)
+ ^^^^ --- --- ---
+ "#]],
+ );
+ }
+
+ #[test]
+ fn test_tuple_pat_free() {
+ check(
+ r#"
+fn main() {
+ let ($0, 1, 3);
+}
+"#,
+ expect![[r#"
+ ({unknown}, i32, i32)
+ ^^^^^^^^^ --- ---
+ "#]],
+ );
+ check(
+ r#"
+fn main() {
+ let (0$0, 1, 3);
+}
+"#,
+ expect![[r#"
+ (i32, i32, i32)
+ ^^^ --- ---
+ "#]],
+ );
+ check(
+ r#"
+fn main() {
+ let ($0 1, 3);
+}
+"#,
+ expect![[r#"
+ (i32, i32)
+ ^^^ ---
+ "#]],
+ );
+ check(
+ r#"
+fn main() {
+ let (1, 3 $0);
+}
+"#,
+ expect![[r#"
+ (i32, i32)
+ --- ^^^
+ "#]],
+ );
+ check(
+ r#"
+fn main() {
+ let (1, 3 $0,);
+}
+"#,
+ expect![[r#"
+ (i32, i32)
+ --- ^^^
+ "#]],
+ );
+ check(
+ r#"
+fn main() {
+ let (1, 3 $0, ..);
+}
+"#,
+ expect![[r#"
+ (i32, i32)
+ --- ^^^
+ "#]],
+ );
+ check(
+ r#"
+fn main() {
+ let (1, 3, .., $0);
+}
+"#,
+ // FIXME: This is wrong, this should not mark the last as active
+ expect![[r#"
+ (i32, i32)
+ --- ^^^
+ "#]],
+ );
+ }
+
+ #[test]
+ fn test_tuple_pat_expected() {
+ check(
+ r#"
+fn main() {
+ let (0$0, 1, 3): (i32, i32, i32);
+}
+"#,
+ expect![[r#"
+ (i32, i32, i32)
+ ^^^ --- ---
+ "#]],
+ );
+ check(
+ r#"
+fn main() {
+ let ($0, 1, 3): (i32, i32, i32);
+}
+"#,
+ expect![[r#"
+ (i32, i32, i32)
+ ^^^ --- ---
+ "#]],
+ );
+ check(
+ r#"
+fn main() {
+ let (1, 3 $0): (i32,);
+}
+"#,
+ expect![[r#"
+ (i32, i32)
+ --- ^^^
+ "#]],
+ );
+ check(
+ r#"
+fn main() {
+ let (1, 3 $0, ..): (i32, i32, i32, i32);
+}
+"#,
+ expect![[r#"
+ (i32, i32, i32, i32)
+ --- ^^^ --- ---
+ "#]],
+ );
+ check(
+ r#"
+fn main() {
+ let (1, 3, .., $0): (i32, i32, i32);
+}
+"#,
+ expect![[r#"
+ (i32, i32, i32)
+ --- --- ^^^
+ "#]],
+ );
+ }
+ #[test]
+ fn test_tuple_pat_expected_inferred() {
+ check(
+ r#"
+fn main() {
+ let (0$0, 1, 3) = (1, 2 ,3);
+}
+"#,
+ expect![[r#"
+ (i32, i32, i32)
+ ^^^ --- ---
+ "#]],
+ );
+ check(
+ r#"
+fn main() {
+ let ($0 1, 3) = (1, 2, 3);
+}
+"#,
+ // FIXME: Should typeck report a 3-ary tuple for the pattern here?
+ expect![[r#"
+ (i32, i32)
+ ^^^ ---
+ "#]],
+ );
+ check(
+ r#"
+fn main() {
+ let (1, 3 $0) = (1,);
+}
+"#,
+ expect![[r#"
+ (i32, i32)
+ --- ^^^
+ "#]],
+ );
+ check(
+ r#"
+fn main() {
+ let (1, 3 $0, ..) = (1, 2, 3, 4);
+}
+"#,
+ expect![[r#"
+ (i32, i32, i32, i32)
+ --- ^^^ --- ---
+ "#]],
+ );
+ check(
+ r#"
+fn main() {
+ let (1, 3, .., $0) = (1, 2, 3);
+}
+"#,
+ expect![[r#"
+ (i32, i32, i32)
+ --- --- ^^^
+ "#]],
+ );
+ }
}