ide_db/
active_parameter.rs1use either::Either;
4use hir::{InFile, Semantics, Type};
5use parser::T;
6use span::TextSize;
7use syntax::{
8 AstNode, NodeOrToken, SyntaxToken,
9 ast::{self, AstChildren, HasArgList, HasAttrs, HasName},
10 match_ast,
11};
12
13use crate::RootDatabase;
14
15#[derive(Debug)]
16pub struct ActiveParameter<'db> {
17 pub ty: Type<'db>,
18 pub src: Option<InFile<Either<ast::SelfParam, ast::Param>>>,
19}
20
21impl<'db> ActiveParameter<'db> {
22 pub fn at_token(sema: &Semantics<'db, RootDatabase>, token: SyntaxToken) -> Option<Self> {
24 let (signature, active_parameter) = callable_for_token(sema, token)?;
25 Self::from_signature_and_active_parameter(sema, signature, active_parameter)
26 }
27
28 pub fn at_arg(
30 sema: &'db Semantics<'db, RootDatabase>,
31 list: ast::ArgList,
32 at: TextSize,
33 ) -> Option<Self> {
34 let (signature, active_parameter) = callable_for_arg_list(sema, list, at)?;
35 Self::from_signature_and_active_parameter(sema, signature, active_parameter)
36 }
37
38 fn from_signature_and_active_parameter(
39 sema: &Semantics<'db, RootDatabase>,
40 signature: hir::Callable<'db>,
41 active_parameter: Option<usize>,
42 ) -> Option<Self> {
43 let idx = active_parameter?;
44 let mut params = signature.params();
45 if idx >= params.len() {
46 cov_mark::hit!(too_many_arguments);
47 return None;
48 }
49 let param = params.swap_remove(idx);
50 Some(ActiveParameter { ty: param.ty().clone(), src: sema.source(param) })
51 }
52
53 pub fn ident(&self) -> Option<ast::Name> {
54 self.src.as_ref().and_then(|param| match param.value.as_ref().right()?.pat()? {
55 ast::Pat::IdentPat(ident) => ident.name(),
56 _ => None,
57 })
58 }
59
60 pub fn attrs(&self) -> Option<AstChildren<ast::Attr>> {
61 self.src.as_ref().and_then(|param| Some(param.value.as_ref().right()?.attrs()))
62 }
63}
64
65pub fn callable_for_token<'db>(
67 sema: &Semantics<'db, RootDatabase>,
68 token: SyntaxToken,
69) -> Option<(hir::Callable<'db>, Option<usize>)> {
70 let offset = token.text_range().start();
71 let parent = token.parent()?;
73 let calling_node = parent
74 .ancestors()
75 .filter_map(ast::CallableExpr::cast)
76 .find(|it| it.arg_list().is_some_and(|it| it.syntax().text_range().contains(offset)))?;
77
78 callable_for_node(sema, &calling_node, offset)
79}
80
81pub fn callable_for_arg_list<'db>(
83 sema: &Semantics<'db, RootDatabase>,
84 arg_list: ast::ArgList,
85 at: TextSize,
86) -> Option<(hir::Callable<'db>, Option<usize>)> {
87 debug_assert!(arg_list.syntax().text_range().contains(at));
88 let callable = arg_list.syntax().parent().and_then(ast::CallableExpr::cast)?;
89 callable_for_node(sema, &callable, at)
90}
91
92pub fn callable_for_node<'db>(
93 sema: &Semantics<'db, RootDatabase>,
94 calling_node: &ast::CallableExpr,
95 offset: TextSize,
96) -> Option<(hir::Callable<'db>, Option<usize>)> {
97 let callable = match calling_node {
98 ast::CallableExpr::Call(call) => sema.resolve_expr_as_callable(&call.expr()?),
99 ast::CallableExpr::MethodCall(call) => sema.resolve_method_call_as_callable(call),
100 }?;
101 let active_param = calling_node.arg_list().map(|arg_list| {
102 arg_list
103 .syntax()
104 .children_with_tokens()
105 .filter_map(NodeOrToken::into_token)
106 .filter(|t| t.kind() == T![,])
107 .take_while(|t| t.text_range().start() <= offset)
108 .count()
109 });
110 Some((callable, active_param))
111}
112
113pub fn generic_def_for_node(
114 sema: &Semantics<'_, RootDatabase>,
115 generic_arg_list: &ast::GenericArgList,
116 token: &SyntaxToken,
117) -> Option<(hir::GenericDef, usize, bool, Option<hir::Variant>)> {
118 let parent = generic_arg_list.syntax().parent()?;
119 let mut variant = None;
120 let def = match_ast! {
121 match parent {
122 ast::PathSegment(ps) => {
123 let res = sema.resolve_path(&ps.parent_path())?;
124 let generic_def: hir::GenericDef = match res {
125 hir::PathResolution::Def(hir::ModuleDef::Adt(it)) => it.into(),
126 hir::PathResolution::Def(hir::ModuleDef::Function(it)) => it.into(),
127 hir::PathResolution::Def(hir::ModuleDef::Trait(it)) => it.into(),
128 hir::PathResolution::Def(hir::ModuleDef::TypeAlias(it)) => it.into(),
129 hir::PathResolution::Def(hir::ModuleDef::Variant(it)) => {
130 variant = Some(it);
131 it.parent_enum(sema.db).into()
132 },
133 hir::PathResolution::Def(hir::ModuleDef::BuiltinType(_))
134 | hir::PathResolution::Def(hir::ModuleDef::Const(_))
135 | hir::PathResolution::Def(hir::ModuleDef::Macro(_))
136 | hir::PathResolution::Def(hir::ModuleDef::Module(_))
137 | hir::PathResolution::Def(hir::ModuleDef::Static(_)) => return None,
138 hir::PathResolution::BuiltinAttr(_)
139 | hir::PathResolution::ToolModule(_)
140 | hir::PathResolution::Local(_)
141 | hir::PathResolution::TypeParam(_)
142 | hir::PathResolution::ConstParam(_)
143 | hir::PathResolution::SelfType(_)
144 | hir::PathResolution::DeriveHelper(_) => return None,
145 };
146
147 generic_def
148 },
149 ast::AssocTypeArg(_) => {
150 return None;
152 },
153 ast::MethodCallExpr(mcall) => {
154 let method = sema.resolve_method_call(&mcall)?;
156 method.into()
157 },
158 _ => return None,
159 }
160 };
161
162 let active_param = generic_arg_list
163 .syntax()
164 .children_with_tokens()
165 .filter_map(NodeOrToken::into_token)
166 .filter(|t| t.kind() == T![,])
167 .take_while(|t| t.text_range().start() <= token.text_range().start())
168 .count();
169
170 let first_arg_is_non_lifetime = generic_arg_list
171 .generic_args()
172 .next()
173 .is_some_and(|arg| !matches!(arg, ast::GenericArg::LifetimeArg(_)));
174
175 Some((def, active_param, first_arg_is_non_lifetime, variant))
176}