From ff0312fa32715ce42f134fd9f049c4df5956d042 Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Thu, 16 Jul 2020 13:00:56 +0200 Subject: Semantical call info --- crates/ra_hir/src/code_model.rs | 76 +++++- crates/ra_hir/src/lib.rs | 13 +- crates/ra_hir/src/semantics.rs | 25 +- crates/ra_hir/src/source_analyzer.rs | 6 +- crates/ra_hir_def/src/nameres/path_resolution.rs | 10 +- crates/ra_ide/src/call_hierarchy.rs | 10 +- crates/ra_ide/src/call_info.rs | 332 ++++++++++++----------- crates/ra_ide/src/completion/presentation.rs | 7 +- crates/ra_ide/src/display/function_signature.rs | 21 -- crates/ra_ide/src/inlay_hints.rs | 10 +- crates/rust-analyzer/src/handlers.rs | 15 +- crates/rust-analyzer/src/to_proto.rs | 43 +-- 12 files changed, 310 insertions(+), 258 deletions(-) diff --git a/crates/ra_hir/src/code_model.rs b/crates/ra_hir/src/code_model.rs index 9891b0785..057dfb82a 100644 --- a/crates/ra_hir/src/code_model.rs +++ b/crates/ra_hir/src/code_model.rs @@ -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 { - Some(self.ty.value.as_callable()?.0) + pub fn as_callable(&self, db: &dyn HirDatabase) -> Option { + 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,60 @@ 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 { + 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, 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(|it| it.params().map(|it| it.pat())) + } + 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 { diff --git a/crates/ra_hir/src/lib.rs b/crates/ra_hir/src/lib.rs index cf7134923..31f3241c9 100644 --- a/crates/ra_hir/src/lib.rs +++ b/crates/ra_hir/src/lib.rs @@ -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; diff --git a/crates/ra_hir/src/semantics.rs b/crates/ra_hir/src/semantics.rs index 155b666d7..f5283ab22 100644 --- a/crates/ra_hir/src/semantics.rs +++ b/crates/ra_hir/src/semantics.rs @@ -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 { - 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 { + self.imp.resolve_method_call_as_callable(call) } pub fn resolve_field(&self, field: &ast::FieldExpr) -> Option { @@ -385,10 +389,21 @@ impl<'db> SemanticsImpl<'db> { self.analyze(param.syntax()).type_of_self(self.db, ¶m) } - fn resolve_method_call(&self, call: &ast::MethodCallExpr) -> Option { + fn resolve_method_call(&self, call: &ast::MethodCallExpr) -> Option { self.analyze(call.syntax()).resolve_method_call(self.db, call) } + fn resolve_method_call_as_callable(&self, call: &ast::MethodCallExpr) -> Option { + // 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 { self.analyze(field.syntax()).resolve_field(self.db, field) } diff --git a/crates/ra_hir/src/source_analyzer.rs b/crates/ra_hir/src/source_analyzer.rs index ecb54f653..86a47a9e5 100644 --- a/crates/ra_hir/src/source_analyzer.rs +++ b/crates/ra_hir/src/source_analyzer.rs @@ -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 { + ) -> Option { 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( diff --git a/crates/ra_hir_def/src/nameres/path_resolution.rs b/crates/ra_hir_def/src/nameres/path_resolution.rs index 19692e70c..dbfa7fccb 100644 --- a/crates/ra_hir_def/src/nameres/path_resolution.rs +++ b/crates/ra_hir_def/src/nameres/path_resolution.rs @@ -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( diff --git a/crates/ra_ide/src/call_hierarchy.rs b/crates/ra_ide/src/call_hierarchy.rs index cb7e62cd6..6af251d23 100644 --- a/crates/ra_ide/src/call_hierarchy.rs +++ b/crates/ra_ide/src/call_hierarchy.rs @@ -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(¯o_call)?; - Some(macro_def.to_nav(db)) - } } { Some((func_target, name_ref.syntax().text_range())) } else { diff --git a/crates/ra_ide/src/call_info.rs b/crates/ra_ide/src/call_info.rs index 1fe1c21de..a2d23b2ec 100644 --- a/crates/ra_ide/src/call_info.rs +++ b/crates/ra_ide/src/call_info.rs @@ -1,20 +1,41 @@ //! FIXME: write short doc here -use hir::Semantics; +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, + pub signature: String, pub active_parameter: Option, + parameters: Vec, +} + +impl CallInfo { + pub fn parameter_labels(&self) -> impl Iterator + '_ { + 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 +45,127 @@ pub(crate) fn call_info(db: &RootDatabase, position: FilePosition) -> Option Option { - call_info(db, position)?.into_active_parameter() + 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) + ); + } } - pub(crate) fn at_token(sema: &Semantics, token: SyntaxToken) -> Option { - call_info_for_token(sema, token)?.into_active_parameter() + 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 { + 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_for_token(sema: &Semantics, token: SyntaxToken) -> Option { +fn call_info_impl( + sema: &Semantics, + token: SyntaxToken, +) -> Option<(hir::Callable, Option)> { // 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(¯o_call)?; - FunctionSignature::from_macro(sema.db, macro_def)? + 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)) +} - // 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; - } +#[derive(Debug)] +pub(crate) struct ActiveParameter { + pub(crate) ty: Type, + pub(crate) name: String, +} - 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; - } +impl ActiveParameter { + pub(crate) fn at(db: &RootDatabase, position: FilePosition) -> Option { + 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) + } - Some(param) - } else { - None - } - } - }; + pub(crate) fn at_token(sema: &Semantics, token: SyntaxToken) -> Option { + let (signature, active_parameter) = call_info_impl(&sema, token)?; - Some(CallInfo { signature, active_parameter }) + 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 }) + } } #[derive(Debug)] pub(crate) enum FnCallNode { CallExpr(ast::CallExpr), MethodCallExpr(ast::MethodCallExpr), - MacroCallExpr(ast::MacroCall), } impl FnCallNode { @@ -138,7 +181,6 @@ impl FnCallNode { } Some(FnCallNode::MethodCallExpr(it)) }, - ast::MacroCall(it) => Some(FnCallNode::MacroCallExpr(it)), _ => None, } } @@ -150,7 +192,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 +207,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 +214,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 { - 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 +230,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::>() @@ -296,10 +322,8 @@ fn foo(x: T, y: U) -> u32 fn bar() { foo(<|>3, ); } "#, expect![[r#" - fn foo(x: T, y: U) -> u32 - where T: Copy + Display, - U: Debug - (, y: U) + fn foo(x: i32, y: {unknown}) -> u32 + (, y: {unknown}) "#]], ); } @@ -312,8 +336,7 @@ fn foo() -> T where T: Copy + Display {} fn bar() { foo(<|>); } "#, expect![[r#" - fn foo() -> T - where T: Copy + Display + fn foo() -> {unknown} () "#]], ); @@ -323,11 +346,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 +372,8 @@ fn bar() { } "#, expect![[r#" - pub fn do_it(&self) - (&self) + fn do_it(&self) + () "#]], ); } @@ -365,8 +391,8 @@ fn bar() { } "#, expect![[r#" - pub fn do_it(&self, x: i32) - (&self, ) + fn do_it(&self, x: i32) + () "#]], ); } @@ -425,7 +451,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 () "##]], ); @@ -467,7 +493,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 () "##]], ); @@ -505,8 +531,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, ) + fn finished(&mut self, ctx: &mut {unknown}) + () "#]], ); } @@ -539,7 +565,7 @@ fn main() { "#, expect![[r#" fn bar(&self, _: u32) - (&self, <_: u32>) + (<_: u32>) "#]], ); } @@ -549,15 +575,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, ) "#]], ); @@ -567,31 +593,18 @@ fn main() { fn generic_struct() { check( r#" -struct TS(T); +struct S(T); fn main() { - let s = TS(<|>); + let s = S(<|>); } "#, expect![[r#" - struct TS(T) -> TS - () + 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,27 +625,19 @@ fn main() { expect![[r#" A Variant ------ - E::A(0: i32) - (<0: i32>) + enum E::A(i32) + () "#]], ); } #[test] - fn cant_call_enum_records() { + fn cant_call_struct_record() { check( r#" -enum E { - /// A Variant - A(i32), - /// Another - B, - /// And C - C { a: i32, b: i32 } -} - +struct S { x: u32, y: i32 } fn main() { - let a = E::C(<|>); + let s = S(<|>); } "#, expect![[""]], @@ -640,24 +645,23 @@ fn main() { } #[test] - fn fn_signature_for_macro() { + fn cant_call_enum_record() { check( r#" -/// empty macro -macro_rules! foo { - () => {} +enum E { + /// A Variant + A(i32), + /// Another + B, + /// And C + C { a: i32, b: i32 } } -fn f() { - foo!(<|>); +fn main() { + let a = E::C(<|>); } "#, - expect![[r#" - empty macro - ------ - foo!() - () - "#]], + expect![[""]], ); } diff --git a/crates/ra_ide/src/completion/presentation.rs b/crates/ra_ide/src/completion/presentation.rs index 64349dcb8..e6b4737aa 100644 --- a/crates/ra_ide/src/completion/presentation.rs +++ b/crates/ra_ide/src/completion/presentation.rs @@ -329,15 +329,10 @@ pub(crate) fn compute_score( ty: &Type, name: &str, ) -> Option { - // 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()) diff --git a/crates/ra_ide/src/display/function_signature.rs b/crates/ra_ide/src/display/function_signature.rs index 1d39544d3..709a85f65 100644 --- a/crates/ra_ide/src/display/function_signature.rs +++ b/crates/ra_ide/src/display/function_signature.rs @@ -149,27 +149,6 @@ impl FunctionSignature { has_self_param: false, }) } - - pub(crate) fn from_macro(db: &RootDatabase, macro_def: hir::MacroDef) -> Option { - 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 { diff --git a/crates/ra_ide/src/inlay_hints.rs b/crates/ra_ide/src/inlay_hints.rs index 3cbae8a45..2e021f032 100644 --- a/crates/ra_ide/src/inlay_hints.rs +++ b/crates/ra_ide/src/inlay_hints.rs @@ -322,15 +322,15 @@ fn get_fn_signature(sema: &Semantics, 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()) } } diff --git a/crates/rust-analyzer/src/handlers.rs b/crates/rust-analyzer/src/handlers.rs index d28c700f1..447d73fd4 100644 --- a/crates/rust-analyzer/src/handlers.rs +++ b/crates/rust-analyzer/src/handlers.rs @@ -553,21 +553,12 @@ 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); + Ok(Some(res)) } pub(crate) fn handle_hover( diff --git a/crates/rust-analyzer/src/to_proto.rs b/crates/rust-analyzer/src/to_proto.rs index 2fcae9ca3..43fc52848 100644 --- a/crates/rust-analyzer/src/to_proto.rs +++ b/crates/rust-analyzer/src/to_proto.rs @@ -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,30 @@ pub(crate) fn completion_item( res } -pub(crate) fn signature_information( - signature: FunctionSignature, - concise: bool, -) -> lsp_types::SignatureInformation { - let (label, documentation, params) = if concise { - let mut params = signature.parameters; - if signature.has_self_param { - params.remove(0); - } - (params.join(", "), None, params) - } else { - (signature.to_string(), signature.doc.map(documentation), signature.parameters) - }; - - let parameters: Vec = params - .into_iter() - .map(|param| lsp_types::ParameterInformation { - label: lsp_types::ParameterLabel::Simple(param), +pub(crate) fn signature_help(call_info: CallInfo, concise: bool) -> lsp_types::SignatureHelp { + let parameters = call_info + .parameter_labels() + .map(|label| lsp_types::ParameterInformation { + label: lsp_types::ParameterLabel::Simple(label.to_string()), documentation: None, }) .collect(); - lsp_types::SignatureInformation { label, documentation, parameters: Some(parameters) } + let label = if concise { call_info.parameter_labels().join(", ") } else { call_info.signature }; + let documentation = call_info.doc.map(|doc| { + lsp_types::Documentation::MarkupContent(lsp_types::MarkupContent { + kind: lsp_types::MarkupKind::Markdown, + value: doc, + }) + }); + + 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 { -- cgit v1.2.3 From 29832b8c3db509dea8da9164e980ccac4bccf47d Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Thu, 16 Jul 2020 18:07:53 +0200 Subject: Reduce visibility --- .../ra_ide/src/completion/complete_trait_impl.rs | 2 +- crates/ra_ide/src/completion/presentation.rs | 2 +- crates/ra_ide/src/display.rs | 5 ++- crates/ra_ide/src/display/function_signature.rs | 40 ++++++++++------------ crates/ra_ide/src/inlay_hints.rs | 4 +-- crates/ra_ide/src/lib.rs | 2 +- 6 files changed, 26 insertions(+), 29 deletions(-) diff --git a/crates/ra_ide/src/completion/complete_trait_impl.rs b/crates/ra_ide/src/completion/complete_trait_impl.rs index a610fd6d1..90f5b1c25 100644 --- a/crates/ra_ide/src/completion/complete_trait_impl.rs +++ b/crates/ra_ide/src/completion/complete_trait_impl.rs @@ -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) { diff --git a/crates/ra_ide/src/completion/presentation.rs b/crates/ra_ide/src/completion/presentation.rs index e6b4737aa..e29b82017 100644 --- a/crates/ra_ide/src/completion/presentation.rs +++ b/crates/ra_ide/src/completion/presentation.rs @@ -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, }; diff --git a/crates/ra_ide/src/display.rs b/crates/ra_ide/src/display.rs index 70d2a2dd1..b59d4bbdf 100644 --- a/crates/ra_ide/src/display.rs +++ b/crates/ra_ide/src/display.rs @@ -1,7 +1,7 @@ //! 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; @@ -11,7 +11,6 @@ use ra_syntax::{ SyntaxKind::{ATTR, COMMENT}, }; -pub use function_signature::FunctionSignature; pub use navigation_target::NavigationTarget; pub use structure::{file_structure, StructureNode}; @@ -19,7 +18,7 @@ pub(crate) use navigation_target::{ToNav, TryToNav}; pub(crate) use short_label::ShortLabel; 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 { diff --git a/crates/ra_ide/src/display/function_signature.rs b/crates/ra_ide/src/display/function_signature.rs index 709a85f65..9b7220d1f 100644 --- a/crates/ra_ide/src/display/function_signature.rs +++ b/crates/ra_ide/src/display/function_signature.rs @@ -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, + pub(crate) visibility: Option, /// Qualifiers like `async`, `unsafe`, ... - pub qualifier: FunctionQualifier, + pub(crate) qualifier: FunctionQualifier, /// Name of the function - pub name: Option, + pub(crate) name: Option, /// Documentation for the function - pub doc: Option, + pub(crate) doc: Option, /// Generic parameters - pub generic_parameters: Vec, + pub(crate) generic_parameters: Vec, /// Parameters of the function - pub parameters: Vec, + pub(crate) parameters: Vec, /// Parameter names of the function - pub parameter_names: Vec, + pub(crate) parameter_names: Vec, /// Parameter types of the function - pub parameter_types: Vec, + pub(crate) parameter_types: Vec, /// Optional return type - pub ret_type: Option, + pub(crate) ret_type: Option, /// Where predicates - pub where_predicates: Vec, + pub(crate) where_predicates: Vec, /// 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, + pub(crate) extern_abi: Option, } impl FunctionSignature { @@ -277,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)?, } } diff --git a/crates/ra_ide/src/inlay_hints.rs b/crates/ra_ide/src/inlay_hints.rs index 2e021f032..ae5695f61 100644 --- a/crates/ra_ide/src/inlay_hints.rs +++ b/crates/ra_ide/src/inlay_hints.rs @@ -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, diff --git a/crates/ra_ide/src/lib.rs b/crates/ra_ide/src/lib.rs index 5d1f64e19..6810c1c6a 100644 --- a/crates/ra_ide/src/lib.rs +++ b/crates/ra_ide/src/lib.rs @@ -65,7 +65,7 @@ pub use crate::{ CompletionConfig, CompletionItem, CompletionItemKind, CompletionScore, InsertTextFormat, }, diagnostics::Severity, - display::{file_structure, FunctionSignature, NavigationTarget, StructureNode}, + display::{file_structure, NavigationTarget, StructureNode}, expand_macro::ExpandedMacro, folding_ranges::{Fold, FoldKind}, hover::{HoverAction, HoverConfig, HoverGotoTypeData, HoverResult}, -- cgit v1.2.3 From a4e9681c79095d6c10a851cfefe64cf1a3570ec5 Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Thu, 16 Jul 2020 18:13:43 +0200 Subject: Better module structure --- crates/ra_ide/src/display.rs | 6 +- crates/ra_ide/src/display/structure.rs | 441 --------------------------------- crates/ra_ide/src/file_structure.rs | 441 +++++++++++++++++++++++++++++++++ crates/ra_ide/src/lib.rs | 37 +-- crates/rust-analyzer/src/cli.rs | 8 +- 5 files changed, 468 insertions(+), 465 deletions(-) delete mode 100644 crates/ra_ide/src/display/structure.rs create mode 100644 crates/ra_ide/src/file_structure.rs diff --git a/crates/ra_ide/src/display.rs b/crates/ra_ide/src/display.rs index b59d4bbdf..1ec946369 100644 --- a/crates/ra_ide/src/display.rs +++ b/crates/ra_ide/src/display.rs @@ -3,7 +3,6 @@ pub(crate) mod function_signature; mod navigation_target; -mod structure; mod short_label; use ra_syntax::{ @@ -11,12 +10,11 @@ use ra_syntax::{ SyntaxKind::{ATTR, COMMENT}, }; -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 { function_signature::FunctionSignature::from(node).to_string() } diff --git a/crates/ra_ide/src/display/structure.rs b/crates/ra_ide/src/display/structure.rs deleted file mode 100644 index 1f6a3febf..000000000 --- a/crates/ra_ide/src/display/structure.rs +++ /dev/null @@ -1,441 +0,0 @@ -use ra_syntax::{ - ast::{self, AttrsOwner, NameOwner, TypeAscriptionOwner, TypeParamsOwner}, - match_ast, AstNode, SourceFile, SyntaxKind, SyntaxNode, TextRange, WalkEvent, -}; - -#[derive(Debug, Clone)] -pub struct StructureNode { - pub parent: Option, - pub label: String, - pub navigation_range: TextRange, - pub node_range: TextRange, - pub kind: SyntaxKind, - pub detail: Option, - pub deprecated: bool, -} - -// Feature: File Structure -// -// Provides a tree of the symbols defined in the file. Can be used to -// -// * fuzzy search symbol in a file (super useful) -// * draw breadcrumbs to describe the context around the cursor -// * draw outline of the file -// -// |=== -// | Editor | Shortcut -// -// | VS Code | kbd:[Ctrl+Shift+O] -// |=== -pub fn file_structure(file: &SourceFile) -> Vec { - let mut res = Vec::new(); - let mut stack = Vec::new(); - - for event in file.syntax().preorder() { - match event { - WalkEvent::Enter(node) => { - if let Some(mut symbol) = structure_node(&node) { - symbol.parent = stack.last().copied(); - stack.push(res.len()); - res.push(symbol); - } - } - WalkEvent::Leave(node) => { - if structure_node(&node).is_some() { - stack.pop().unwrap(); - } - } - } - } - res -} - -fn structure_node(node: &SyntaxNode) -> Option { - fn decl(node: N) -> Option { - decl_with_detail(node, None) - } - - fn decl_with_ascription( - node: N, - ) -> Option { - let ty = node.ascribed_type(); - decl_with_type_ref(node, ty) - } - - fn decl_with_type_ref( - node: N, - type_ref: Option, - ) -> Option { - let detail = type_ref.map(|type_ref| { - let mut detail = String::new(); - collapse_ws(type_ref.syntax(), &mut detail); - detail - }); - decl_with_detail(node, detail) - } - - fn decl_with_detail( - node: N, - detail: Option, - ) -> Option { - let name = node.name()?; - - Some(StructureNode { - parent: None, - label: name.text().to_string(), - navigation_range: name.syntax().text_range(), - node_range: node.syntax().text_range(), - kind: node.syntax().kind(), - detail, - deprecated: node.attrs().filter_map(|x| x.simple_name()).any(|x| x == "deprecated"), - }) - } - - fn collapse_ws(node: &SyntaxNode, output: &mut String) { - let mut can_insert_ws = false; - node.text().for_each_chunk(|chunk| { - for line in chunk.lines() { - let line = line.trim(); - if line.is_empty() { - if can_insert_ws { - output.push(' '); - can_insert_ws = false; - } - } else { - output.push_str(line); - can_insert_ws = true; - } - } - }) - } - - match_ast! { - match node { - ast::FnDef(it) => { - let mut detail = String::from("fn"); - if let Some(type_param_list) = it.type_param_list() { - collapse_ws(type_param_list.syntax(), &mut detail); - } - if let Some(param_list) = it.param_list() { - collapse_ws(param_list.syntax(), &mut detail); - } - if let Some(ret_type) = it.ret_type() { - detail.push_str(" "); - collapse_ws(ret_type.syntax(), &mut detail); - } - - decl_with_detail(it, Some(detail)) - }, - ast::StructDef(it) => decl(it), - ast::UnionDef(it) => decl(it), - ast::EnumDef(it) => decl(it), - ast::EnumVariant(it) => decl(it), - ast::TraitDef(it) => decl(it), - ast::Module(it) => decl(it), - ast::TypeAliasDef(it) => { - let ty = it.type_ref(); - decl_with_type_ref(it, ty) - }, - ast::RecordFieldDef(it) => decl_with_ascription(it), - ast::ConstDef(it) => decl_with_ascription(it), - ast::StaticDef(it) => decl_with_ascription(it), - ast::ImplDef(it) => { - let target_type = it.target_type()?; - let target_trait = it.target_trait(); - let label = match target_trait { - None => format!("impl {}", target_type.syntax().text()), - Some(t) => { - format!("impl {} for {}", t.syntax().text(), target_type.syntax().text(),) - } - }; - - let node = StructureNode { - parent: None, - label, - navigation_range: target_type.syntax().text_range(), - node_range: it.syntax().text_range(), - kind: it.syntax().kind(), - detail: None, - deprecated: false, - }; - Some(node) - }, - ast::MacroCall(it) => { - match it.path().and_then(|it| it.segment()).and_then(|it| it.name_ref()) { - Some(path_segment) if path_segment.text() == "macro_rules" - => decl(it), - _ => None, - } - }, - _ => None, - } - } -} - -#[cfg(test)] -mod tests { - use expect::{expect, Expect}; - - use super::*; - - fn check(ra_fixture: &str, expect: Expect) { - let file = SourceFile::parse(ra_fixture).ok().unwrap(); - let structure = file_structure(&file); - expect.assert_debug_eq(&structure) - } - - #[test] - fn test_file_structure() { - check( - r#" -struct Foo { - x: i32 -} - -mod m { - fn bar1() {} - fn bar2(t: T) -> T {} - fn bar3(a: A, - b: B) -> Vec< - u32 - > {} -} - -enum E { X, Y(i32) } -type T = (); -static S: i32 = 92; -const C: i32 = 92; - -impl E {} - -impl fmt::Debug for E {} - -macro_rules! mc { - () => {} -} - -#[macro_export] -macro_rules! mcexp { - () => {} -} - -/// Doc comment -macro_rules! mcexp { - () => {} -} - -#[deprecated] -fn obsolete() {} - -#[deprecated(note = "for awhile")] -fn very_obsolete() {} -"#, - expect![[r#" - [ - StructureNode { - parent: None, - label: "Foo", - navigation_range: 8..11, - node_range: 1..26, - kind: STRUCT_DEF, - detail: None, - deprecated: false, - }, - StructureNode { - parent: Some( - 0, - ), - label: "x", - navigation_range: 18..19, - node_range: 18..24, - kind: RECORD_FIELD_DEF, - detail: Some( - "i32", - ), - deprecated: false, - }, - StructureNode { - parent: None, - label: "m", - navigation_range: 32..33, - node_range: 28..158, - kind: MODULE, - detail: None, - deprecated: false, - }, - StructureNode { - parent: Some( - 2, - ), - label: "bar1", - navigation_range: 43..47, - node_range: 40..52, - kind: FN_DEF, - detail: Some( - "fn()", - ), - deprecated: false, - }, - StructureNode { - parent: Some( - 2, - ), - label: "bar2", - navigation_range: 60..64, - node_range: 57..81, - kind: FN_DEF, - detail: Some( - "fn(t: T) -> T", - ), - deprecated: false, - }, - StructureNode { - parent: Some( - 2, - ), - label: "bar3", - navigation_range: 89..93, - node_range: 86..156, - kind: FN_DEF, - detail: Some( - "fn(a: A, b: B) -> Vec< u32 >", - ), - deprecated: false, - }, - StructureNode { - parent: None, - label: "E", - navigation_range: 165..166, - node_range: 160..180, - kind: ENUM_DEF, - detail: None, - deprecated: false, - }, - StructureNode { - parent: Some( - 6, - ), - label: "X", - navigation_range: 169..170, - node_range: 169..170, - kind: ENUM_VARIANT, - detail: None, - deprecated: false, - }, - StructureNode { - parent: Some( - 6, - ), - label: "Y", - navigation_range: 172..173, - node_range: 172..178, - kind: ENUM_VARIANT, - detail: None, - deprecated: false, - }, - StructureNode { - parent: None, - label: "T", - navigation_range: 186..187, - node_range: 181..193, - kind: TYPE_ALIAS_DEF, - detail: Some( - "()", - ), - deprecated: false, - }, - StructureNode { - parent: None, - label: "S", - navigation_range: 201..202, - node_range: 194..213, - kind: STATIC_DEF, - detail: Some( - "i32", - ), - deprecated: false, - }, - StructureNode { - parent: None, - label: "C", - navigation_range: 220..221, - node_range: 214..232, - kind: CONST_DEF, - detail: Some( - "i32", - ), - deprecated: false, - }, - StructureNode { - parent: None, - label: "impl E", - navigation_range: 239..240, - node_range: 234..243, - kind: IMPL_DEF, - detail: None, - deprecated: false, - }, - StructureNode { - parent: None, - label: "impl fmt::Debug for E", - navigation_range: 265..266, - node_range: 245..269, - kind: IMPL_DEF, - detail: None, - deprecated: false, - }, - StructureNode { - parent: None, - label: "mc", - navigation_range: 284..286, - node_range: 271..303, - kind: MACRO_CALL, - detail: None, - deprecated: false, - }, - StructureNode { - parent: None, - label: "mcexp", - navigation_range: 334..339, - node_range: 305..356, - kind: MACRO_CALL, - detail: None, - deprecated: false, - }, - StructureNode { - parent: None, - label: "mcexp", - navigation_range: 387..392, - node_range: 358..409, - kind: MACRO_CALL, - detail: None, - deprecated: false, - }, - StructureNode { - parent: None, - label: "obsolete", - navigation_range: 428..436, - node_range: 411..441, - kind: FN_DEF, - detail: Some( - "fn()", - ), - deprecated: true, - }, - StructureNode { - parent: None, - label: "very_obsolete", - navigation_range: 481..494, - node_range: 443..499, - kind: FN_DEF, - detail: Some( - "fn()", - ), - deprecated: true, - }, - ] - "#]], - ); - } -} diff --git a/crates/ra_ide/src/file_structure.rs b/crates/ra_ide/src/file_structure.rs new file mode 100644 index 000000000..1f6a3febf --- /dev/null +++ b/crates/ra_ide/src/file_structure.rs @@ -0,0 +1,441 @@ +use ra_syntax::{ + ast::{self, AttrsOwner, NameOwner, TypeAscriptionOwner, TypeParamsOwner}, + match_ast, AstNode, SourceFile, SyntaxKind, SyntaxNode, TextRange, WalkEvent, +}; + +#[derive(Debug, Clone)] +pub struct StructureNode { + pub parent: Option, + pub label: String, + pub navigation_range: TextRange, + pub node_range: TextRange, + pub kind: SyntaxKind, + pub detail: Option, + pub deprecated: bool, +} + +// Feature: File Structure +// +// Provides a tree of the symbols defined in the file. Can be used to +// +// * fuzzy search symbol in a file (super useful) +// * draw breadcrumbs to describe the context around the cursor +// * draw outline of the file +// +// |=== +// | Editor | Shortcut +// +// | VS Code | kbd:[Ctrl+Shift+O] +// |=== +pub fn file_structure(file: &SourceFile) -> Vec { + let mut res = Vec::new(); + let mut stack = Vec::new(); + + for event in file.syntax().preorder() { + match event { + WalkEvent::Enter(node) => { + if let Some(mut symbol) = structure_node(&node) { + symbol.parent = stack.last().copied(); + stack.push(res.len()); + res.push(symbol); + } + } + WalkEvent::Leave(node) => { + if structure_node(&node).is_some() { + stack.pop().unwrap(); + } + } + } + } + res +} + +fn structure_node(node: &SyntaxNode) -> Option { + fn decl(node: N) -> Option { + decl_with_detail(node, None) + } + + fn decl_with_ascription( + node: N, + ) -> Option { + let ty = node.ascribed_type(); + decl_with_type_ref(node, ty) + } + + fn decl_with_type_ref( + node: N, + type_ref: Option, + ) -> Option { + let detail = type_ref.map(|type_ref| { + let mut detail = String::new(); + collapse_ws(type_ref.syntax(), &mut detail); + detail + }); + decl_with_detail(node, detail) + } + + fn decl_with_detail( + node: N, + detail: Option, + ) -> Option { + let name = node.name()?; + + Some(StructureNode { + parent: None, + label: name.text().to_string(), + navigation_range: name.syntax().text_range(), + node_range: node.syntax().text_range(), + kind: node.syntax().kind(), + detail, + deprecated: node.attrs().filter_map(|x| x.simple_name()).any(|x| x == "deprecated"), + }) + } + + fn collapse_ws(node: &SyntaxNode, output: &mut String) { + let mut can_insert_ws = false; + node.text().for_each_chunk(|chunk| { + for line in chunk.lines() { + let line = line.trim(); + if line.is_empty() { + if can_insert_ws { + output.push(' '); + can_insert_ws = false; + } + } else { + output.push_str(line); + can_insert_ws = true; + } + } + }) + } + + match_ast! { + match node { + ast::FnDef(it) => { + let mut detail = String::from("fn"); + if let Some(type_param_list) = it.type_param_list() { + collapse_ws(type_param_list.syntax(), &mut detail); + } + if let Some(param_list) = it.param_list() { + collapse_ws(param_list.syntax(), &mut detail); + } + if let Some(ret_type) = it.ret_type() { + detail.push_str(" "); + collapse_ws(ret_type.syntax(), &mut detail); + } + + decl_with_detail(it, Some(detail)) + }, + ast::StructDef(it) => decl(it), + ast::UnionDef(it) => decl(it), + ast::EnumDef(it) => decl(it), + ast::EnumVariant(it) => decl(it), + ast::TraitDef(it) => decl(it), + ast::Module(it) => decl(it), + ast::TypeAliasDef(it) => { + let ty = it.type_ref(); + decl_with_type_ref(it, ty) + }, + ast::RecordFieldDef(it) => decl_with_ascription(it), + ast::ConstDef(it) => decl_with_ascription(it), + ast::StaticDef(it) => decl_with_ascription(it), + ast::ImplDef(it) => { + let target_type = it.target_type()?; + let target_trait = it.target_trait(); + let label = match target_trait { + None => format!("impl {}", target_type.syntax().text()), + Some(t) => { + format!("impl {} for {}", t.syntax().text(), target_type.syntax().text(),) + } + }; + + let node = StructureNode { + parent: None, + label, + navigation_range: target_type.syntax().text_range(), + node_range: it.syntax().text_range(), + kind: it.syntax().kind(), + detail: None, + deprecated: false, + }; + Some(node) + }, + ast::MacroCall(it) => { + match it.path().and_then(|it| it.segment()).and_then(|it| it.name_ref()) { + Some(path_segment) if path_segment.text() == "macro_rules" + => decl(it), + _ => None, + } + }, + _ => None, + } + } +} + +#[cfg(test)] +mod tests { + use expect::{expect, Expect}; + + use super::*; + + fn check(ra_fixture: &str, expect: Expect) { + let file = SourceFile::parse(ra_fixture).ok().unwrap(); + let structure = file_structure(&file); + expect.assert_debug_eq(&structure) + } + + #[test] + fn test_file_structure() { + check( + r#" +struct Foo { + x: i32 +} + +mod m { + fn bar1() {} + fn bar2(t: T) -> T {} + fn bar3(a: A, + b: B) -> Vec< + u32 + > {} +} + +enum E { X, Y(i32) } +type T = (); +static S: i32 = 92; +const C: i32 = 92; + +impl E {} + +impl fmt::Debug for E {} + +macro_rules! mc { + () => {} +} + +#[macro_export] +macro_rules! mcexp { + () => {} +} + +/// Doc comment +macro_rules! mcexp { + () => {} +} + +#[deprecated] +fn obsolete() {} + +#[deprecated(note = "for awhile")] +fn very_obsolete() {} +"#, + expect![[r#" + [ + StructureNode { + parent: None, + label: "Foo", + navigation_range: 8..11, + node_range: 1..26, + kind: STRUCT_DEF, + detail: None, + deprecated: false, + }, + StructureNode { + parent: Some( + 0, + ), + label: "x", + navigation_range: 18..19, + node_range: 18..24, + kind: RECORD_FIELD_DEF, + detail: Some( + "i32", + ), + deprecated: false, + }, + StructureNode { + parent: None, + label: "m", + navigation_range: 32..33, + node_range: 28..158, + kind: MODULE, + detail: None, + deprecated: false, + }, + StructureNode { + parent: Some( + 2, + ), + label: "bar1", + navigation_range: 43..47, + node_range: 40..52, + kind: FN_DEF, + detail: Some( + "fn()", + ), + deprecated: false, + }, + StructureNode { + parent: Some( + 2, + ), + label: "bar2", + navigation_range: 60..64, + node_range: 57..81, + kind: FN_DEF, + detail: Some( + "fn(t: T) -> T", + ), + deprecated: false, + }, + StructureNode { + parent: Some( + 2, + ), + label: "bar3", + navigation_range: 89..93, + node_range: 86..156, + kind: FN_DEF, + detail: Some( + "fn(a: A, b: B) -> Vec< u32 >", + ), + deprecated: false, + }, + StructureNode { + parent: None, + label: "E", + navigation_range: 165..166, + node_range: 160..180, + kind: ENUM_DEF, + detail: None, + deprecated: false, + }, + StructureNode { + parent: Some( + 6, + ), + label: "X", + navigation_range: 169..170, + node_range: 169..170, + kind: ENUM_VARIANT, + detail: None, + deprecated: false, + }, + StructureNode { + parent: Some( + 6, + ), + label: "Y", + navigation_range: 172..173, + node_range: 172..178, + kind: ENUM_VARIANT, + detail: None, + deprecated: false, + }, + StructureNode { + parent: None, + label: "T", + navigation_range: 186..187, + node_range: 181..193, + kind: TYPE_ALIAS_DEF, + detail: Some( + "()", + ), + deprecated: false, + }, + StructureNode { + parent: None, + label: "S", + navigation_range: 201..202, + node_range: 194..213, + kind: STATIC_DEF, + detail: Some( + "i32", + ), + deprecated: false, + }, + StructureNode { + parent: None, + label: "C", + navigation_range: 220..221, + node_range: 214..232, + kind: CONST_DEF, + detail: Some( + "i32", + ), + deprecated: false, + }, + StructureNode { + parent: None, + label: "impl E", + navigation_range: 239..240, + node_range: 234..243, + kind: IMPL_DEF, + detail: None, + deprecated: false, + }, + StructureNode { + parent: None, + label: "impl fmt::Debug for E", + navigation_range: 265..266, + node_range: 245..269, + kind: IMPL_DEF, + detail: None, + deprecated: false, + }, + StructureNode { + parent: None, + label: "mc", + navigation_range: 284..286, + node_range: 271..303, + kind: MACRO_CALL, + detail: None, + deprecated: false, + }, + StructureNode { + parent: None, + label: "mcexp", + navigation_range: 334..339, + node_range: 305..356, + kind: MACRO_CALL, + detail: None, + deprecated: false, + }, + StructureNode { + parent: None, + label: "mcexp", + navigation_range: 387..392, + node_range: 358..409, + kind: MACRO_CALL, + detail: None, + deprecated: false, + }, + StructureNode { + parent: None, + label: "obsolete", + navigation_range: 428..436, + node_range: 411..441, + kind: FN_DEF, + detail: Some( + "fn()", + ), + deprecated: true, + }, + StructureNode { + parent: None, + label: "very_obsolete", + navigation_range: 481..494, + node_range: 443..499, + kind: FN_DEF, + detail: Some( + "fn()", + ), + deprecated: true, + }, + ] + "#]], + ); + } +} diff --git a/crates/ra_ide/src/lib.rs b/crates/ra_ide/src/lib.rs index 6810c1c6a..d3b20f371 100644 --- a/crates/ra_ide/src/lib.rs +++ b/crates/ra_ide/src/lib.rs @@ -19,29 +19,31 @@ pub mod mock_analysis; mod markup; mod prime_caches; -mod status; +mod display; + +mod call_hierarchy; +mod call_info; mod completion; -mod runnables; +mod diagnostics; +mod expand_macro; +mod extend_selection; +mod file_structure; +mod folding_ranges; mod goto_definition; -mod goto_type_definition; mod goto_implementation; -mod extend_selection; +mod goto_type_definition; mod hover; -mod call_hierarchy; -mod call_info; -mod syntax_highlighting; +mod inlay_hints; +mod join_lines; +mod matching_brace; mod parent_module; mod references; -mod diagnostics; +mod runnables; +mod ssr; +mod status; +mod syntax_highlighting; mod syntax_tree; -mod folding_ranges; -mod join_lines; mod typing; -mod matching_brace; -mod display; -mod inlay_hints; -mod expand_macro; -mod ssr; use std::sync::Arc; @@ -65,8 +67,9 @@ pub use crate::{ CompletionConfig, CompletionItem, CompletionItemKind, CompletionScore, InsertTextFormat, }, diagnostics::Severity, - display::{file_structure, 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> { - 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. diff --git a/crates/rust-analyzer/src/cli.rs b/crates/rust-analyzer/src/cli.rs index 6863f100b..753001949 100644 --- a/crates/rust-analyzer/src/cli.rs +++ b/crates/rust-analyzer/src/cli.rs @@ -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(()) -- cgit v1.2.3 From 6da22ed9752b239fcd4e7c75673907ceb1ac6b65 Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Thu, 16 Jul 2020 18:24:26 +0200 Subject: Redner self as param for call infor for assoc fn call --- crates/ra_hir/src/code_model.rs | 14 ++++++++++++-- crates/ra_ide/src/call_info.rs | 35 ++++++++++++++++++++++++++++------- 2 files changed, 40 insertions(+), 9 deletions(-) diff --git a/crates/ra_hir/src/code_model.rs b/crates/ra_hir/src/code_model.rs index 057dfb82a..eb6a14eda 100644 --- a/crates/ra_hir/src/code_model.rs +++ b/crates/ra_hir/src/code_model.rs @@ -1552,7 +1552,10 @@ impl Callable { let param_list = src.value.param_list()?; param_list.self_param() } - pub fn params(&self, db: &dyn HirDatabase) -> Vec<(Option, Type)> { + pub fn params( + &self, + db: &dyn HirDatabase, + ) -> Vec<(Option>, Type)> { let types = self .sig .params() @@ -1562,7 +1565,14 @@ impl Callable { let patterns = match self.id { CallableDefId::FunctionId(func) => { let src = func.lookup(db.upcast()).source(db.upcast()); - src.value.param_list().map(|it| it.params().map(|it| it.pat())) + 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, diff --git a/crates/ra_ide/src/call_info.rs b/crates/ra_ide/src/call_info.rs index a2d23b2ec..35a8a0dc5 100644 --- a/crates/ra_ide/src/call_info.rs +++ b/crates/ra_ide/src/call_info.rs @@ -1,4 +1,5 @@ //! FIXME: write short doc here +use either::Either; use hir::{Docs, HirDisplay, Semantics, Type}; use ra_ide_db::RootDatabase; use ra_syntax::{ @@ -80,7 +81,10 @@ pub(crate) fn call_info(db: &RootDatabase, position: FilePosition) -> Option format_to!(buf, "self: "), + Either::Right(pat) => format_to!(buf, "{}: ", pat), + } } format_to!(buf, "{}", ty.display(db)); res.push_param(&buf); @@ -383,20 +387,37 @@ 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#" - fn do_it(&self, x: i32) + fn foo(&self, 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) + (, x: i32) + "#]], + ); + } + #[test] fn test_fn_signature_with_docs_simple() { check( -- cgit v1.2.3 From e1e79cf0648624e7a3787d0013c0c7e86210772f Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Thu, 16 Jul 2020 18:41:16 +0200 Subject: Take label offets client capability into account --- crates/rust-analyzer/src/config.rs | 10 ++++++ crates/rust-analyzer/src/handlers.rs | 6 +++- crates/rust-analyzer/src/to_proto.rs | 60 ++++++++++++++++++++++++++++++------ 3 files changed, 66 insertions(+), 10 deletions(-) diff --git a/crates/rust-analyzer/src/config.rs b/crates/rust-analyzer/src/config.rs index ed5e52871..68b2a2abd 100644 --- a/crates/rust-analyzer/src/config.rs +++ b/crates/rust-analyzer/src/config.rs @@ -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 { diff --git a/crates/rust-analyzer/src/handlers.rs b/crates/rust-analyzer/src/handlers.rs index 447d73fd4..18d660f42 100644 --- a/crates/rust-analyzer/src/handlers.rs +++ b/crates/rust-analyzer/src/handlers.rs @@ -557,7 +557,11 @@ pub(crate) fn handle_signature_help( None => return Ok(None), }; let concise = !snap.config.call_info_full; - let res = to_proto::signature_help(call_info, concise); + let res = to_proto::signature_help( + call_info, + concise, + snap.config.client_caps.signature_help_label_offsets, + ); Ok(Some(res)) } diff --git a/crates/rust-analyzer/src/to_proto.rs b/crates/rust-analyzer/src/to_proto.rs index 43fc52848..7fcb43a4f 100644 --- a/crates/rust-analyzer/src/to_proto.rs +++ b/crates/rust-analyzer/src/to_proto.rs @@ -219,16 +219,58 @@ pub(crate) fn completion_item( res } -pub(crate) fn signature_help(call_info: CallInfo, concise: bool) -> lsp_types::SignatureHelp { - let parameters = call_info - .parameter_labels() - .map(|label| lsp_types::ParameterInformation { - label: lsp_types::ParameterLabel::Simple(label.to_string()), - documentation: None, - }) - .collect(); +pub(crate) fn signature_help( + call_info: CallInfo, + concise: bool, + 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::>(); + 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::>(); + (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) + } + }; - let label = if concise { call_info.parameter_labels().join(", ") } else { call_info.signature }; let documentation = call_info.doc.map(|doc| { lsp_types::Documentation::MarkupContent(lsp_types::MarkupContent { kind: lsp_types::MarkupKind::Markdown, -- cgit v1.2.3