]> git.lizzy.rs Git - rust.git/blob - crates/ide_db/src/active_parameter.rs
Merge #11157
[rust.git] / crates / ide_db / src / active_parameter.rs
1 //! This module provides functionality for querying callable information about a token.
2
3 use either::Either;
4 use hir::{Semantics, Type};
5 use syntax::{
6     ast::{self, HasArgList, HasName},
7     AstNode, SyntaxToken,
8 };
9
10 use crate::RootDatabase;
11
12 #[derive(Debug)]
13 pub struct ActiveParameter {
14     pub ty: Type,
15     pub pat: Either<ast::SelfParam, ast::Pat>,
16 }
17
18 impl ActiveParameter {
19     /// Returns information about the call argument this token is part of.
20     pub fn at_token(sema: &Semantics<RootDatabase>, token: SyntaxToken) -> Option<Self> {
21         let (signature, active_parameter) = callable_for_token(sema, token)?;
22
23         let idx = active_parameter?;
24         let mut params = signature.params(sema.db);
25         if !(idx < params.len()) {
26             cov_mark::hit!(too_many_arguments);
27             return None;
28         }
29         let (pat, ty) = params.swap_remove(idx);
30         pat.map(|pat| ActiveParameter { ty, pat })
31     }
32
33     pub fn ident(&self) -> Option<ast::Name> {
34         self.pat.as_ref().right().and_then(|param| match param {
35             ast::Pat::IdentPat(ident) => ident.name(),
36             _ => None,
37         })
38     }
39 }
40
41 /// Returns a [`hir::Callable`] this token is a part of and its argument index of said callable.
42 pub fn callable_for_token(
43     sema: &Semantics<RootDatabase>,
44     token: SyntaxToken,
45 ) -> Option<(hir::Callable, Option<usize>)> {
46     // Find the calling expression and it's NameRef
47     let parent = token.parent()?;
48     let calling_node = parent.ancestors().filter_map(ast::CallableExpr::cast).find(|it| {
49         it.arg_list()
50             .map_or(false, |it| it.syntax().text_range().contains(token.text_range().start()))
51     })?;
52
53     let callable = match &calling_node {
54         ast::CallableExpr::Call(call) => {
55             let expr = call.expr()?;
56             sema.type_of_expr(&expr)?.adjusted().as_callable(sema.db)
57         }
58         ast::CallableExpr::MethodCall(call) => sema.resolve_method_call_as_callable(call),
59     }?;
60     let active_param = if let Some(arg_list) = calling_node.arg_list() {
61         let param = arg_list
62             .args()
63             .take_while(|arg| arg.syntax().text_range().end() <= token.text_range().start())
64             .count();
65         Some(param)
66     } else {
67         None
68     };
69     Some((callable, active_param))
70 }