5413: Semantical call info r=matklad a=matklad



Co-authored-by: Aleksey Kladov <aleksey.kladov@gmail.com>
This commit is contained in:
bors[bot] 2020-07-16 16:50:37 +00:00 committed by GitHub
commit 081596dd58
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
18 changed files with 458 additions and 319 deletions

View File

@ -1,5 +1,5 @@
//! FIXME: write short doc here
use std::sync::Arc;
use std::{iter, sync::Arc};
use arrayvec::ArrayVec;
use either::Either;
@ -12,6 +12,7 @@ use hir_def::{
import_map,
per_ns::PerNs,
resolver::{HasResolver, Resolver},
src::HasSource as _,
type_ref::{Mutability, TypeRef},
AdtId, AssocContainerId, ConstId, DefWithBodyId, EnumId, FunctionId, GenericDefId, HasModule,
ImplId, LocalEnumVariantId, LocalFieldId, LocalModuleId, Lookup, ModuleId, StaticId, StructId,
@ -25,8 +26,8 @@ use hir_expand::{
use hir_ty::{
autoderef,
display::{HirDisplayError, HirFormatter},
method_resolution, ApplicationTy, Canonical, GenericPredicate, InEnvironment, Substs,
TraitEnvironment, Ty, TyDefId, TypeCtor,
method_resolution, ApplicationTy, CallableDefId, Canonical, FnSig, GenericPredicate,
InEnvironment, Substs, TraitEnvironment, Ty, TyDefId, TypeCtor,
};
use ra_db::{CrateId, Edition, FileId};
use ra_prof::profile;
@ -40,7 +41,7 @@ use stdx::impl_from;
use crate::{
db::{DefDatabase, HirDatabase},
has_source::HasSource,
CallableDefId, HirDisplay, InFile, Name,
HirDisplay, InFile, Name,
};
/// hir::Crate describes a single crate. It's the main interface with which
@ -1168,6 +1169,12 @@ impl Type {
Type::new(db, krate, def, ty)
}
pub fn is_unit(&self) -> bool {
matches!(
self.ty.value,
Ty::Apply(ApplicationTy { ctor: TypeCtor::Tuple { cardinality: 0 }, .. })
)
}
pub fn is_bool(&self) -> bool {
matches!(self.ty.value, Ty::Apply(ApplicationTy { ctor: TypeCtor::Bool, .. }))
}
@ -1225,9 +1232,10 @@ impl Type {
db.trait_solve(self.krate, goal).is_some()
}
// FIXME: this method is broken, as it doesn't take closures into account.
pub fn as_callable(&self) -> Option<CallableDefId> {
Some(self.ty.value.as_callable()?.0)
pub fn as_callable(&self, db: &dyn HirDatabase) -> Option<Callable> {
let (id, substs) = self.ty.value.as_callable()?;
let sig = db.callable_item_signature(id).subst(substs);
Some(Callable { ty: self.clone(), sig, id, is_bound_method: false })
}
pub fn is_closure(&self) -> bool {
@ -1512,6 +1520,70 @@ impl HirDisplay for Type {
}
}
// FIXME: closures
#[derive(Debug)]
pub struct Callable {
ty: Type,
sig: FnSig,
id: CallableDefId,
pub(crate) is_bound_method: bool,
}
pub enum CallableKind {
Function(Function),
TupleStruct(Struct),
TupleEnumVariant(EnumVariant),
}
impl Callable {
pub fn kind(&self) -> CallableKind {
match self.id {
CallableDefId::FunctionId(it) => CallableKind::Function(it.into()),
CallableDefId::StructId(it) => CallableKind::TupleStruct(it.into()),
CallableDefId::EnumVariantId(it) => CallableKind::TupleEnumVariant(it.into()),
}
}
pub fn receiver_param(&self, db: &dyn HirDatabase) -> Option<ast::SelfParam> {
let func = match self.id {
CallableDefId::FunctionId(it) if self.is_bound_method => it,
_ => return None,
};
let src = func.lookup(db.upcast()).source(db.upcast());
let param_list = src.value.param_list()?;
param_list.self_param()
}
pub fn params(
&self,
db: &dyn HirDatabase,
) -> Vec<(Option<Either<ast::SelfParam, ast::Pat>>, Type)> {
let types = self
.sig
.params()
.iter()
.skip(if self.is_bound_method { 1 } else { 0 })
.map(|ty| self.ty.derived(ty.clone()));
let patterns = match self.id {
CallableDefId::FunctionId(func) => {
let src = func.lookup(db.upcast()).source(db.upcast());
src.value.param_list().map(|param_list| {
param_list
.self_param()
.map(|it| Some(Either::Left(it)))
.filter(|_| !self.is_bound_method)
.into_iter()
.chain(param_list.params().map(|it| it.pat().map(Either::Right)))
})
}
CallableDefId::StructId(_) => None,
CallableDefId::EnumVariantId(_) => None,
};
patterns.into_iter().flatten().chain(iter::repeat(None)).zip(types).collect()
}
pub fn return_type(&self) -> Type {
self.ty.derived(self.sig.ret().clone())
}
}
/// For IDE only
#[derive(Debug)]
pub enum ScopeDef {

View File

@ -32,10 +32,10 @@ mod has_source;
pub use crate::{
code_model::{
Adt, AsAssocItem, AssocItem, AssocItemContainer, AttrDef, Const, Crate, CrateDependency,
DefWithBody, Docs, Enum, EnumVariant, Field, FieldSource, Function, GenericDef, HasAttrs,
HasVisibility, ImplDef, Local, MacroDef, Module, ModuleDef, ScopeDef, Static, Struct,
Trait, Type, TypeAlias, TypeParam, Union, VariantDef, Visibility,
Adt, AsAssocItem, AssocItem, AssocItemContainer, AttrDef, Callable, CallableKind, Const,
Crate, CrateDependency, DefWithBody, Docs, Enum, EnumVariant, Field, FieldSource, Function,
GenericDef, HasAttrs, HasVisibility, ImplDef, Local, MacroDef, Module, ModuleDef, ScopeDef,
Static, Struct, Trait, Type, TypeAlias, TypeParam, Union, VariantDef, Visibility,
},
has_source::HasSource,
semantics::{original_range, PathResolution, Semantics, SemanticsScope},
@ -52,7 +52,8 @@ pub use hir_def::{
type_ref::Mutability,
};
pub use hir_expand::{
hygiene::Hygiene, name::Name, HirFileId, InFile, MacroCallId, MacroCallLoc, MacroDefId,
hygiene::Hygiene, name::Name, HirFileId, InFile, MacroCallId, MacroCallLoc,
MacroDefId, /* FIXME */
MacroFile, Origin,
};
pub use hir_ty::{display::HirDisplay, CallableDefId};
pub use hir_ty::display::HirDisplay;

View File

@ -6,7 +6,7 @@ use std::{cell::RefCell, fmt, iter::successors};
use hir_def::{
resolver::{self, HasResolver, Resolver},
AsMacroCall, TraitId, VariantId,
AsMacroCall, FunctionId, TraitId, VariantId,
};
use hir_expand::{diagnostics::AstDiagnostic, hygiene::Hygiene, ExpansionInfo};
use hir_ty::associated_type_shorthand_candidates;
@ -24,8 +24,8 @@ use crate::{
diagnostics::Diagnostic,
semantics::source_to_def::{ChildContainer, SourceToDefCache, SourceToDefCtx},
source_analyzer::{resolve_hir_path, resolve_hir_path_qualifier, SourceAnalyzer},
AssocItem, Field, Function, HirFileId, ImplDef, InFile, Local, MacroDef, Module, ModuleDef,
Name, Origin, Path, ScopeDef, Trait, Type, TypeAlias, TypeParam, VariantDef,
AssocItem, Callable, Field, Function, HirFileId, ImplDef, InFile, Local, MacroDef, Module,
ModuleDef, Name, Origin, Path, ScopeDef, Trait, Type, TypeAlias, TypeParam, VariantDef,
};
use resolver::TypeNs;
@ -197,7 +197,11 @@ impl<'db, DB: HirDatabase> Semantics<'db, DB> {
}
pub fn resolve_method_call(&self, call: &ast::MethodCallExpr) -> Option<Function> {
self.imp.resolve_method_call(call)
self.imp.resolve_method_call(call).map(Function::from)
}
pub fn resolve_method_call_as_callable(&self, call: &ast::MethodCallExpr) -> Option<Callable> {
self.imp.resolve_method_call_as_callable(call)
}
pub fn resolve_field(&self, field: &ast::FieldExpr) -> Option<Field> {
@ -385,10 +389,21 @@ impl<'db> SemanticsImpl<'db> {
self.analyze(param.syntax()).type_of_self(self.db, &param)
}
fn resolve_method_call(&self, call: &ast::MethodCallExpr) -> Option<Function> {
fn resolve_method_call(&self, call: &ast::MethodCallExpr) -> Option<FunctionId> {
self.analyze(call.syntax()).resolve_method_call(self.db, call)
}
fn resolve_method_call_as_callable(&self, call: &ast::MethodCallExpr) -> Option<Callable> {
// FIXME: this erases Substs
let func = self.resolve_method_call(call)?;
let ty = self.db.value_ty(func.into());
let resolver = self.analyze(call.syntax()).resolver;
let ty = Type::new_with_resolver(self.db, &resolver, ty.value)?;
let mut res = ty.as_callable(self.db)?;
res.is_bound_method = true;
Some(res)
}
fn resolve_field(&self, field: &ast::FieldExpr) -> Option<Field> {
self.analyze(field.syntax()).resolve_field(self.db, field)
}

View File

@ -14,7 +14,7 @@ use hir_def::{
},
expr::{ExprId, Pat, PatId},
resolver::{resolver_for_scope, Resolver, TypeNs, ValueNs},
AsMacroCall, DefWithBodyId, FieldId, LocalFieldId, VariantId,
AsMacroCall, DefWithBodyId, FieldId, FunctionId, LocalFieldId, VariantId,
};
use hir_expand::{hygiene::Hygiene, name::AsName, HirFileId, InFile};
use hir_ty::{
@ -142,9 +142,9 @@ impl SourceAnalyzer {
&self,
db: &dyn HirDatabase,
call: &ast::MethodCallExpr,
) -> Option<Function> {
) -> Option<FunctionId> {
let expr_id = self.expr_id(db, &call.clone().into())?;
self.infer.as_ref()?.method_resolution(expr_id).map(Function::from)
self.infer.as_ref()?.method_resolution(expr_id)
}
pub(crate) fn resolve_field(

View File

@ -226,7 +226,15 @@ impl CrateDefMap {
match enum_data.variant(&segment) {
Some(local_id) => {
let variant = EnumVariantId { parent: e, local_id };
PerNs::both(variant.into(), variant.into(), Visibility::Public)
match &*enum_data.variants[local_id].variant_data {
crate::adt::VariantData::Record(_) => {
PerNs::types(variant.into(), Visibility::Public)
}
crate::adt::VariantData::Tuple(_)
| crate::adt::VariantData::Unit => {
PerNs::both(variant.into(), variant.into(), Visibility::Public)
}
}
}
None => {
return ResolvePathResult::with(

View File

@ -95,9 +95,9 @@ pub(crate) fn outgoing_calls(db: &RootDatabase, position: FilePosition) -> Optio
if let Some(func_target) = match &call_node {
FnCallNode::CallExpr(expr) => {
//FIXME: Type::as_callable is broken
let callable_def = sema.type_of_expr(&expr.expr()?)?.as_callable()?;
match callable_def {
hir::CallableDefId::FunctionId(it) => {
let callable = sema.type_of_expr(&expr.expr()?)?.as_callable(db)?;
match callable.kind() {
hir::CallableKind::Function(it) => {
let fn_def: hir::Function = it.into();
let nav = fn_def.to_nav(db);
Some(nav)
@ -109,10 +109,6 @@ pub(crate) fn outgoing_calls(db: &RootDatabase, position: FilePosition) -> Optio
let function = sema.resolve_method_call(&expr)?;
Some(function.to_nav(db))
}
FnCallNode::MacroCallExpr(macro_call) => {
let macro_def = sema.resolve_macro_call(&macro_call)?;
Some(macro_def.to_nav(db))
}
} {
Some((func_target, name_ref.syntax().text_range()))
} else {

View File

@ -1,20 +1,42 @@
//! FIXME: write short doc here
use hir::Semantics;
use either::Either;
use hir::{Docs, HirDisplay, Semantics, Type};
use ra_ide_db::RootDatabase;
use ra_syntax::{
ast::{self, ArgListOwner},
match_ast, AstNode, SyntaxNode, SyntaxToken,
match_ast, AstNode, SyntaxNode, SyntaxToken, TextRange, TextSize,
};
use stdx::format_to;
use test_utils::mark;
use crate::{FilePosition, FunctionSignature};
use crate::FilePosition;
/// Contains information about a call site. Specifically the
/// `FunctionSignature`and current parameter.
#[derive(Debug)]
pub struct CallInfo {
pub signature: FunctionSignature,
pub doc: Option<String>,
pub signature: String,
pub active_parameter: Option<usize>,
parameters: Vec<TextRange>,
}
impl CallInfo {
pub fn parameter_labels(&self) -> impl Iterator<Item = &str> + '_ {
self.parameters.iter().map(move |&it| &self.signature[it])
}
pub fn parameter_ranges(&self) -> &[TextRange] {
&self.parameters
}
fn push_param(&mut self, param: &str) {
if !self.signature.ends_with('(') {
self.signature.push_str(", ");
}
let start = TextSize::of(&self.signature);
self.signature.push_str(param);
let end = TextSize::of(&self.signature);
self.parameters.push(TextRange::new(start, end))
}
}
/// Computes parameter information for the given call expression.
@ -24,105 +46,130 @@ pub(crate) fn call_info(db: &RootDatabase, position: FilePosition) -> Option<Cal
let file = file.syntax();
let token = file.token_at_offset(position.offset).next()?;
let token = sema.descend_into_macros(token);
call_info_for_token(&sema, token)
let (callable, active_parameter) = call_info_impl(&sema, token)?;
let mut res =
CallInfo { doc: None, signature: String::new(), parameters: vec![], active_parameter };
match callable.kind() {
hir::CallableKind::Function(func) => {
res.doc = func.docs(db).map(|it| it.as_str().to_string());
format_to!(res.signature, "fn {}", func.name(db));
}
hir::CallableKind::TupleStruct(strukt) => {
res.doc = strukt.docs(db).map(|it| it.as_str().to_string());
format_to!(res.signature, "struct {}", strukt.name(db));
}
hir::CallableKind::TupleEnumVariant(variant) => {
res.doc = variant.docs(db).map(|it| it.as_str().to_string());
format_to!(
res.signature,
"enum {}::{}",
variant.parent_enum(db).name(db),
variant.name(db)
);
}
}
res.signature.push('(');
{
if let Some(self_param) = callable.receiver_param(db) {
format_to!(res.signature, "{}", self_param)
}
let mut buf = String::new();
for (pat, ty) in callable.params(db) {
buf.clear();
if let Some(pat) = pat {
match pat {
Either::Left(_self) => format_to!(buf, "self: "),
Either::Right(pat) => format_to!(buf, "{}: ", pat),
}
}
format_to!(buf, "{}", ty.display(db));
res.push_param(&buf);
}
}
res.signature.push(')');
match callable.kind() {
hir::CallableKind::Function(_) => {
let ret_type = callable.return_type();
if !ret_type.is_unit() {
format_to!(res.signature, " -> {}", ret_type.display(db));
}
}
hir::CallableKind::TupleStruct(_) | hir::CallableKind::TupleEnumVariant(_) => {}
}
Some(res)
}
fn call_info_impl(
sema: &Semantics<RootDatabase>,
token: SyntaxToken,
) -> Option<(hir::Callable, Option<usize>)> {
// Find the calling expression and it's NameRef
let calling_node = FnCallNode::with_node(&token.parent())?;
let callable = match &calling_node {
FnCallNode::CallExpr(call) => sema.type_of_expr(&call.expr()?)?.as_callable(sema.db)?,
FnCallNode::MethodCallExpr(call) => sema.resolve_method_call_as_callable(call)?,
};
let active_param = if let Some(arg_list) = calling_node.arg_list() {
// Number of arguments specified at the call site
let num_args_at_callsite = arg_list.args().count();
let arg_list_range = arg_list.syntax().text_range();
if !arg_list_range.contains_inclusive(token.text_range().start()) {
mark::hit!(call_info_bad_offset);
return None;
}
let param = std::cmp::min(
num_args_at_callsite,
arg_list
.args()
.take_while(|arg| arg.syntax().text_range().end() <= token.text_range().start())
.count(),
);
Some(param)
} else {
None
};
Some((callable, active_param))
}
#[derive(Debug)]
pub(crate) struct ActiveParameter {
/// FIXME: should be `Type` and `Name
pub(crate) ty: String,
pub(crate) ty: Type,
pub(crate) name: String,
}
impl ActiveParameter {
pub(crate) fn at(db: &RootDatabase, position: FilePosition) -> Option<Self> {
call_info(db, position)?.into_active_parameter()
let sema = Semantics::new(db);
let file = sema.parse(position.file_id);
let file = file.syntax();
let token = file.token_at_offset(position.offset).next()?;
let token = sema.descend_into_macros(token);
Self::at_token(&sema, token)
}
pub(crate) fn at_token(sema: &Semantics<RootDatabase>, token: SyntaxToken) -> Option<Self> {
call_info_for_token(sema, token)?.into_active_parameter()
let (signature, active_parameter) = call_info_impl(&sema, token)?;
let idx = active_parameter?;
let mut params = signature.params(sema.db);
let (pat, ty) = params.swap_remove(idx);
let name = pat?.to_string();
Some(ActiveParameter { ty, name })
}
}
fn call_info_for_token(sema: &Semantics<RootDatabase>, token: SyntaxToken) -> Option<CallInfo> {
// Find the calling expression and it's NameRef
let calling_node = FnCallNode::with_node(&token.parent())?;
let signature = match &calling_node {
FnCallNode::CallExpr(call) => {
//FIXME: Type::as_callable is broken
let callable_def = sema.type_of_expr(&call.expr()?)?.as_callable()?;
match callable_def {
hir::CallableDefId::FunctionId(it) => {
let fn_def = it.into();
FunctionSignature::from_hir(sema.db, fn_def)
}
hir::CallableDefId::StructId(it) => {
FunctionSignature::from_struct(sema.db, it.into())?
}
hir::CallableDefId::EnumVariantId(it) => {
FunctionSignature::from_enum_variant(sema.db, it.into())?
}
}
}
FnCallNode::MethodCallExpr(method_call) => {
let function = sema.resolve_method_call(&method_call)?;
FunctionSignature::from_hir(sema.db, function)
}
FnCallNode::MacroCallExpr(macro_call) => {
let macro_def = sema.resolve_macro_call(&macro_call)?;
FunctionSignature::from_macro(sema.db, macro_def)?
}
};
// If we have a calling expression let's find which argument we are on
let num_params = signature.parameters.len();
let active_parameter = match num_params {
0 => None,
1 if signature.has_self_param => None,
1 => Some(0),
_ => {
if let Some(arg_list) = calling_node.arg_list() {
// Number of arguments specified at the call site
let num_args_at_callsite = arg_list.args().count();
let arg_list_range = arg_list.syntax().text_range();
if !arg_list_range.contains_inclusive(token.text_range().start()) {
mark::hit!(call_info_bad_offset);
return None;
}
let mut param = std::cmp::min(
num_args_at_callsite,
arg_list
.args()
.take_while(|arg| {
arg.syntax().text_range().end() <= token.text_range().start()
})
.count(),
);
// If we are in a method account for `self`
if signature.has_self_param {
param += 1;
}
Some(param)
} else {
None
}
}
};
Some(CallInfo { signature, active_parameter })
}
#[derive(Debug)]
pub(crate) enum FnCallNode {
CallExpr(ast::CallExpr),
MethodCallExpr(ast::MethodCallExpr),
MacroCallExpr(ast::MacroCall),
}
impl FnCallNode {
@ -138,7 +185,6 @@ impl FnCallNode {
}
Some(FnCallNode::MethodCallExpr(it))
},
ast::MacroCall(it) => Some(FnCallNode::MacroCallExpr(it)),
_ => None,
}
}
@ -150,7 +196,6 @@ impl FnCallNode {
match node {
ast::CallExpr(it) => Some(FnCallNode::CallExpr(it)),
ast::MethodCallExpr(it) => Some(FnCallNode::MethodCallExpr(it)),
ast::MacroCall(it) => Some(FnCallNode::MacroCallExpr(it)),
_ => None,
}
}
@ -166,8 +211,6 @@ impl FnCallNode {
FnCallNode::MethodCallExpr(call_expr) => {
call_expr.syntax().children().filter_map(ast::NameRef::cast).next()
}
FnCallNode::MacroCallExpr(call_expr) => call_expr.path()?.segment()?.name_ref(),
}
}
@ -175,21 +218,10 @@ impl FnCallNode {
match self {
FnCallNode::CallExpr(expr) => expr.arg_list(),
FnCallNode::MethodCallExpr(expr) => expr.arg_list(),
FnCallNode::MacroCallExpr(_) => None,
}
}
}
impl CallInfo {
fn into_active_parameter(self) -> Option<ActiveParameter> {
let idx = self.active_parameter?;
let ty = self.signature.parameter_types.get(idx)?.clone();
let name = self.signature.parameter_names.get(idx)?.clone();
let res = ActiveParameter { ty, name };
Some(res)
}
}
#[cfg(test)]
mod tests {
use expect::{expect, Expect};
@ -202,20 +234,18 @@ mod tests {
let call_info = analysis.call_info(position).unwrap();
let actual = match call_info {
Some(call_info) => {
let docs = match &call_info.signature.doc {
let docs = match &call_info.doc {
None => "".to_string(),
Some(docs) => format!("{}\n------\n", docs.as_str()),
};
let params = call_info
.signature
.parameters
.iter()
.parameter_labels()
.enumerate()
.map(|(i, param)| {
if Some(i) == call_info.active_parameter {
format!("<{}>", param)
} else {
param.clone()
param.to_string()
}
})
.collect::<Vec<_>>()
@ -296,10 +326,8 @@ fn foo<T, U: Copy + Display>(x: T, y: U) -> u32
fn bar() { foo(<|>3, ); }
"#,
expect![[r#"
fn foo<T, U: Copy + Display>(x: T, y: U) -> u32
where T: Copy + Display,
U: Debug
(<x: T>, y: U)
fn foo(x: i32, y: {unknown}) -> u32
(<x: i32>, y: {unknown})
"#]],
);
}
@ -312,8 +340,7 @@ fn foo<T>() -> T where T: Copy + Display {}
fn bar() { foo(<|>); }
"#,
expect![[r#"
fn foo<T>() -> T
where T: Copy + Display
fn foo() -> {unknown}
()
"#]],
);
@ -323,11 +350,14 @@ fn bar() { foo(<|>); }
fn test_fn_signature_for_impl() {
check(
r#"
struct F; impl F { pub fn new() { F{}} }
fn bar() {let _ : F = F::new(<|>);}
struct F;
impl F { pub fn new() { } }
fn bar() {
let _ : F = F::new(<|>);
}
"#,
expect![[r#"
pub fn new()
fn new()
()
"#]],
);
@ -346,8 +376,8 @@ fn bar() {
}
"#,
expect![[r#"
pub fn do_it(&self)
(&self)
fn do_it(&self)
()
"#]],
);
}
@ -357,16 +387,33 @@ fn bar() {
check(
r#"
struct S;
impl S { pub fn do_it(&self, x: i32) {} }
fn bar() {
let s: S = S;
s.do_it(<|>);
impl S {
fn foo(&self, x: i32) {}
}
fn main() { S.foo(<|>); }
"#,
expect![[r#"
pub fn do_it(&self, x: i32)
(&self, <x: i32>)
fn foo(&self, x: i32)
(<x: i32>)
"#]],
);
}
#[test]
fn test_fn_signature_for_method_with_arg_as_assoc_fn() {
check(
r#"
struct S;
impl S {
fn foo(&self, x: i32) {}
}
fn main() { S::foo(<|>); }
"#,
expect![[r#"
fn foo(self: &S, x: i32)
(<self: &S>, x: i32)
"#]],
);
}
@ -425,7 +472,7 @@ pub fn do() {
assert_eq!(6, my_crate::add_one(5));
```
------
pub fn add_one(x: i32) -> i32
fn add_one(x: i32) -> i32
(<x: i32>)
"##]],
);
@ -467,7 +514,7 @@ pub fn do_it() {
assert_eq!(6, my_crate::add_one(5));
```
------
pub fn add_one(x: i32) -> i32
fn add_one(x: i32) -> i32
(<x: i32>)
"##]],
);
@ -505,8 +552,8 @@ pub fn foo(mut r: WriteHandler<()>) {
By default this method stops actor's `Context`.
------
fn finished(&mut self, ctx: &mut Self::Context)
(&mut self, <ctx: &mut Self::Context>)
fn finished(&mut self, ctx: &mut {unknown})
(<ctx: &mut {unknown}>)
"#]],
);
}
@ -539,7 +586,7 @@ fn main() {
"#,
expect![[r#"
fn bar(&self, _: u32)
(&self, <_: u32>)
(<_: u32>)
"#]],
);
}
@ -549,15 +596,15 @@ fn main() {
check(
r#"
/// A cool tuple struct
struct TS(u32, i32);
struct S(u32, i32);
fn main() {
let s = TS(0, <|>);
let s = S(0, <|>);
}
"#,
expect![[r#"
A cool tuple struct
------
struct TS(u32, i32) -> TS
struct S(u32, i32)
(u32, <i32>)
"#]],
);
@ -567,31 +614,18 @@ fn main() {
fn generic_struct() {
check(
r#"
struct TS<T>(T);
struct S<T>(T);
fn main() {
let s = TS(<|>);
let s = S(<|>);
}
"#,
expect![[r#"
struct TS<T>(T) -> TS
(<T>)
struct S({unknown})
(<{unknown}>)
"#]],
);
}
#[test]
fn cant_call_named_structs() {
check(
r#"
struct TS { x: u32, y: i32 }
fn main() {
let s = TS(<|>);
}
"#,
expect![[""]],
);
}
#[test]
fn works_for_enum_variants() {
check(
@ -612,14 +646,27 @@ fn main() {
expect![[r#"
A Variant
------
E::A(0: i32)
(<0: i32>)
enum E::A(i32)
(<i32>)
"#]],
);
}
#[test]
fn cant_call_enum_records() {
fn cant_call_struct_record() {
check(
r#"
struct S { x: u32, y: i32 }
fn main() {
let s = S(<|>);
}
"#,
expect![[""]],
);
}
#[test]
fn cant_call_enum_record() {
check(
r#"
enum E {
@ -639,28 +686,6 @@ fn main() {
);
}
#[test]
fn fn_signature_for_macro() {
check(
r#"
/// empty macro
macro_rules! foo {
() => {}
}
fn f() {
foo!(<|>);
}
"#,
expect![[r#"
empty macro
------
foo!()
()
"#]],
);
}
#[test]
fn fn_signature_for_call_in_macro() {
check(

View File

@ -43,7 +43,7 @@ use crate::{
completion::{
CompletionContext, CompletionItem, CompletionItemKind, CompletionKind, Completions,
},
display::FunctionSignature,
display::function_signature::FunctionSignature,
};
pub(crate) fn complete_trait_impl(acc: &mut Completions, ctx: &CompletionContext) {

View File

@ -11,7 +11,7 @@ use crate::{
completion_item::Builder, CompletionContext, CompletionItem, CompletionItemKind,
CompletionKind, Completions,
},
display::{const_label, macro_label, type_label, FunctionSignature},
display::{const_label, function_signature::FunctionSignature, macro_label, type_label},
CompletionScore, RootDatabase,
};
@ -329,15 +329,10 @@ pub(crate) fn compute_score(
ty: &Type,
name: &str,
) -> Option<CompletionScore> {
// FIXME: this should not fall back to string equality.
let ty = &ty.display(ctx.db).to_string();
let (active_name, active_type) = if let Some(record_field) = &ctx.record_field_syntax {
mark::hit!(record_field_type_match);
let (struct_field, _local) = ctx.sema.resolve_record_field(record_field)?;
(
struct_field.name(ctx.db).to_string(),
struct_field.signature_ty(ctx.db).display(ctx.db).to_string(),
)
(struct_field.name(ctx.db).to_string(), struct_field.signature_ty(ctx.db))
} else if let Some(active_parameter) = &ctx.active_parameter {
mark::hit!(active_param_type_match);
(active_parameter.name.clone(), active_parameter.ty.clone())

View File

@ -1,9 +1,8 @@
//! This module contains utilities for turning SyntaxNodes and HIR types
//! into types that may be used to render in a UI.
mod function_signature;
pub(crate) mod function_signature;
mod navigation_target;
mod structure;
mod short_label;
use ra_syntax::{
@ -11,15 +10,13 @@ use ra_syntax::{
SyntaxKind::{ATTR, COMMENT},
};
pub use function_signature::FunctionSignature;
pub use navigation_target::NavigationTarget;
pub use structure::{file_structure, StructureNode};
pub(crate) use navigation_target::{ToNav, TryToNav};
pub(crate) use short_label::ShortLabel;
pub use navigation_target::NavigationTarget;
pub(crate) fn function_label(node: &ast::FnDef) -> String {
FunctionSignature::from(node).to_string()
function_signature::FunctionSignature::from(node).to_string()
}
pub(crate) fn const_label(node: &ast::ConstDef) -> String {

View File

@ -15,49 +15,48 @@ use stdx::{split_delim, SepBy};
use crate::display::{generic_parameters, where_predicates};
#[derive(Debug)]
pub enum CallableKind {
pub(crate) enum CallableKind {
Function,
StructConstructor,
VariantConstructor,
Macro,
}
/// Contains information about a function signature
#[derive(Debug)]
pub struct FunctionSignature {
pub kind: CallableKind,
pub(crate) struct FunctionSignature {
pub(crate) kind: CallableKind,
/// Optional visibility
pub visibility: Option<String>,
pub(crate) visibility: Option<String>,
/// Qualifiers like `async`, `unsafe`, ...
pub qualifier: FunctionQualifier,
pub(crate) qualifier: FunctionQualifier,
/// Name of the function
pub name: Option<String>,
pub(crate) name: Option<String>,
/// Documentation for the function
pub doc: Option<Documentation>,
pub(crate) doc: Option<Documentation>,
/// Generic parameters
pub generic_parameters: Vec<String>,
pub(crate) generic_parameters: Vec<String>,
/// Parameters of the function
pub parameters: Vec<String>,
pub(crate) parameters: Vec<String>,
/// Parameter names of the function
pub parameter_names: Vec<String>,
pub(crate) parameter_names: Vec<String>,
/// Parameter types of the function
pub parameter_types: Vec<String>,
pub(crate) parameter_types: Vec<String>,
/// Optional return type
pub ret_type: Option<String>,
pub(crate) ret_type: Option<String>,
/// Where predicates
pub where_predicates: Vec<String>,
pub(crate) where_predicates: Vec<String>,
/// Self param presence
pub has_self_param: bool,
pub(crate) has_self_param: bool,
}
#[derive(Debug, Default)]
pub struct FunctionQualifier {
pub(crate) struct FunctionQualifier {
// `async` and `const` are mutually exclusive. Do we need to enforcing it here?
pub is_async: bool,
pub is_const: bool,
pub is_unsafe: bool,
pub(crate) is_async: bool,
pub(crate) is_const: bool,
pub(crate) is_unsafe: bool,
/// The string `extern ".."`
pub extern_abi: Option<String>,
pub(crate) extern_abi: Option<String>,
}
impl FunctionSignature {
@ -149,27 +148,6 @@ impl FunctionSignature {
has_self_param: false,
})
}
pub(crate) fn from_macro(db: &RootDatabase, macro_def: hir::MacroDef) -> Option<Self> {
let node: ast::MacroCall = macro_def.source(db).value;
let params = vec![];
Some(FunctionSignature {
kind: CallableKind::Macro,
visibility: None,
qualifier: Default::default(),
name: node.name().map(|n| n.text().to_string()),
ret_type: None,
parameters: params,
parameter_names: vec![],
parameter_types: vec![],
generic_parameters: vec![],
where_predicates: vec![],
doc: macro_def.docs(db),
has_self_param: false,
})
}
}
impl From<&'_ ast::FnDef> for FunctionSignature {
@ -298,7 +276,6 @@ impl Display for FunctionSignature {
CallableKind::Function => write!(f, "fn {}", name)?,
CallableKind::StructConstructor => write!(f, "struct {}", name)?,
CallableKind::VariantConstructor => write!(f, "{}", name)?,
CallableKind::Macro => write!(f, "{}!", name)?,
}
}

View File

@ -5,10 +5,10 @@ use ra_syntax::{
ast::{self, ArgListOwner, AstNode, TypeAscriptionOwner},
match_ast, Direction, NodeOrToken, SmolStr, SyntaxKind, TextRange, T,
};
use crate::{FileId, FunctionSignature};
use stdx::to_lower_snake_case;
use crate::{display::function_signature::FunctionSignature, FileId};
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct InlayHintsConfig {
pub type_hints: bool,
@ -322,15 +322,15 @@ fn get_fn_signature(sema: &Semantics<RootDatabase>, expr: &ast::Expr) -> Option<
match expr {
ast::Expr::CallExpr(expr) => {
// FIXME: Type::as_callable is broken for closures
let callable_def = sema.type_of_expr(&expr.expr()?)?.as_callable()?;
match callable_def {
hir::CallableDefId::FunctionId(it) => {
let callable = sema.type_of_expr(&expr.expr()?)?.as_callable(sema.db)?;
match callable.kind() {
hir::CallableKind::Function(it) => {
Some(FunctionSignature::from_hir(sema.db, it.into()))
}
hir::CallableDefId::StructId(it) => {
hir::CallableKind::TupleStruct(it) => {
FunctionSignature::from_struct(sema.db, it.into())
}
hir::CallableDefId::EnumVariantId(it) => {
hir::CallableKind::TupleEnumVariant(it) => {
FunctionSignature::from_enum_variant(sema.db, it.into())
}
}

View File

@ -19,29 +19,31 @@ pub mod mock_analysis;
mod markup;
mod prime_caches;
mod status;
mod completion;
mod runnables;
mod goto_definition;
mod goto_type_definition;
mod goto_implementation;
mod extend_selection;
mod hover;
mod display;
mod call_hierarchy;
mod call_info;
mod syntax_highlighting;
mod completion;
mod diagnostics;
mod expand_macro;
mod extend_selection;
mod file_structure;
mod folding_ranges;
mod goto_definition;
mod goto_implementation;
mod goto_type_definition;
mod hover;
mod inlay_hints;
mod join_lines;
mod matching_brace;
mod parent_module;
mod references;
mod diagnostics;
mod syntax_tree;
mod folding_ranges;
mod join_lines;
mod typing;
mod matching_brace;
mod display;
mod inlay_hints;
mod expand_macro;
mod runnables;
mod ssr;
mod status;
mod syntax_highlighting;
mod syntax_tree;
mod typing;
use std::sync::Arc;
@ -65,8 +67,9 @@ pub use crate::{
CompletionConfig, CompletionItem, CompletionItemKind, CompletionScore, InsertTextFormat,
},
diagnostics::Severity,
display::{file_structure, FunctionSignature, NavigationTarget, StructureNode},
display::NavigationTarget,
expand_macro::ExpandedMacro,
file_structure::StructureNode,
folding_ranges::{Fold, FoldKind},
hover::{HoverAction, HoverConfig, HoverGotoTypeData, HoverResult},
inlay_hints::{InlayHint, InlayHintsConfig, InlayKind},
@ -323,7 +326,7 @@ impl Analysis {
/// Returns a tree representation of symbols in the file. Useful to draw a
/// file outline.
pub fn file_structure(&self, file_id: FileId) -> Cancelable<Vec<StructureNode>> {
self.with_db(|db| file_structure(&db.parse(file_id).tree()))
self.with_db(|db| file_structure::file_structure(&db.parse(file_id).tree()))
}
/// Returns a list of the places in the file where type hints can be displayed.

View File

@ -10,7 +10,7 @@ mod ssr;
use std::io::Read;
use anyhow::Result;
use ra_ide::{file_structure, Analysis};
use ra_ide::Analysis;
use ra_prof::profile;
use ra_syntax::{AstNode, SourceFile};
@ -48,8 +48,10 @@ pub fn parse(no_dump: bool) -> Result<()> {
}
pub fn symbols() -> Result<()> {
let file = file()?;
for s in file_structure(&file) {
let text = read_stdin()?;
let (analysis, file_id) = Analysis::from_single_file(text);
let structure = analysis.file_structure(file_id).unwrap();
for s in structure {
println!("{:?}", s);
}
Ok(())

View File

@ -127,6 +127,7 @@ pub struct ClientCapsConfig {
pub resolve_code_action: bool,
pub hover_actions: bool,
pub status_notification: bool,
pub signature_help_label_offsets: bool,
}
impl Config {
@ -302,6 +303,15 @@ impl Config {
{
self.client_caps.code_action_literals = value;
}
if let Some(value) = doc_caps
.signature_help
.as_ref()
.and_then(|it| it.signature_information.as_ref())
.and_then(|it| it.parameter_information.as_ref())
.and_then(|it| it.label_offset_support)
{
self.client_caps.signature_help_label_offsets = value;
}
self.completion.allow_snippets(false);
if let Some(completion) = &doc_caps.completion {

View File

@ -553,21 +553,16 @@ pub(crate) fn handle_signature_help(
let _p = profile("handle_signature_help");
let position = from_proto::file_position(&snap, params.text_document_position_params)?;
let call_info = match snap.analysis.call_info(position)? {
None => return Ok(None),
Some(it) => it,
None => return Ok(None),
};
let concise = !snap.config.call_info_full;
let mut active_parameter = call_info.active_parameter.map(|it| it as i64);
if concise && call_info.signature.has_self_param {
active_parameter = active_parameter.map(|it| it.saturating_sub(1));
}
let sig_info = to_proto::signature_information(call_info.signature, concise);
Ok(Some(lsp_types::SignatureHelp {
signatures: vec![sig_info],
active_signature: Some(0),
active_parameter,
}))
let res = to_proto::signature_help(
call_info,
concise,
snap.config.client_caps.signature_help_label_offsets,
);
Ok(Some(res))
}
pub(crate) fn handle_hover(

View File

@ -4,8 +4,8 @@ use std::path::{self, Path};
use itertools::Itertools;
use ra_db::{FileId, FileRange};
use ra_ide::{
Assist, AssistKind, CompletionItem, CompletionItemKind, Documentation, FileSystemEdit, Fold,
FoldKind, FunctionSignature, Highlight, HighlightModifier, HighlightTag, HighlightedRange,
Assist, AssistKind, CallInfo, CompletionItem, CompletionItemKind, Documentation,
FileSystemEdit, Fold, FoldKind, Highlight, HighlightModifier, HighlightTag, HighlightedRange,
Indel, InlayHint, InlayKind, InsertTextFormat, LineIndex, Markup, NavigationTarget,
ReferenceAccess, ResolvedAssist, Runnable, Severity, SourceChange, SourceFileEdit, TextEdit,
};
@ -219,29 +219,72 @@ pub(crate) fn completion_item(
res
}
pub(crate) fn signature_information(
signature: FunctionSignature,
pub(crate) fn signature_help(
call_info: CallInfo,
concise: bool,
) -> lsp_types::SignatureInformation {
let (label, documentation, params) = if concise {
let mut params = signature.parameters;
if signature.has_self_param {
params.remove(0);
label_offsets: bool,
) -> lsp_types::SignatureHelp {
let (label, parameters) = match (concise, label_offsets) {
(_, false) => {
let params = call_info
.parameter_labels()
.map(|label| lsp_types::ParameterInformation {
label: lsp_types::ParameterLabel::Simple(label.to_string()),
documentation: None,
})
.collect::<Vec<_>>();
let label =
if concise { call_info.parameter_labels().join(", ") } else { call_info.signature };
(label, params)
}
(false, true) => {
let params = call_info
.parameter_ranges()
.iter()
.map(|it| [u32::from(it.start()).into(), u32::from(it.end()).into()])
.map(|label_offsets| lsp_types::ParameterInformation {
label: lsp_types::ParameterLabel::LabelOffsets(label_offsets),
documentation: None,
})
.collect::<Vec<_>>();
(call_info.signature, params)
}
(true, true) => {
let mut params = Vec::new();
let mut label = String::new();
let mut first = true;
for param in call_info.parameter_labels() {
if !first {
label.push_str(", ");
}
first = false;
let start = label.len() as u64;
label.push_str(param);
let end = label.len() as u64;
params.push(lsp_types::ParameterInformation {
label: lsp_types::ParameterLabel::LabelOffsets([start, end]),
documentation: None,
});
}
(label, params)
}
(params.join(", "), None, params)
} else {
(signature.to_string(), signature.doc.map(documentation), signature.parameters)
};
let parameters: Vec<lsp_types::ParameterInformation> = params
.into_iter()
.map(|param| lsp_types::ParameterInformation {
label: lsp_types::ParameterLabel::Simple(param),
documentation: None,
let documentation = call_info.doc.map(|doc| {
lsp_types::Documentation::MarkupContent(lsp_types::MarkupContent {
kind: lsp_types::MarkupKind::Markdown,
value: doc,
})
.collect();
});
lsp_types::SignatureInformation { label, documentation, parameters: Some(parameters) }
let signature =
lsp_types::SignatureInformation { label, documentation, parameters: Some(parameters) };
lsp_types::SignatureHelp {
signatures: vec![signature],
active_signature: None,
active_parameter: call_info.active_parameter.map(|it| it as i64),
}
}
pub(crate) fn inlay_int(line_index: &LineIndex, inlay_hint: InlayHint) -> lsp_ext::InlayHint {