|
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