mirror of
https://github.com/rust-lang/rust.git
synced 2025-04-28 02:57:37 +00:00
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 <kjeremy@gmail.com> Co-authored-by: kjeremy <kjeremy@gmail.com>
This commit is contained in:
commit
de16f94ada
@ -20,24 +20,26 @@ pub(crate) fn call_info(db: &RootDatabase, position: FilePosition) -> Option<Cal
|
||||
let name_ref = calling_node.name_ref()?;
|
||||
|
||||
let analyzer = hir::SourceAnalyzer::new(db, position.file_id, name_ref.syntax(), None);
|
||||
let function = match &calling_node {
|
||||
let (mut call_info, has_self) = match &calling_node {
|
||||
FnCallNode::CallExpr(expr) => {
|
||||
//FIXME: apply subst
|
||||
let (callable_def, _subst) = analyzer.type_of(db, &expr.expr()?)?.as_callable()?;
|
||||
match callable_def {
|
||||
hir::CallableDef::Function(it) => it,
|
||||
//FIXME: handle other callables
|
||||
_ => return None,
|
||||
hir::CallableDef::Function(it) => {
|
||||
(CallInfo::with_fn(db, it), it.data(db).has_self_param())
|
||||
}
|
||||
hir::CallableDef::Struct(it) => (CallInfo::with_struct(db, it)?, false),
|
||||
hir::CallableDef::EnumVariant(it) => (CallInfo::with_enum_variant(db, it)?, false),
|
||||
}
|
||||
}
|
||||
FnCallNode::MethodCallExpr(expr) => analyzer.resolve_method_call(&expr)?,
|
||||
FnCallNode::MethodCallExpr(expr) => {
|
||||
let function = analyzer.resolve_method_call(&expr)?;
|
||||
(CallInfo::with_fn(db, function), function.data(db).has_self_param())
|
||||
}
|
||||
};
|
||||
|
||||
let mut call_info = CallInfo::new(db, function);
|
||||
|
||||
// If we have a calling expression let's find which argument we are on
|
||||
let num_params = call_info.parameters().len();
|
||||
let has_self = function.data(db).has_self_param();
|
||||
|
||||
if num_params == 1 {
|
||||
if !has_self {
|
||||
@ -115,12 +117,24 @@ impl FnCallNode {
|
||||
}
|
||||
|
||||
impl CallInfo {
|
||||
fn new(db: &RootDatabase, function: hir::Function) -> Self {
|
||||
fn with_fn(db: &RootDatabase, function: hir::Function) -> Self {
|
||||
let signature = FunctionSignature::from_hir(db, function);
|
||||
|
||||
CallInfo { signature, active_parameter: None }
|
||||
}
|
||||
|
||||
fn with_struct(db: &RootDatabase, st: hir::Struct) -> Option<Self> {
|
||||
let signature = FunctionSignature::from_struct(db, st)?;
|
||||
|
||||
Some(CallInfo { signature, active_parameter: None })
|
||||
}
|
||||
|
||||
fn with_enum_variant(db: &RootDatabase, variant: hir::EnumVariant) -> Option<Self> {
|
||||
let signature = FunctionSignature::from_enum_variant(db, variant)?;
|
||||
|
||||
Some(CallInfo { signature, active_parameter: None })
|
||||
}
|
||||
|
||||
fn parameters(&self) -> &[String] {
|
||||
&self.signature.parameters
|
||||
}
|
||||
@ -462,4 +476,77 @@ fn main() {
|
||||
assert_eq!(info.active_parameter, Some(1));
|
||||
assert_eq!(info.label(), "fn bar(&self, _: u32)");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn works_for_tuple_structs() {
|
||||
let info = call_info(
|
||||
r#"
|
||||
/// A cool tuple struct
|
||||
struct TS(u32, i32);
|
||||
fn main() {
|
||||
let s = TS(0, <|>);
|
||||
}"#,
|
||||
);
|
||||
|
||||
assert_eq!(info.label(), "struct TS(u32, i32) -> TS");
|
||||
assert_eq!(info.doc().map(|it| it.into()), Some("A cool tuple struct".to_string()));
|
||||
assert_eq!(info.active_parameter, Some(1));
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic]
|
||||
fn cant_call_named_structs() {
|
||||
let _ = call_info(
|
||||
r#"
|
||||
struct TS { x: u32, y: i32 }
|
||||
fn main() {
|
||||
let s = TS(<|>);
|
||||
}"#,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn works_for_enum_variants() {
|
||||
let info = call_info(
|
||||
r#"
|
||||
enum E {
|
||||
/// A Variant
|
||||
A(i32),
|
||||
/// Another
|
||||
B,
|
||||
/// And C
|
||||
C { a: i32, b: i32 }
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let a = E::A(<|>);
|
||||
}
|
||||
"#,
|
||||
);
|
||||
|
||||
assert_eq!(info.label(), "E::A(0: i32)");
|
||||
assert_eq!(info.doc().map(|it| it.into()), Some("A Variant".to_string()));
|
||||
assert_eq!(info.active_parameter, Some(0));
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic]
|
||||
fn cant_call_enum_records() {
|
||||
let _ = call_info(
|
||||
r#"
|
||||
enum E {
|
||||
/// A Variant
|
||||
A(i32),
|
||||
/// Another
|
||||
B,
|
||||
/// And C
|
||||
C { a: i32, b: i32 }
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let a = E::C(<|>);
|
||||
}
|
||||
"#,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -2,7 +2,7 @@
|
||||
|
||||
use std::fmt::{self, Display};
|
||||
|
||||
use hir::{Docs, Documentation, HasSource};
|
||||
use hir::{Docs, Documentation, HasSource, HirDisplay};
|
||||
use join_to_string::join;
|
||||
use ra_syntax::ast::{self, AstNode, NameOwner, VisibilityOwner};
|
||||
use std::convert::From;
|
||||
@ -12,9 +12,17 @@ use crate::{
|
||||
display::{generic_parameters, where_predicates},
|
||||
};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum CallableKind {
|
||||
Function,
|
||||
StructConstructor,
|
||||
VariantConstructor,
|
||||
}
|
||||
|
||||
/// Contains information about a function signature
|
||||
#[derive(Debug)]
|
||||
pub struct FunctionSignature {
|
||||
pub kind: CallableKind,
|
||||
/// Optional visibility
|
||||
pub visibility: Option<String>,
|
||||
/// Name of the function
|
||||
@ -42,6 +50,79 @@ impl FunctionSignature {
|
||||
let ast_node = function.source(db).ast;
|
||||
FunctionSignature::from(&ast_node).with_doc_opt(doc)
|
||||
}
|
||||
|
||||
pub(crate) fn from_struct(db: &db::RootDatabase, st: hir::Struct) -> Option<Self> {
|
||||
let node: ast::StructDef = st.source(db).ast;
|
||||
match node.kind() {
|
||||
ast::StructKind::Named(_) => return None,
|
||||
_ => (),
|
||||
};
|
||||
|
||||
let params = st
|
||||
.fields(db)
|
||||
.into_iter()
|
||||
.map(|field: hir::StructField| {
|
||||
let ty = field.ty(db);
|
||||
format!("{}", ty.display(db))
|
||||
})
|
||||
.collect();
|
||||
|
||||
Some(
|
||||
FunctionSignature {
|
||||
kind: CallableKind::StructConstructor,
|
||||
visibility: node.visibility().map(|n| n.syntax().text().to_string()),
|
||||
name: node.name().map(|n| n.text().to_string()),
|
||||
ret_type: node.name().map(|n| n.text().to_string()),
|
||||
parameters: params,
|
||||
generic_parameters: generic_parameters(&node),
|
||||
where_predicates: where_predicates(&node),
|
||||
doc: None,
|
||||
}
|
||||
.with_doc_opt(st.docs(db)),
|
||||
)
|
||||
}
|
||||
|
||||
pub(crate) fn from_enum_variant(
|
||||
db: &db::RootDatabase,
|
||||
variant: hir::EnumVariant,
|
||||
) -> Option<Self> {
|
||||
let node: ast::EnumVariant = variant.source(db).ast;
|
||||
match node.kind() {
|
||||
ast::StructKind::Named(_) | ast::StructKind::Unit => return None,
|
||||
_ => (),
|
||||
};
|
||||
|
||||
let parent_name = match variant.parent_enum(db).name(db) {
|
||||
Some(name) => name.to_string(),
|
||||
None => "missing".into(),
|
||||
};
|
||||
|
||||
let name = format!("{}::{}", parent_name, variant.name(db).unwrap());
|
||||
|
||||
let params = variant
|
||||
.fields(db)
|
||||
.into_iter()
|
||||
.map(|field: hir::StructField| {
|
||||
let name = field.name(db);
|
||||
let ty = field.ty(db);
|
||||
format!("{}: {}", name, ty.display(db))
|
||||
})
|
||||
.collect();
|
||||
|
||||
Some(
|
||||
FunctionSignature {
|
||||
kind: CallableKind::VariantConstructor,
|
||||
visibility: None,
|
||||
name: Some(name),
|
||||
ret_type: None,
|
||||
parameters: params,
|
||||
generic_parameters: vec![],
|
||||
where_predicates: vec![],
|
||||
doc: None,
|
||||
}
|
||||
.with_doc_opt(variant.docs(db)),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&'_ ast::FnDef> for FunctionSignature {
|
||||
@ -59,6 +140,7 @@ impl From<&'_ ast::FnDef> for FunctionSignature {
|
||||
}
|
||||
|
||||
FunctionSignature {
|
||||
kind: CallableKind::Function,
|
||||
visibility: node.visibility().map(|n| n.syntax().text().to_string()),
|
||||
name: node.name().map(|n| n.text().to_string()),
|
||||
ret_type: node
|
||||
@ -81,7 +163,11 @@ impl Display for FunctionSignature {
|
||||
}
|
||||
|
||||
if let Some(name) = &self.name {
|
||||
write!(f, "fn {}", name)?;
|
||||
match self.kind {
|
||||
CallableKind::Function => write!(f, "fn {}", name)?,
|
||||
CallableKind::StructConstructor => write!(f, "struct {}", name)?,
|
||||
CallableKind::VariantConstructor => write!(f, "{}", name)?,
|
||||
}
|
||||
}
|
||||
|
||||
if !self.generic_parameters.is_empty() {
|
||||
|
Loading…
Reference in New Issue
Block a user