• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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