aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_ide_api
diff options
context:
space:
mode:
authorbors[bot] <26634292+bors[bot]@users.noreply.github.com>2019-10-29 13:48:26 +0000
committerGitHub <[email protected]>2019-10-29 13:48:26 +0000
commitde16f94ada933cfd394ddab34c31410cf05268f1 (patch)
treef27b83617f56cb96f91b3a7ba37a5a5f41524606 /crates/ra_ide_api
parente38cdf6e56d963525fcc656b80965e7114756496 (diff)
parentb915bf2d05e3edf7e23e595b2b95bdcdaa0907fd (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/ra_ide_api')
-rw-r--r--crates/ra_ide_api/src/call_info.rs105
-rw-r--r--crates/ra_ide_api/src/display/function_signature.rs90
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
117impl CallInfo { 119impl 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
485struct TS(u32, i32);
486fn 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#"
501struct TS { x: u32, y: i32 }
502fn main() {
503 let s = TS(<|>);
504}"#,
505 );
506 }
507
508 #[test]
509 fn works_for_enum_variants() {
510 let info = call_info(
511 r#"
512enum E {
513 /// A Variant
514 A(i32),
515 /// Another
516 B,
517 /// And C
518 C { a: i32, b: i32 }
519}
520
521fn 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#"
537enum E {
538 /// A Variant
539 A(i32),
540 /// Another
541 B,
542 /// And C
543 C { a: i32, b: i32 }
544}
545
546fn 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
3use std::fmt::{self, Display}; 3use std::fmt::{self, Display};
4 4
5use hir::{Docs, Documentation, HasSource}; 5use hir::{Docs, Documentation, HasSource, HirDisplay};
6use join_to_string::join; 6use join_to_string::join;
7use ra_syntax::ast::{self, AstNode, NameOwner, VisibilityOwner}; 7use ra_syntax::ast::{self, AstNode, NameOwner, VisibilityOwner};
8use std::convert::From; 8use 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)]
16pub 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)]
17pub struct FunctionSignature { 24pub 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
47impl From<&'_ ast::FnDef> for FunctionSignature { 128impl 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() {