1 //! This module provides functionality for querying callable information about a token.
2
3 use either::Either;
4 use hir::{Semantics, Type};
5 use parser::T;
6 use syntax::{
7 ast::{self, HasArgList, HasName},
8 match_ast, AstNode, NodeOrToken, SyntaxToken,
9 };
10
11 use crate::RootDatabase;
12
13 #[derive(Debug)]
14 pub struct ActiveParameter {
15 pub ty: Type,
16 pub pat: Option<Either<ast::SelfParam, ast::Pat>>,
17 }
18
19 impl ActiveParameter {
20 /// Returns information about the call argument this token is part of.
at_token(sema: &Semantics<'_, RootDatabase>, token: SyntaxToken) -> Option<Self>21 pub fn at_token(sema: &Semantics<'_, RootDatabase>, token: SyntaxToken) -> Option<Self> {
22 let (signature, active_parameter) = callable_for_token(sema, token)?;
23
24 let idx = active_parameter?;
25 let mut params = signature.params(sema.db);
26 if !(idx < params.len()) {
27 cov_mark::hit!(too_many_arguments);
28 return None;
29 }
30 let (pat, ty) = params.swap_remove(idx);
31 Some(ActiveParameter { ty, pat })
32 }
33
ident(&self) -> Option<ast::Name>34 pub fn ident(&self) -> Option<ast::Name> {
35 self.pat.as_ref().and_then(|param| match param {
36 Either::Right(ast::Pat::IdentPat(ident)) => ident.name(),
37 _ => None,
38 })
39 }
40 }
41
42 /// Returns a [`hir::Callable`] this token is a part of and its argument index of said callable.
callable_for_token( sema: &Semantics<'_, RootDatabase>, token: SyntaxToken, ) -> Option<(hir::Callable, Option<usize>)>43 pub fn callable_for_token(
44 sema: &Semantics<'_, RootDatabase>,
45 token: SyntaxToken,
46 ) -> Option<(hir::Callable, Option<usize>)> {
47 // Find the calling expression and its NameRef
48 let parent = token.parent()?;
49 let calling_node = parent.ancestors().filter_map(ast::CallableExpr::cast).find(|it| {
50 it.arg_list()
51 .map_or(false, |it| it.syntax().text_range().contains(token.text_range().start()))
52 })?;
53
54 callable_for_node(sema, &calling_node, &token)
55 }
56
callable_for_node( sema: &Semantics<'_, RootDatabase>, calling_node: &ast::CallableExpr, token: &SyntaxToken, ) -> Option<(hir::Callable, Option<usize>)>57 pub fn callable_for_node(
58 sema: &Semantics<'_, RootDatabase>,
59 calling_node: &ast::CallableExpr,
60 token: &SyntaxToken,
61 ) -> Option<(hir::Callable, Option<usize>)> {
62 let callable = match calling_node {
63 ast::CallableExpr::Call(call) => {
64 let expr = call.expr()?;
65 sema.type_of_expr(&expr)?.adjusted().as_callable(sema.db)
66 }
67 ast::CallableExpr::MethodCall(call) => sema.resolve_method_call_as_callable(call),
68 }?;
69 let active_param = if let Some(arg_list) = calling_node.arg_list() {
70 Some(
71 arg_list
72 .syntax()
73 .children_with_tokens()
74 .filter_map(NodeOrToken::into_token)
75 .filter(|t| t.kind() == T![,])
76 .take_while(|t| t.text_range().start() <= token.text_range().start())
77 .count(),
78 )
79 } else {
80 None
81 };
82 Some((callable, active_param))
83 }
84
generic_def_for_node( sema: &Semantics<'_, RootDatabase>, generic_arg_list: &ast::GenericArgList, token: &SyntaxToken, ) -> Option<(hir::GenericDef, usize, bool)>85 pub fn generic_def_for_node(
86 sema: &Semantics<'_, RootDatabase>,
87 generic_arg_list: &ast::GenericArgList,
88 token: &SyntaxToken,
89 ) -> Option<(hir::GenericDef, usize, bool)> {
90 let parent = generic_arg_list.syntax().parent()?;
91 let def = match_ast! {
92 match parent {
93 ast::PathSegment(ps) => {
94 let res = sema.resolve_path(&ps.parent_path())?;
95 let generic_def: hir::GenericDef = match res {
96 hir::PathResolution::Def(hir::ModuleDef::Adt(it)) => it.into(),
97 hir::PathResolution::Def(hir::ModuleDef::Function(it)) => it.into(),
98 hir::PathResolution::Def(hir::ModuleDef::Trait(it)) => it.into(),
99 hir::PathResolution::Def(hir::ModuleDef::TraitAlias(it)) => it.into(),
100 hir::PathResolution::Def(hir::ModuleDef::TypeAlias(it)) => it.into(),
101 hir::PathResolution::Def(hir::ModuleDef::Variant(it)) => it.into(),
102 hir::PathResolution::Def(hir::ModuleDef::BuiltinType(_))
103 | hir::PathResolution::Def(hir::ModuleDef::Const(_))
104 | hir::PathResolution::Def(hir::ModuleDef::Macro(_))
105 | hir::PathResolution::Def(hir::ModuleDef::Module(_))
106 | hir::PathResolution::Def(hir::ModuleDef::Static(_)) => return None,
107 hir::PathResolution::BuiltinAttr(_)
108 | hir::PathResolution::ToolModule(_)
109 | hir::PathResolution::Local(_)
110 | hir::PathResolution::TypeParam(_)
111 | hir::PathResolution::ConstParam(_)
112 | hir::PathResolution::SelfType(_)
113 | hir::PathResolution::DeriveHelper(_) => return None,
114 };
115
116 generic_def
117 },
118 ast::AssocTypeArg(_) => {
119 // FIXME: We don't record the resolutions for this anywhere atm
120 return None;
121 },
122 ast::MethodCallExpr(mcall) => {
123 // recv.method::<$0>()
124 let method = sema.resolve_method_call(&mcall)?;
125 method.into()
126 },
127 _ => return None,
128 }
129 };
130
131 let active_param = generic_arg_list
132 .syntax()
133 .children_with_tokens()
134 .filter_map(NodeOrToken::into_token)
135 .filter(|t| t.kind() == T![,])
136 .take_while(|t| t.text_range().start() <= token.text_range().start())
137 .count();
138
139 let first_arg_is_non_lifetime = generic_arg_list
140 .generic_args()
141 .next()
142 .map_or(false, |arg| !matches!(arg, ast::GenericArg::LifetimeArg(_)));
143
144 Some((def, active_param, first_arg_is_non_lifetime))
145 }
146