diff options
author | bors[bot] <26634292+bors[bot]@users.noreply.github.com> | 2019-10-29 13:48:26 +0000 |
---|---|---|
committer | GitHub <[email protected]> | 2019-10-29 13:48:26 +0000 |
commit | de16f94ada933cfd394ddab34c31410cf05268f1 (patch) | |
tree | f27b83617f56cb96f91b3a7ba37a5a5f41524606 /crates | |
parent | e38cdf6e56d963525fcc656b80965e7114756496 (diff) | |
parent | b915bf2d05e3edf7e23e595b2b95bdcdaa0907fd (diff) |
Merge #2103
2103: Expand signature help r=matklad a=kjeremy
Signature help using call syntax with tuple structs and enum variants
Fixes #2102.
Co-authored-by: Jeremy Kolb <[email protected]>
Co-authored-by: kjeremy <[email protected]>
Diffstat (limited to 'crates')
-rw-r--r-- | crates/ra_ide_api/src/call_info.rs | 105 | ||||
-rw-r--r-- | crates/ra_ide_api/src/display/function_signature.rs | 90 |
2 files changed, 184 insertions, 11 deletions
diff --git a/crates/ra_ide_api/src/call_info.rs b/crates/ra_ide_api/src/call_info.rs index c95133343..d947ac50c 100644 --- a/crates/ra_ide_api/src/call_info.rs +++ b/crates/ra_ide_api/src/call_info.rs | |||
@@ -20,24 +20,26 @@ pub(crate) fn call_info(db: &RootDatabase, position: FilePosition) -> Option<Cal | |||
20 | let name_ref = calling_node.name_ref()?; | 20 | let name_ref = calling_node.name_ref()?; |
21 | 21 | ||
22 | let analyzer = hir::SourceAnalyzer::new(db, position.file_id, name_ref.syntax(), None); | 22 | let analyzer = hir::SourceAnalyzer::new(db, position.file_id, name_ref.syntax(), None); |
23 | let function = match &calling_node { | 23 | let (mut call_info, has_self) = match &calling_node { |
24 | FnCallNode::CallExpr(expr) => { | 24 | FnCallNode::CallExpr(expr) => { |
25 | //FIXME: apply subst | 25 | //FIXME: apply subst |
26 | let (callable_def, _subst) = analyzer.type_of(db, &expr.expr()?)?.as_callable()?; | 26 | let (callable_def, _subst) = analyzer.type_of(db, &expr.expr()?)?.as_callable()?; |
27 | match callable_def { | 27 | match callable_def { |
28 | hir::CallableDef::Function(it) => it, | 28 | hir::CallableDef::Function(it) => { |
29 | //FIXME: handle other callables | 29 | (CallInfo::with_fn(db, it), it.data(db).has_self_param()) |
30 | _ => return None, | 30 | } |
31 | hir::CallableDef::Struct(it) => (CallInfo::with_struct(db, it)?, false), | ||
32 | hir::CallableDef::EnumVariant(it) => (CallInfo::with_enum_variant(db, it)?, false), | ||
31 | } | 33 | } |
32 | } | 34 | } |
33 | FnCallNode::MethodCallExpr(expr) => analyzer.resolve_method_call(&expr)?, | 35 | FnCallNode::MethodCallExpr(expr) => { |
36 | let function = analyzer.resolve_method_call(&expr)?; | ||
37 | (CallInfo::with_fn(db, function), function.data(db).has_self_param()) | ||
38 | } | ||
34 | }; | 39 | }; |
35 | 40 | ||
36 | let mut call_info = CallInfo::new(db, function); | ||
37 | |||
38 | // If we have a calling expression let's find which argument we are on | 41 | // If we have a calling expression let's find which argument we are on |
39 | let num_params = call_info.parameters().len(); | 42 | let num_params = call_info.parameters().len(); |
40 | let has_self = function.data(db).has_self_param(); | ||
41 | 43 | ||
42 | if num_params == 1 { | 44 | if num_params == 1 { |
43 | if !has_self { | 45 | if !has_self { |
@@ -115,12 +117,24 @@ impl FnCallNode { | |||
115 | } | 117 | } |
116 | 118 | ||
117 | impl CallInfo { | 119 | impl CallInfo { |
118 | fn new(db: &RootDatabase, function: hir::Function) -> Self { | 120 | fn with_fn(db: &RootDatabase, function: hir::Function) -> Self { |
119 | let signature = FunctionSignature::from_hir(db, function); | 121 | let signature = FunctionSignature::from_hir(db, function); |
120 | 122 | ||
121 | CallInfo { signature, active_parameter: None } | 123 | CallInfo { signature, active_parameter: None } |
122 | } | 124 | } |
123 | 125 | ||
126 | fn with_struct(db: &RootDatabase, st: hir::Struct) -> Option<Self> { | ||
127 | let signature = FunctionSignature::from_struct(db, st)?; | ||
128 | |||
129 | Some(CallInfo { signature, active_parameter: None }) | ||
130 | } | ||
131 | |||
132 | fn with_enum_variant(db: &RootDatabase, variant: hir::EnumVariant) -> Option<Self> { | ||
133 | let signature = FunctionSignature::from_enum_variant(db, variant)?; | ||
134 | |||
135 | Some(CallInfo { signature, active_parameter: None }) | ||
136 | } | ||
137 | |||
124 | fn parameters(&self) -> &[String] { | 138 | fn parameters(&self) -> &[String] { |
125 | &self.signature.parameters | 139 | &self.signature.parameters |
126 | } | 140 | } |
@@ -462,4 +476,77 @@ fn main() { | |||
462 | assert_eq!(info.active_parameter, Some(1)); | 476 | assert_eq!(info.active_parameter, Some(1)); |
463 | assert_eq!(info.label(), "fn bar(&self, _: u32)"); | 477 | assert_eq!(info.label(), "fn bar(&self, _: u32)"); |
464 | } | 478 | } |
479 | |||
480 | #[test] | ||
481 | fn works_for_tuple_structs() { | ||
482 | let info = call_info( | ||
483 | r#" | ||
484 | /// A cool tuple struct | ||
485 | struct TS(u32, i32); | ||
486 | fn main() { | ||
487 | let s = TS(0, <|>); | ||
488 | }"#, | ||
489 | ); | ||
490 | |||
491 | assert_eq!(info.label(), "struct TS(u32, i32) -> TS"); | ||
492 | assert_eq!(info.doc().map(|it| it.into()), Some("A cool tuple struct".to_string())); | ||
493 | assert_eq!(info.active_parameter, Some(1)); | ||
494 | } | ||
495 | |||
496 | #[test] | ||
497 | #[should_panic] | ||
498 | fn cant_call_named_structs() { | ||
499 | let _ = call_info( | ||
500 | r#" | ||
501 | struct TS { x: u32, y: i32 } | ||
502 | fn main() { | ||
503 | let s = TS(<|>); | ||
504 | }"#, | ||
505 | ); | ||
506 | } | ||
507 | |||
508 | #[test] | ||
509 | fn works_for_enum_variants() { | ||
510 | let info = call_info( | ||
511 | r#" | ||
512 | enum E { | ||
513 | /// A Variant | ||
514 | A(i32), | ||
515 | /// Another | ||
516 | B, | ||
517 | /// And C | ||
518 | C { a: i32, b: i32 } | ||
519 | } | ||
520 | |||
521 | fn main() { | ||
522 | let a = E::A(<|>); | ||
523 | } | ||
524 | "#, | ||
525 | ); | ||
526 | |||
527 | assert_eq!(info.label(), "E::A(0: i32)"); | ||
528 | assert_eq!(info.doc().map(|it| it.into()), Some("A Variant".to_string())); | ||
529 | assert_eq!(info.active_parameter, Some(0)); | ||
530 | } | ||
531 | |||
532 | #[test] | ||
533 | #[should_panic] | ||
534 | fn cant_call_enum_records() { | ||
535 | let _ = call_info( | ||
536 | r#" | ||
537 | enum E { | ||
538 | /// A Variant | ||
539 | A(i32), | ||
540 | /// Another | ||
541 | B, | ||
542 | /// And C | ||
543 | C { a: i32, b: i32 } | ||
544 | } | ||
545 | |||
546 | fn main() { | ||
547 | let a = E::C(<|>); | ||
548 | } | ||
549 | "#, | ||
550 | ); | ||
551 | } | ||
465 | } | 552 | } |
diff --git a/crates/ra_ide_api/src/display/function_signature.rs b/crates/ra_ide_api/src/display/function_signature.rs index 43f022ccd..e21f8378d 100644 --- a/crates/ra_ide_api/src/display/function_signature.rs +++ b/crates/ra_ide_api/src/display/function_signature.rs | |||
@@ -2,7 +2,7 @@ | |||
2 | 2 | ||
3 | use std::fmt::{self, Display}; | 3 | use std::fmt::{self, Display}; |
4 | 4 | ||
5 | use hir::{Docs, Documentation, HasSource}; | 5 | use hir::{Docs, Documentation, HasSource, HirDisplay}; |
6 | use join_to_string::join; | 6 | use join_to_string::join; |
7 | use ra_syntax::ast::{self, AstNode, NameOwner, VisibilityOwner}; | 7 | use ra_syntax::ast::{self, AstNode, NameOwner, VisibilityOwner}; |
8 | use std::convert::From; | 8 | use std::convert::From; |
@@ -12,9 +12,17 @@ use crate::{ | |||
12 | display::{generic_parameters, where_predicates}, | 12 | display::{generic_parameters, where_predicates}, |
13 | }; | 13 | }; |
14 | 14 | ||
15 | #[derive(Debug)] | ||
16 | pub enum CallableKind { | ||
17 | Function, | ||
18 | StructConstructor, | ||
19 | VariantConstructor, | ||
20 | } | ||
21 | |||
15 | /// Contains information about a function signature | 22 | /// Contains information about a function signature |
16 | #[derive(Debug)] | 23 | #[derive(Debug)] |
17 | pub struct FunctionSignature { | 24 | pub struct FunctionSignature { |
25 | pub kind: CallableKind, | ||
18 | /// Optional visibility | 26 | /// Optional visibility |
19 | pub visibility: Option<String>, | 27 | pub visibility: Option<String>, |
20 | /// Name of the function | 28 | /// Name of the function |
@@ -42,6 +50,79 @@ impl FunctionSignature { | |||
42 | let ast_node = function.source(db).ast; | 50 | let ast_node = function.source(db).ast; |
43 | FunctionSignature::from(&ast_node).with_doc_opt(doc) | 51 | FunctionSignature::from(&ast_node).with_doc_opt(doc) |
44 | } | 52 | } |
53 | |||
54 | pub(crate) fn from_struct(db: &db::RootDatabase, st: hir::Struct) -> Option<Self> { | ||
55 | let node: ast::StructDef = st.source(db).ast; | ||
56 | match node.kind() { | ||
57 | ast::StructKind::Named(_) => return None, | ||
58 | _ => (), | ||
59 | }; | ||
60 | |||
61 | let params = st | ||
62 | .fields(db) | ||
63 | .into_iter() | ||
64 | .map(|field: hir::StructField| { | ||
65 | let ty = field.ty(db); | ||
66 | format!("{}", ty.display(db)) | ||
67 | }) | ||
68 | .collect(); | ||
69 | |||
70 | Some( | ||
71 | FunctionSignature { | ||
72 | kind: CallableKind::StructConstructor, | ||
73 | visibility: node.visibility().map(|n| n.syntax().text().to_string()), | ||
74 | name: node.name().map(|n| n.text().to_string()), | ||
75 | ret_type: node.name().map(|n| n.text().to_string()), | ||
76 | parameters: params, | ||
77 | generic_parameters: generic_parameters(&node), | ||
78 | where_predicates: where_predicates(&node), | ||
79 | doc: None, | ||
80 | } | ||
81 | .with_doc_opt(st.docs(db)), | ||
82 | ) | ||
83 | } | ||
84 | |||
85 | pub(crate) fn from_enum_variant( | ||
86 | db: &db::RootDatabase, | ||
87 | variant: hir::EnumVariant, | ||
88 | ) -> Option<Self> { | ||
89 | let node: ast::EnumVariant = variant.source(db).ast; | ||
90 | match node.kind() { | ||
91 | ast::StructKind::Named(_) | ast::StructKind::Unit => return None, | ||
92 | _ => (), | ||
93 | }; | ||
94 | |||
95 | let parent_name = match variant.parent_enum(db).name(db) { | ||
96 | Some(name) => name.to_string(), | ||
97 | None => "missing".into(), | ||
98 | }; | ||
99 | |||
100 | let name = format!("{}::{}", parent_name, variant.name(db).unwrap()); | ||
101 | |||
102 | let params = variant | ||
103 | .fields(db) | ||
104 | .into_iter() | ||
105 | .map(|field: hir::StructField| { | ||
106 | let name = field.name(db); | ||
107 | let ty = field.ty(db); | ||
108 | format!("{}: {}", name, ty.display(db)) | ||
109 | }) | ||
110 | .collect(); | ||
111 | |||
112 | Some( | ||
113 | FunctionSignature { | ||
114 | kind: CallableKind::VariantConstructor, | ||
115 | visibility: None, | ||
116 | name: Some(name), | ||
117 | ret_type: None, | ||
118 | parameters: params, | ||
119 | generic_parameters: vec![], | ||
120 | where_predicates: vec![], | ||
121 | doc: None, | ||
122 | } | ||
123 | .with_doc_opt(variant.docs(db)), | ||
124 | ) | ||
125 | } | ||
45 | } | 126 | } |
46 | 127 | ||
47 | impl From<&'_ ast::FnDef> for FunctionSignature { | 128 | impl From<&'_ ast::FnDef> for FunctionSignature { |
@@ -59,6 +140,7 @@ impl From<&'_ ast::FnDef> for FunctionSignature { | |||
59 | } | 140 | } |
60 | 141 | ||
61 | FunctionSignature { | 142 | FunctionSignature { |
143 | kind: CallableKind::Function, | ||
62 | visibility: node.visibility().map(|n| n.syntax().text().to_string()), | 144 | visibility: node.visibility().map(|n| n.syntax().text().to_string()), |
63 | name: node.name().map(|n| n.text().to_string()), | 145 | name: node.name().map(|n| n.text().to_string()), |
64 | ret_type: node | 146 | ret_type: node |
@@ -81,7 +163,11 @@ impl Display for FunctionSignature { | |||
81 | } | 163 | } |
82 | 164 | ||
83 | if let Some(name) = &self.name { | 165 | if let Some(name) = &self.name { |
84 | write!(f, "fn {}", name)?; | 166 | match self.kind { |
167 | CallableKind::Function => write!(f, "fn {}", name)?, | ||
168 | CallableKind::StructConstructor => write!(f, "struct {}", name)?, | ||
169 | CallableKind::VariantConstructor => write!(f, "{}", name)?, | ||
170 | } | ||
85 | } | 171 | } |
86 | 172 | ||
87 | if !self.generic_parameters.is_empty() { | 173 | if !self.generic_parameters.is_empty() { |