about summary refs log tree commit diff
diff options
context:
space:
mode:
authorhkalbasi <hamidrezakalbasi@protonmail.com>2022-04-02 16:19:36 +0430
committerhkalbasi <hamidrezakalbasi@protonmail.com>2022-04-03 11:17:33 +0430
commit003a6b74e469713e99f23d8cff58af95c10b29e0 (patch)
tree1483bbebe8613a005833a1bf9a73300086356278
parentd312b4519ab35850fff8977f9e3c474a4eb8c0bc (diff)
downloadrust-003a6b74e469713e99f23d8cff58af95c10b29e0.tar.gz
rust-003a6b74e469713e99f23d8cff58af95c10b29e0.zip
suggest infered type in auto complete
-rw-r--r--crates/hir_def/src/item_tree/lower.rs10
-rw-r--r--crates/ide_completion/src/completions.rs20
-rw-r--r--crates/ide_completion/src/item.rs9
-rw-r--r--crates/ide_completion/src/lib.rs1
-rw-r--r--crates/ide_completion/src/patterns.rs75
-rw-r--r--crates/ide_completion/src/render.rs10
-rw-r--r--crates/ide_completion/src/tests/type_pos.rs200
-rw-r--r--crates/rust-analyzer/src/to_proto.rs1
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,