1 //! FIXME: write short doc here
3 use ra_db::SourceDatabase;
5 algo::ancestors_at_offset,
6 ast::{self, ArgListOwner},
7 match_ast, AstNode, SyntaxNode, TextUnit,
9 use test_utils::tested_by;
11 use crate::{db::RootDatabase, CallInfo, FilePosition, FunctionSignature};
13 /// Computes parameter information for the given call expression.
14 pub(crate) fn call_info(db: &RootDatabase, position: FilePosition) -> Option<CallInfo> {
15 let parse = db.parse(position.file_id);
16 let syntax = parse.tree().syntax().clone();
18 // Find the calling expression and it's NameRef
19 let calling_node = FnCallNode::with_node(&syntax, position.offset)?;
20 let name_ref = calling_node.name_ref()?;
21 let name_ref = hir::InFile::new(position.file_id.into(), name_ref.syntax());
23 let analyzer = hir::SourceAnalyzer::new(db, name_ref, None);
24 let (mut call_info, has_self) = match &calling_node {
25 FnCallNode::CallExpr(expr) => {
26 //FIXME: Type::as_callable is broken
27 let callable_def = analyzer.type_of(db, &expr.expr()?)?.as_callable()?;
29 hir::CallableDef::FunctionId(it) => {
30 let fn_def = it.into();
31 (CallInfo::with_fn(db, fn_def), fn_def.has_self_param(db))
33 hir::CallableDef::StructId(it) => (CallInfo::with_struct(db, it.into())?, false),
34 hir::CallableDef::EnumVariantId(it) => {
35 (CallInfo::with_enum_variant(db, it.into())?, false)
39 FnCallNode::MethodCallExpr(expr) => {
40 let function = analyzer.resolve_method_call(&expr)?;
41 (CallInfo::with_fn(db, function), function.has_self_param(db))
43 FnCallNode::MacroCallExpr(expr) => {
44 let macro_def = analyzer.resolve_macro_call(db, name_ref.with_value(&expr))?;
45 (CallInfo::with_macro(db, macro_def)?, false)
49 // If we have a calling expression let's find which argument we are on
50 let num_params = call_info.parameters().len();
54 call_info.active_parameter = Some(0);
56 } else if num_params > 1 {
57 // Count how many parameters into the call we are.
58 if let Some(arg_list) = calling_node.arg_list() {
59 // Number of arguments specified at the call site
60 let num_args_at_callsite = arg_list.args().count();
62 let arg_list_range = arg_list.syntax().text_range();
63 if !arg_list_range.contains_inclusive(position.offset) {
64 tested_by!(call_info_bad_offset);
68 let mut param = std::cmp::min(
72 .take_while(|arg| arg.syntax().text_range().end() < position.offset)
76 // If we are in a method account for `self`
81 call_info.active_parameter = Some(param);
90 CallExpr(ast::CallExpr),
91 MethodCallExpr(ast::MethodCallExpr),
92 MacroCallExpr(ast::MacroCall),
96 fn with_node(syntax: &SyntaxNode, offset: TextUnit) -> Option<FnCallNode> {
97 ancestors_at_offset(syntax, offset).find_map(|node| {
100 ast::CallExpr(it) => { Some(FnCallNode::CallExpr(it)) },
101 ast::MethodCallExpr(it) => { Some(FnCallNode::MethodCallExpr(it)) },
102 ast::MacroCall(it) => { Some(FnCallNode::MacroCallExpr(it)) },
109 fn name_ref(&self) -> Option<ast::NameRef> {
111 FnCallNode::CallExpr(call_expr) => Some(match call_expr.expr()? {
112 ast::Expr::PathExpr(path_expr) => path_expr.path()?.segment()?.name_ref()?,
116 FnCallNode::MethodCallExpr(call_expr) => {
117 call_expr.syntax().children().filter_map(ast::NameRef::cast).nth(0)
120 FnCallNode::MacroCallExpr(call_expr) => call_expr.path()?.segment()?.name_ref(),
124 fn arg_list(&self) -> Option<ast::ArgList> {
126 FnCallNode::CallExpr(expr) => expr.arg_list(),
127 FnCallNode::MethodCallExpr(expr) => expr.arg_list(),
128 FnCallNode::MacroCallExpr(_) => None,
134 fn with_fn(db: &RootDatabase, function: hir::Function) -> Self {
135 let signature = FunctionSignature::from_hir(db, function);
137 CallInfo { signature, active_parameter: None }
140 fn with_struct(db: &RootDatabase, st: hir::Struct) -> Option<Self> {
141 let signature = FunctionSignature::from_struct(db, st)?;
143 Some(CallInfo { signature, active_parameter: None })
146 fn with_enum_variant(db: &RootDatabase, variant: hir::EnumVariant) -> Option<Self> {
147 let signature = FunctionSignature::from_enum_variant(db, variant)?;
149 Some(CallInfo { signature, active_parameter: None })
152 fn with_macro(db: &RootDatabase, macro_def: hir::MacroDef) -> Option<Self> {
153 let signature = FunctionSignature::from_macro(db, macro_def)?;
155 Some(CallInfo { signature, active_parameter: None })
158 fn parameters(&self) -> &[String] {
159 &self.signature.parameters
165 use test_utils::covers;
167 use crate::mock_analysis::single_file_with_position;
171 // These are only used when testing
173 fn doc(&self) -> Option<hir::Documentation> {
174 self.signature.doc.clone()
177 fn label(&self) -> String {
178 self.signature.to_string()
182 fn call_info(text: &str) -> CallInfo {
183 let (analysis, position) = single_file_with_position(text);
184 analysis.call_info(position).unwrap().unwrap()
188 fn test_fn_signature_two_args_firstx() {
189 let info = call_info(
190 r#"fn foo(x: u32, y: u32) -> u32 {x + y}
191 fn bar() { foo(<|>3, ); }"#,
194 assert_eq!(info.parameters(), ["x: u32", "y: u32"]);
195 assert_eq!(info.active_parameter, Some(0));
199 fn test_fn_signature_two_args_second() {
200 let info = call_info(
201 r#"fn foo(x: u32, y: u32) -> u32 {x + y}
202 fn bar() { foo(3, <|>); }"#,
205 assert_eq!(info.parameters(), ["x: u32", "y: u32"]);
206 assert_eq!(info.active_parameter, Some(1));
210 fn test_fn_signature_two_args_empty() {
211 let info = call_info(
212 r#"fn foo(x: u32, y: u32) -> u32 {x + y}
213 fn bar() { foo(<|>); }"#,
216 assert_eq!(info.parameters(), ["x: u32", "y: u32"]);
217 assert_eq!(info.active_parameter, Some(0));
221 fn test_fn_signature_two_args_first_generics() {
222 let info = call_info(
223 r#"fn foo<T, U: Copy + Display>(x: T, y: U) -> u32 where T: Copy + Display, U: Debug {x + y}
224 fn bar() { foo(<|>3, ); }"#,
227 assert_eq!(info.parameters(), ["x: T", "y: U"]);
231 fn foo<T, U: Copy + Display>(x: T, y: U) -> u32
232 where T: Copy + Display,
237 assert_eq!(info.active_parameter, Some(0));
241 fn test_fn_signature_no_params() {
242 let info = call_info(
243 r#"fn foo<T>() -> T where T: Copy + Display {}
244 fn bar() { foo(<|>); }"#,
247 assert!(info.parameters().is_empty());
252 where T: Copy + Display
256 assert!(info.active_parameter.is_none());
260 fn test_fn_signature_for_impl() {
261 let info = call_info(
262 r#"struct F; impl F { pub fn new() { F{}} }
263 fn bar() {let _ : F = F::new(<|>);}"#,
266 assert!(info.parameters().is_empty());
267 assert_eq!(info.active_parameter, None);
271 fn test_fn_signature_for_method_self() {
272 let info = call_info(
279 pub fn do_it(&self) {}
283 let f : F = F::new();
288 assert_eq!(info.parameters(), ["&self"]);
289 assert_eq!(info.active_parameter, None);
293 fn test_fn_signature_for_method_with_arg() {
294 let info = call_info(
301 pub fn do_it(&self, x: i32) {}
305 let f : F = F::new();
310 assert_eq!(info.parameters(), ["&self", "x: i32"]);
311 assert_eq!(info.active_parameter, Some(1));
315 fn test_fn_signature_with_docs_simple() {
316 let info = call_info(
320 fn foo(j: u32) -> u32 {
330 assert_eq!(info.parameters(), ["j: u32"]);
331 assert_eq!(info.active_parameter, Some(0));
332 assert_eq!(info.label(), "fn foo(j: u32) -> u32");
333 assert_eq!(info.doc().map(|it| it.into()), Some("test".to_string()));
337 fn test_fn_signature_with_docs() {
338 let info = call_info(
340 /// Adds one to the number given.
347 /// assert_eq!(6, my_crate::add_one(5));
349 pub fn add_one(x: i32) -> i32 {
358 assert_eq!(info.parameters(), ["x: i32"]);
359 assert_eq!(info.active_parameter, Some(0));
360 assert_eq!(info.label(), "pub fn add_one(x: i32) -> i32");
362 info.doc().map(|it| it.into()),
364 r#"Adds one to the number given.
371 assert_eq!(6, my_crate::add_one(5));
379 fn test_fn_signature_with_docs_impl() {
380 let info = call_info(
384 /// Adds one to the number given.
391 /// assert_eq!(6, my_crate::add_one(5));
393 pub fn add_one(x: i32) -> i32 {
404 assert_eq!(info.parameters(), ["x: i32"]);
405 assert_eq!(info.active_parameter, Some(0));
406 assert_eq!(info.label(), "pub fn add_one(x: i32) -> i32");
408 info.doc().map(|it| it.into()),
410 r#"Adds one to the number given.
417 assert_eq!(6, my_crate::add_one(5));
425 fn test_fn_signature_with_docs_from_actix() {
426 let info = call_info(
428 struct WriteHandler<E>;
430 impl<E> WriteHandler<E> {
431 /// Method is called when writer emits error.
433 /// If this method returns `ErrorAction::Continue` writer processing
434 /// continues otherwise stream processing stops.
435 fn error(&mut self, err: E, ctx: &mut Self::Context) -> Running {
439 /// Method is called when writer finishes.
441 /// By default this method stops actor's `Context`.
442 fn finished(&mut self, ctx: &mut Self::Context) {
447 pub fn foo(mut r: WriteHandler<()>) {
454 assert_eq!(info.label(), "fn finished(&mut self, ctx: &mut Self::Context)".to_string());
455 assert_eq!(info.parameters(), ["&mut self", "ctx: &mut Self::Context"]);
456 assert_eq!(info.active_parameter, Some(1));
458 info.doc().map(|it| it.into()),
460 r#"Method is called when writer finishes.
462 By default this method stops actor's `Context`."#
469 fn call_info_bad_offset() {
470 covers!(call_info_bad_offset);
471 let (analysis, position) = single_file_with_position(
472 r#"fn foo(x: u32, y: u32) -> u32 {x + y}
473 fn bar() { foo <|> (3, ); }"#,
475 let call_info = analysis.call_info(position).unwrap();
476 assert!(call_info.is_none());
480 fn test_nested_method_in_lamba() {
481 let info = call_info(
485 fn bar(&self, _: u32) { }
492 std::thread::spawn(move || foo.bar(<|>));
496 assert_eq!(info.parameters(), ["&self", "_: u32"]);
497 assert_eq!(info.active_parameter, Some(1));
498 assert_eq!(info.label(), "fn bar(&self, _: u32)");
502 fn works_for_tuple_structs() {
503 let info = call_info(
505 /// A cool tuple struct
512 assert_eq!(info.label(), "struct TS(u32, i32) -> TS");
513 assert_eq!(info.doc().map(|it| it.into()), Some("A cool tuple struct".to_string()));
514 assert_eq!(info.active_parameter, Some(1));
519 fn cant_call_named_structs() {
522 struct TS { x: u32, y: i32 }
530 fn works_for_enum_variants() {
531 let info = call_info(
548 assert_eq!(info.label(), "E::A(0: i32)");
549 assert_eq!(info.doc().map(|it| it.into()), Some("A Variant".to_string()));
550 assert_eq!(info.active_parameter, Some(0));
555 fn cant_call_enum_records() {
575 fn fn_signature_for_macro() {
576 let info = call_info(
589 assert_eq!(info.label(), "foo!()");
590 assert_eq!(info.doc().map(|it| it.into()), Some("empty macro".to_string()));