Merge #11879
11879: Suggest infered type in auto complete r=HKalbasi a=HKalbasi fix #11855 It doesn't work for return types and consts (so their tests are failing) because I can't find their body node in the original file. (Are these original and fake file documented somewhere?) Also it currently needs to type first character of the type (or manual ctrl+space) to open the auto complete panel, is it possible to open it automatically on typing `:` and `->`? Co-authored-by: hkalbasi <hamidrezakalbasi@protonmail.com>
This commit is contained in:
commit
4204e35563
@ -302,9 +302,13 @@ fn lower_function(&mut self, func: &ast::Fn) -> Option<FileItemTreeId<Function>>
|
||||
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() {
|
||||
|
@ -21,12 +21,13 @@
|
||||
|
||||
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 @@
|
||||
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
|
||||
}
|
||||
|
@ -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 @@ pub fn score(self) -> u32 {
|
||||
is_op_method,
|
||||
is_private_editable,
|
||||
postfix_match,
|
||||
is_definite,
|
||||
} = self;
|
||||
|
||||
// lower rank private things
|
||||
@ -225,7 +228,9 @@ pub fn score(self) -> u32 {
|
||||
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 @@ pub(crate) fn tag(&self) -> &'static str {
|
||||
},
|
||||
CompletionItemKind::Binding => "bn",
|
||||
CompletionItemKind::BuiltinType => "bt",
|
||||
CompletionItemKind::InferredType => "it",
|
||||
CompletionItemKind::Keyword => "kw",
|
||||
CompletionItemKind::Method => "me",
|
||||
CompletionItemKind::Snippet => "sn",
|
||||
|
@ -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);
|
||||
|
@ -8,7 +8,7 @@
|
||||
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)
|
||||
}
|
||||
|
||||
|
@ -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,
|
||||
},
|
||||
},
|
||||
]
|
||||
|
@ -89,6 +89,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(
|
||||
|
@ -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,
|
||||
|
Loading…
Reference in New Issue
Block a user