Unnamed repository; edit this file 'description' to name the repository.
Diffstat (limited to 'crates/ide-db/src/active_parameter.rs')
| -rw-r--r-- | crates/ide-db/src/active_parameter.rs | 78 |
1 files changed, 78 insertions, 0 deletions
diff --git a/crates/ide-db/src/active_parameter.rs b/crates/ide-db/src/active_parameter.rs new file mode 100644 index 0000000000..788a5ed17e --- /dev/null +++ b/crates/ide-db/src/active_parameter.rs @@ -0,0 +1,78 @@ +//! This module provides functionality for querying callable information about a token. + +use either::Either; +use hir::{Semantics, Type}; +use syntax::{ + ast::{self, HasArgList, HasName}, + AstNode, SyntaxToken, +}; + +use crate::RootDatabase; + +#[derive(Debug)] +pub struct ActiveParameter { + pub ty: Type, + pub pat: Either<ast::SelfParam, ast::Pat>, +} + +impl ActiveParameter { + /// Returns information about the call argument this token is part of. + pub fn at_token(sema: &Semantics<RootDatabase>, token: SyntaxToken) -> Option<Self> { + let (signature, active_parameter) = callable_for_token(sema, token)?; + + let idx = active_parameter?; + let mut params = signature.params(sema.db); + if !(idx < params.len()) { + cov_mark::hit!(too_many_arguments); + return None; + } + let (pat, ty) = params.swap_remove(idx); + pat.map(|pat| ActiveParameter { ty, pat }) + } + + pub fn ident(&self) -> Option<ast::Name> { + self.pat.as_ref().right().and_then(|param| match param { + ast::Pat::IdentPat(ident) => ident.name(), + _ => None, + }) + } +} + +/// Returns a [`hir::Callable`] this token is a part of and its argument index of said callable. +pub fn callable_for_token( + sema: &Semantics<RootDatabase>, + token: SyntaxToken, +) -> Option<(hir::Callable, Option<usize>)> { + // Find the calling expression and its NameRef + let parent = token.parent()?; + let calling_node = parent.ancestors().filter_map(ast::CallableExpr::cast).find(|it| { + it.arg_list() + .map_or(false, |it| it.syntax().text_range().contains(token.text_range().start())) + })?; + + callable_for_node(sema, &calling_node, &token) +} + +pub fn callable_for_node( + sema: &Semantics<RootDatabase>, + calling_node: &ast::CallableExpr, + token: &SyntaxToken, +) -> Option<(hir::Callable, Option<usize>)> { + let callable = match &calling_node { + ast::CallableExpr::Call(call) => { + let expr = call.expr()?; + sema.type_of_expr(&expr)?.adjusted().as_callable(sema.db) + } + ast::CallableExpr::MethodCall(call) => sema.resolve_method_call_as_callable(call), + }?; + let active_param = if let Some(arg_list) = calling_node.arg_list() { + let param = arg_list + .args() + .take_while(|arg| arg.syntax().text_range().end() <= token.text_range().start()) + .count(); + Some(param) + } else { + None + }; + Some((callable, active_param)) +} |