diff options
| author | hkalbasi <hamidrezakalbasi@protonmail.com> | 2022-04-02 16:19:36 +0430 |
|---|---|---|
| committer | hkalbasi <hamidrezakalbasi@protonmail.com> | 2022-04-03 11:17:33 +0430 |
| commit | 003a6b74e469713e99f23d8cff58af95c10b29e0 (patch) | |
| tree | 1483bbebe8613a005833a1bf9a73300086356278 | |
| parent | d312b4519ab35850fff8977f9e3c474a4eb8c0bc (diff) | |
| download | rust-003a6b74e469713e99f23d8cff58af95c10b29e0.tar.gz rust-003a6b74e469713e99f23d8cff58af95c10b29e0.zip | |
suggest infered type in auto complete
| -rw-r--r-- | crates/hir_def/src/item_tree/lower.rs | 10 | ||||
| -rw-r--r-- | crates/ide_completion/src/completions.rs | 20 | ||||
| -rw-r--r-- | crates/ide_completion/src/item.rs | 9 | ||||
| -rw-r--r-- | crates/ide_completion/src/lib.rs | 1 | ||||
| -rw-r--r-- | crates/ide_completion/src/patterns.rs | 75 | ||||
| -rw-r--r-- | crates/ide_completion/src/render.rs | 10 | ||||
| -rw-r--r-- | crates/ide_completion/src/tests/type_pos.rs | 200 | ||||
| -rw-r--r-- | crates/rust-analyzer/src/to_proto.rs | 1 |
8 files changed, 312 insertions, 14 deletions
diff --git a/crates/hir_def/src/item_tree/lower.rs b/crates/hir_def/src/item_tree/lower.rs index 379e03504b2..b576815f877 100644 --- a/crates/hir_def/src/item_tree/lower.rs +++ b/crates/hir_def/src/item_tree/lower.rs @@ -302,9 +302,13 @@ impl<'a> Ctx<'a> { let end_param = self.next_param_idx(); let params = IdxRange::new(start_param..end_param); - let ret_type = match func.ret_type().and_then(|rt| rt.ty()) { - Some(type_ref) => TypeRef::from_ast(&self.body_ctx, type_ref), - _ => TypeRef::unit(), + let ret_type = match func.ret_type() { + Some(rt) => match rt.ty() { + Some(type_ref) => TypeRef::from_ast(&self.body_ctx, type_ref), + None if rt.thin_arrow_token().is_some() => TypeRef::Error, + None => TypeRef::unit(), + }, + None => TypeRef::unit(), }; let (ret_type, async_ret_type) = if func.async_token().is_some() { diff --git a/crates/ide_completion/src/completions.rs b/crates/ide_completion/src/completions.rs index ab442fa64b1..7dbc5113954 100644 --- a/crates/ide_completion/src/completions.rs +++ b/crates/ide_completion/src/completions.rs @@ -21,12 +21,13 @@ pub(crate) mod vis; use std::iter; -use hir::{db::HirDatabase, known, ScopeDef}; +use hir::{db::HirDatabase, known, HirDisplay, ScopeDef}; use ide_db::SymbolKind; use crate::{ context::Visible, item::Builder, + patterns::{ImmediateLocation, TypeAnnotation}, render::{ const_::render_const, function::{render_fn, render_method}, @@ -34,6 +35,7 @@ use crate::{ macro_::render_macro, pattern::{render_struct_pat, render_variant_pat}, render_field, render_resolution, render_resolution_simple, render_tuple_field, + render_type_inference, type_alias::{render_type_alias, render_type_alias_with_eq}, union_literal::render_union_literal, RenderContext, @@ -374,3 +376,19 @@ fn enum_variants_with_paths( } } } + +pub(crate) fn inferred_type(acc: &mut Completions, ctx: &CompletionContext) -> Option<()> { + use TypeAnnotation::*; + let pat = match &ctx.completion_location { + Some(ImmediateLocation::TypeAnnotation(t)) => t, + _ => return None, + }; + let x = match pat { + Let(pat) | FnParam(pat) => ctx.sema.type_of_pat(pat.as_ref()?), + Const(exp) | RetType(exp) => ctx.sema.type_of_expr(exp.as_ref()?), + }? + .adjusted(); + let ty_string = x.display_source_code(ctx.db, ctx.module.into()).ok()?; + acc.add(render_type_inference(ty_string, ctx)); + None +} diff --git a/crates/ide_completion/src/item.rs b/crates/ide_completion/src/item.rs index 3c2be32f4e9..8c73bcaab2d 100644 --- a/crates/ide_completion/src/item.rs +++ b/crates/ide_completion/src/item.rs @@ -138,6 +138,8 @@ pub struct CompletionRelevance { pub is_private_editable: bool, /// Set for postfix snippet item completions pub postfix_match: Option<CompletionRelevancePostfixMatch>, + /// This is setted for type inference results + pub is_definite: bool, } #[derive(Debug, Clone, Copy, Eq, PartialEq)] @@ -198,6 +200,7 @@ impl CompletionRelevance { is_op_method, is_private_editable, postfix_match, + is_definite, } = self; // lower rank private things @@ -225,7 +228,9 @@ impl CompletionRelevance { if is_local { score += 1; } - + if is_definite { + score += 10; + } score } @@ -243,6 +248,7 @@ pub enum CompletionItemKind { SymbolKind(SymbolKind), Binding, BuiltinType, + InferredType, Keyword, Method, Snippet, @@ -284,6 +290,7 @@ impl CompletionItemKind { }, CompletionItemKind::Binding => "bn", CompletionItemKind::BuiltinType => "bt", + CompletionItemKind::InferredType => "it", CompletionItemKind::Keyword => "kw", CompletionItemKind::Method => "me", CompletionItemKind::Snippet => "sn", diff --git a/crates/ide_completion/src/lib.rs b/crates/ide_completion/src/lib.rs index 8eb7615f8f2..6924ba1db6d 100644 --- a/crates/ide_completion/src/lib.rs +++ b/crates/ide_completion/src/lib.rs @@ -155,6 +155,7 @@ pub fn completions( completions::flyimport::import_on_the_fly(&mut acc, &ctx); completions::fn_param::complete_fn_param(&mut acc, &ctx); completions::format_string::format_string(&mut acc, &ctx); + completions::inferred_type(&mut acc, &ctx); completions::keyword::complete_expr_keyword(&mut acc, &ctx); completions::lifetime::complete_label(&mut acc, &ctx); completions::lifetime::complete_lifetime(&mut acc, &ctx); diff --git a/crates/ide_completion/src/patterns.rs b/crates/ide_completion/src/patterns.rs index 4536e3e6ee5..5fd9602b46f 100644 --- a/crates/ide_completion/src/patterns.rs +++ b/crates/ide_completion/src/patterns.rs @@ -8,7 +8,7 @@ use hir::Semantics; use ide_db::RootDatabase; use syntax::{ algo::non_trivia_sibling, - ast::{self, HasArgList, HasLoopBody}, + ast::{self, HasArgList, HasLoopBody, HasName}, match_ast, AstNode, Direction, SyntaxElement, SyntaxKind::*, SyntaxNode, SyntaxToken, TextRange, TextSize, @@ -27,6 +27,14 @@ pub(crate) enum ImmediatePrevSibling { Attribute, } +#[derive(Clone, Debug, PartialEq, Eq)] +pub(crate) enum TypeAnnotation { + Let(Option<ast::Pat>), + FnParam(Option<ast::Pat>), + RetType(Option<ast::Expr>), + Const(Option<ast::Expr>), +} + /// Direct parent "thing" of what we are currently completing. /// /// This may contain nodes of the fake file as well as the original, comments on the variants specify @@ -44,6 +52,8 @@ pub(crate) enum ImmediateLocation { ItemList, TypeBound, Variant, + /// Original file ast node + TypeAnnotation(TypeAnnotation), /// Fake file ast node ModDeclaration(ast::Module), /// Original file ast node @@ -235,10 +245,7 @@ pub(crate) fn determine_location( } }, ast::FieldExpr(it) => { - let receiver = it - .expr() - .map(|e| e.syntax().text_range()) - .and_then(|r| find_node_with_range(original_file, r)); + let receiver = find_in_original_file(it.expr(), original_file); let receiver_is_ambiguous_float_literal = if let Some(ast::Expr::Literal(l)) = &receiver { match l.kind() { ast::LiteralKind::FloatNumber { .. } => l.token().text().ends_with('.'), @@ -253,15 +260,65 @@ pub(crate) fn determine_location( } }, ast::MethodCallExpr(it) => ImmediateLocation::MethodCall { - receiver: it - .receiver() - .map(|e| e.syntax().text_range()) - .and_then(|r| find_node_with_range(original_file, r)), + receiver: find_in_original_file(it.receiver(), original_file), has_parens: it.arg_list().map_or(false, |it| it.l_paren_token().is_some()) }, + ast::Const(it) => { + if !it.ty().map_or(false, |x| x.syntax().text_range().contains(offset)) { + return None; + } + let name = find_in_original_file(it.name(), original_file)?; + let original = ast::Const::cast(name.syntax().parent()?)?; + ImmediateLocation::TypeAnnotation(TypeAnnotation::Const(original.body())) + }, + ast::RetType(it) => { + if it.thin_arrow_token().is_none() { + return None; + } + if !it.ty().map_or(false, |x| x.syntax().text_range().contains(offset)) { + return None; + } + let parent = match ast::Fn::cast(parent.parent()?) { + Some(x) => x.param_list(), + None => ast::ClosureExpr::cast(parent.parent()?)?.param_list(), + }; + let parent = find_in_original_file(parent, original_file)?.syntax().parent()?; + ImmediateLocation::TypeAnnotation(TypeAnnotation::RetType(match_ast! { + match parent { + ast::ClosureExpr(it) => { + it.body() + }, + ast::Fn(it) => { + it.body().map(ast::Expr::BlockExpr) + }, + _ => return None, + } + })) + }, + ast::Param(it) => { + if it.colon_token().is_none() { + return None; + } + if !it.ty().map_or(false, |x| x.syntax().text_range().contains(offset)) { + return None; + } + ImmediateLocation::TypeAnnotation(TypeAnnotation::FnParam(find_in_original_file(it.pat(), original_file))) + }, + ast::LetStmt(it) => { + if it.colon_token().is_none() { + return None; + } + if !it.ty().map_or(false, |x| x.syntax().text_range().contains(offset)) { + return None; + } + ImmediateLocation::TypeAnnotation(TypeAnnotation::Let(find_in_original_file(it.pat(), original_file))) + }, _ => return None, } }; + fn find_in_original_file<N: AstNode>(x: Option<N>, original_file: &SyntaxNode) -> Option<N> { + x.map(|e| e.syntax().text_range()).and_then(|r| find_node_with_range(original_file, r)) + } Some(res) } diff --git a/crates/ide_completion/src/render.rs b/crates/ide_completion/src/render.rs index e5ec2732125..65375494569 100644 --- a/crates/ide_completion/src/render.rs +++ b/crates/ide_completion/src/render.rs @@ -170,6 +170,13 @@ pub(crate) fn render_resolution_with_import( Some(render_resolution_(ctx, local_name, Some(import_edit), resolution)) } +pub(crate) fn render_type_inference(ty_string: String, ctx: &CompletionContext) -> CompletionItem { + let mut builder = + CompletionItem::new(CompletionItemKind::InferredType, ctx.source_range(), ty_string); + builder.set_relevance(CompletionRelevance { is_definite: true, ..Default::default() }); + builder.build() +} + fn render_resolution_( ctx: RenderContext<'_>, local_name: hir::Name, @@ -620,6 +627,7 @@ fn main() { let _: m::Spam = S$0 } is_op_method: false, is_private_editable: false, postfix_match: None, + is_definite: false, }, }, CompletionItem { @@ -641,6 +649,7 @@ fn main() { let _: m::Spam = S$0 } is_op_method: false, is_private_editable: false, postfix_match: None, + is_definite: false, }, }, ] @@ -728,6 +737,7 @@ fn foo() { A { the$0 } } is_op_method: false, is_private_editable: false, postfix_match: None, + is_definite: false, }, }, ] diff --git a/crates/ide_completion/src/tests/type_pos.rs b/crates/ide_completion/src/tests/type_pos.rs index c8260f6e23c..e3ffb92f786 100644 --- a/crates/ide_completion/src/tests/type_pos.rs +++ b/crates/ide_completion/src/tests/type_pos.rs @@ -90,6 +90,206 @@ fn x<'lt, T, const C: usize>() -> $0 } #[test] +fn inferred_type_const() { + check( + r#" +struct Foo<T>(T); +const FOO: $0 = Foo(2); +"#, + expect![[r#" + it Foo<i32> + kw self + kw super + kw crate + tt Trait + en Enum + st Record + st Tuple + md module + st Foo<…> + st Unit + ma makro!(…) macro_rules! makro + un Union + bt u32 + "#]], + ); +} + +#[test] +fn inferred_type_closure_param() { + check( + r#" +fn f1(f: fn(i32) -> i32) {} +fn f2() { + f1(|x: $0); +} +"#, + expect![[r#" + it i32 + kw self + kw super + kw crate + tt Trait + en Enum + st Record + st Tuple + md module + st Unit + ma makro!(…) macro_rules! makro + un Union + bt u32 + "#]], + ); +} + +#[test] +fn inferred_type_closure_return() { + check( + r#" +fn f1(f: fn(u64) -> u64) {} +fn f2() { + f1(|x| -> $0 { + x + 5 + }); +} +"#, + expect![[r#" + it u64 + kw self + kw super + kw crate + tt Trait + en Enum + st Record + st Tuple + md module + st Unit + ma makro!(…) macro_rules! makro + un Union + bt u32 + "#]], + ); +} + +#[test] +fn inferred_type_fn_return() { + check( + r#" +fn f2(x: u64) -> $0 { + x + 5 +} +"#, + expect![[r#" + it u64 + kw self + kw super + kw crate + tt Trait + en Enum + st Record + st Tuple + md module + st Unit + ma makro!(…) macro_rules! makro + un Union + bt u32 + "#]], + ); +} + +#[test] +fn inferred_type_fn_param() { + check( + r#" +fn f1(x: i32) {} +fn f2(x: $0) { + f1(x); +} +"#, + expect![[r#" + it i32 + kw self + kw super + kw crate + tt Trait + en Enum + st Record + st Tuple + md module + st Unit + ma makro!(…) macro_rules! makro + un Union + bt u32 + "#]], + ); +} + +#[test] +fn inferred_type_not_in_the_scope() { + check( + r#" +mod a { + pub struct Foo<T>(T); + pub fn x() -> Foo<Foo<i32>> { + Foo(Foo(2)) + } +} +fn foo<'lt, T, const C: usize>() { + let local = (); + let foo: $0 = a::x(); +} +"#, + expect![[r#" + it a::Foo<a::Foo<i32>> + kw self + kw super + kw crate + tp T + tt Trait + en Enum + st Record + st Tuple + md module + st Unit + ma makro!(…) macro_rules! makro + un Union + md a + bt u32 + "#]], + ); +} + +#[test] +fn inferred_type_let() { + check( + r#" +struct Foo<T>(T); +fn foo<'lt, T, const C: usize>() { + let local = (); + let foo: $0 = Foo(2); +} +"#, + expect![[r#" + it Foo<i32> + kw self + kw super + kw crate + tp T + tt Trait + en Enum + st Record + st Tuple + md module + st Foo<…> + st Unit + ma makro!(…) macro_rules! makro + un Union + bt u32 + "#]], + ); +} + +#[test] fn body_type_pos() { check( r#" diff --git a/crates/rust-analyzer/src/to_proto.rs b/crates/rust-analyzer/src/to_proto.rs index 9b377f742b3..e52505d8a17 100644 --- a/crates/rust-analyzer/src/to_proto.rs +++ b/crates/rust-analyzer/src/to_proto.rs @@ -107,6 +107,7 @@ pub(crate) fn completion_item_kind( match completion_item_kind { CompletionItemKind::Binding => lsp_types::CompletionItemKind::VARIABLE, CompletionItemKind::BuiltinType => lsp_types::CompletionItemKind::STRUCT, + CompletionItemKind::InferredType => lsp_types::CompletionItemKind::SNIPPET, CompletionItemKind::Keyword => lsp_types::CompletionItemKind::KEYWORD, CompletionItemKind::Method => lsp_types::CompletionItemKind::METHOD, CompletionItemKind::Snippet => lsp_types::CompletionItemKind::SNIPPET, |
