|
2 | 2 | //! a call or use-site.
|
3 | 3 |
|
4 | 4 | use either::Either;
|
5 |
| -use hir::{HasAttrs, HirDisplay, Semantics}; |
6 |
| -use ide_db::{ |
7 |
| - active_parameter::{callable_for_node, generics_for_token}, |
8 |
| - base_db::FilePosition, |
9 |
| -}; |
| 5 | +use hir::{GenericParam, HasAttrs, HirDisplay, Semantics}; |
| 6 | +use ide_db::{active_parameter::callable_for_node, base_db::FilePosition}; |
10 | 7 | use stdx::format_to;
|
11 | 8 | use syntax::{
|
12 | 9 | algo,
|
@@ -73,8 +70,8 @@ pub(crate) fn signature_help(db: &RootDatabase, position: FilePosition) -> Optio
|
73 | 70 | return Some(help);
|
74 | 71 | }
|
75 | 72 |
|
76 |
| - if let Some((generic_def, active_parameter)) = generics_for_token(&sema, token.clone()) { |
77 |
| - return signature_help_for_generics(db, generic_def, active_parameter); |
| 73 | + if let Some(help) = signature_help_for_generics(&sema, &token) { |
| 74 | + return Some(help); |
78 | 75 | }
|
79 | 76 |
|
80 | 77 | None
|
@@ -167,17 +164,69 @@ fn signature_help_for_call(
|
167 | 164 | }
|
168 | 165 |
|
169 | 166 | fn signature_help_for_generics(
|
170 |
| - db: &RootDatabase, |
171 |
| - mut generics_def: hir::GenericDef, |
172 |
| - active_parameter: usize, |
| 167 | + sema: &Semantics<RootDatabase>, |
| 168 | + token: &SyntaxToken, |
173 | 169 | ) -> Option<SignatureHelp> {
|
| 170 | + let parent = token.parent()?; |
| 171 | + let arg_list = parent |
| 172 | + .ancestors() |
| 173 | + .filter_map(ast::GenericArgList::cast) |
| 174 | + .find(|list| list.syntax().text_range().contains(token.text_range().start()))?; |
| 175 | + |
| 176 | + let mut active_parameter = arg_list |
| 177 | + .generic_args() |
| 178 | + .take_while(|arg| arg.syntax().text_range().end() <= token.text_range().start()) |
| 179 | + .count(); |
| 180 | + |
| 181 | + let first_arg_is_non_lifetime = arg_list |
| 182 | + .generic_args() |
| 183 | + .next() |
| 184 | + .map_or(false, |arg| !matches!(arg, ast::GenericArg::LifetimeArg(_))); |
| 185 | + |
| 186 | + let mut generics_def = if let Some(path) = |
| 187 | + arg_list.syntax().ancestors().find_map(ast::Path::cast) |
| 188 | + { |
| 189 | + let res = sema.resolve_path(&path)?; |
| 190 | + let generic_def: hir::GenericDef = match res { |
| 191 | + hir::PathResolution::Def(hir::ModuleDef::Adt(it)) => it.into(), |
| 192 | + hir::PathResolution::Def(hir::ModuleDef::Function(it)) => it.into(), |
| 193 | + hir::PathResolution::Def(hir::ModuleDef::Trait(it)) => it.into(), |
| 194 | + hir::PathResolution::Def(hir::ModuleDef::TypeAlias(it)) => it.into(), |
| 195 | + hir::PathResolution::Def(hir::ModuleDef::Variant(it)) => it.into(), |
| 196 | + hir::PathResolution::Def(hir::ModuleDef::BuiltinType(_)) |
| 197 | + | hir::PathResolution::Def(hir::ModuleDef::Const(_)) |
| 198 | + | hir::PathResolution::Def(hir::ModuleDef::Macro(_)) |
| 199 | + | hir::PathResolution::Def(hir::ModuleDef::Module(_)) |
| 200 | + | hir::PathResolution::Def(hir::ModuleDef::Static(_)) => return None, |
| 201 | + hir::PathResolution::AssocItem(hir::AssocItem::Function(it)) => it.into(), |
| 202 | + hir::PathResolution::AssocItem(hir::AssocItem::TypeAlias(it)) => it.into(), |
| 203 | + hir::PathResolution::AssocItem(hir::AssocItem::Const(_)) => return None, |
| 204 | + hir::PathResolution::BuiltinAttr(_) |
| 205 | + | hir::PathResolution::ToolModule(_) |
| 206 | + | hir::PathResolution::Local(_) |
| 207 | + | hir::PathResolution::TypeParam(_) |
| 208 | + | hir::PathResolution::ConstParam(_) |
| 209 | + | hir::PathResolution::SelfType(_) => return None, |
| 210 | + }; |
| 211 | + |
| 212 | + generic_def |
| 213 | + } else if let Some(method_call) = arg_list.syntax().parent().and_then(ast::MethodCallExpr::cast) |
| 214 | + { |
| 215 | + // recv.method::<$0>() |
| 216 | + let method = sema.resolve_method_call(&method_call)?; |
| 217 | + method.into() |
| 218 | + } else { |
| 219 | + return None; |
| 220 | + }; |
| 221 | + |
174 | 222 | let mut res = SignatureHelp {
|
175 | 223 | doc: None,
|
176 | 224 | signature: String::new(),
|
177 | 225 | parameters: vec![],
|
178 |
| - active_parameter: Some(active_parameter), |
| 226 | + active_parameter: None, |
179 | 227 | };
|
180 | 228 |
|
| 229 | + let db = sema.db; |
181 | 230 | match generics_def {
|
182 | 231 | hir::GenericDef::Function(it) => {
|
183 | 232 | res.doc = it.docs(db).map(|it| it.into());
|
@@ -216,8 +265,16 @@ fn signature_help_for_generics(
|
216 | 265 | hir::GenericDef::Impl(_) | hir::GenericDef::Const(_) => return None,
|
217 | 266 | }
|
218 | 267 |
|
| 268 | + let params = generics_def.params(sema.db); |
| 269 | + let num_lifetime_params = |
| 270 | + params.iter().take_while(|param| matches!(param, GenericParam::LifetimeParam(_))).count(); |
| 271 | + if first_arg_is_non_lifetime { |
| 272 | + // Lifetime parameters were omitted. |
| 273 | + active_parameter += num_lifetime_params; |
| 274 | + } |
| 275 | + res.active_parameter = Some(active_parameter); |
| 276 | + |
219 | 277 | res.signature.push('<');
|
220 |
| - let params = generics_def.params(db); |
221 | 278 | let mut buf = String::new();
|
222 | 279 | for param in params {
|
223 | 280 | if let hir::GenericParam::TypeParam(ty) = param {
|
@@ -976,14 +1033,27 @@ fn f() {
|
976 | 1033 | fn test_generic_kinds() {
|
977 | 1034 | check(
|
978 | 1035 | r#"
|
979 |
| -fn callee<'a, const A: (), T, const C: u8>() {} |
| 1036 | +fn callee<'a, const A: u8, T, const C: u8>() {} |
980 | 1037 |
|
981 | 1038 | fn f() {
|
982 | 1039 | callee::<'static, $0
|
983 | 1040 | }
|
984 | 1041 | "#,
|
985 | 1042 | expect![[r#"
|
986 |
| - fn callee<'a, const A: (), T, const C: u8> |
| 1043 | + fn callee<'a, const A: u8, T, const C: u8> |
| 1044 | + -- ^^^^^^^^^^^ - ----------- |
| 1045 | + "#]], |
| 1046 | + ); |
| 1047 | + check( |
| 1048 | + r#" |
| 1049 | +fn callee<'a, const A: u8, T, const C: u8>() {} |
| 1050 | +
|
| 1051 | +fn f() { |
| 1052 | + callee::<NON_LIFETIME$0 |
| 1053 | +} |
| 1054 | + "#, |
| 1055 | + expect![[r#" |
| 1056 | + fn callee<'a, const A: u8, T, const C: u8> |
987 | 1057 | -- ^^^^^^^^^^^ - -----------
|
988 | 1058 | "#]],
|
989 | 1059 | );
|
|
0 commit comments