Unnamed repository; edit this file 'description' to name the repository.
Auto merge of #12563 - Veykril:completion, r=Veykril
internal: Simplify
bors 2022-06-17
parent a69b17b · parent 85363d1 · commit 9c0b727
-rw-r--r--crates/ide-completion/src/completions/expr.rs9
-rw-r--r--crates/ide-completion/src/completions/type.rs2
-rw-r--r--crates/ide-completion/src/context.rs4
-rw-r--r--crates/ide-completion/src/context/analysis.rs145
4 files changed, 95 insertions, 65 deletions
diff --git a/crates/ide-completion/src/completions/expr.rs b/crates/ide-completion/src/completions/expr.rs
index 3d92a0ccef..e97545eae5 100644
--- a/crates/ide-completion/src/completions/expr.rs
+++ b/crates/ide-completion/src/completions/expr.rs
@@ -2,7 +2,6 @@
use hir::ScopeDef;
use ide_db::FxHashSet;
-use syntax::T;
use crate::{
context::{NameRefContext, NameRefKind, PathCompletionCtx, PathKind, PathQualifierCtx},
@@ -20,6 +19,7 @@ pub(crate) fn complete_expr_path(acc: &mut Completions, ctx: &CompletionContext)
is_func_update,
after_if_expr,
wants_mut_token,
+ in_condition,
) = match ctx.nameref_ctx() {
Some(&NameRefContext {
kind:
@@ -29,6 +29,7 @@ pub(crate) fn complete_expr_path(acc: &mut Completions, ctx: &CompletionContext)
in_block_expr,
in_loop_body,
after_if_expr,
+ in_condition,
ref ref_expr_parent,
ref is_func_update,
},
@@ -45,6 +46,7 @@ pub(crate) fn complete_expr_path(acc: &mut Completions, ctx: &CompletionContext)
is_func_update.is_some(),
after_if_expr,
ref_expr_parent.as_ref().map(|it| it.mut_token().is_none()).unwrap_or(false),
+ in_condition,
),
_ => return,
};
@@ -235,10 +237,7 @@ pub(crate) fn complete_expr_path(acc: &mut Completions, ctx: &CompletionContext)
add_keyword("true", "true");
add_keyword("false", "false");
- if ctx.previous_token_is(T![if])
- || ctx.previous_token_is(T![while])
- || in_block_expr
- {
+ if (in_condition && !is_absolute_path) || in_block_expr {
add_keyword("let", "let");
}
diff --git a/crates/ide-completion/src/completions/type.rs b/crates/ide-completion/src/completions/type.rs
index 9d3a1c2429..3b2e383a09 100644
--- a/crates/ide-completion/src/completions/type.rs
+++ b/crates/ide-completion/src/completions/type.rs
@@ -34,7 +34,7 @@ pub(crate) fn complete_type_path(acc: &mut Completions, ctx: &CompletionContext)
matches!(location, TypeLocation::GenericArgList(_))
}
ScopeDef::ImplSelfType(_) => {
- !ctx.previous_token_is(syntax::T![impl]) && !ctx.previous_token_is(syntax::T![for])
+ !matches!(location, TypeLocation::ImplTarget | TypeLocation::ImplTrait)
}
// Don't suggest attribute macros and derives.
ScopeDef::ModuleDef(Macro(mac)) => mac.is_fn_like(ctx.db),
diff --git a/crates/ide-completion/src/context.rs b/crates/ide-completion/src/context.rs
index d5f754d665..767ea5c20d 100644
--- a/crates/ide-completion/src/context.rs
+++ b/crates/ide-completion/src/context.rs
@@ -92,6 +92,8 @@ pub(super) enum PathKind {
in_block_expr: bool,
in_loop_body: bool,
after_if_expr: bool,
+ /// Whether this expression is the direct condition of an if or while expression
+ in_condition: bool,
ref_expr_parent: Option<ast::RefExpr>,
is_func_update: Option<ast::RecordExpr>,
},
@@ -121,6 +123,8 @@ pub(crate) enum TypeLocation {
TypeAscription(TypeAscriptionTarget),
GenericArgList(Option<ast::GenericArgList>),
TypeBound,
+ ImplTarget,
+ ImplTrait,
Other,
}
diff --git a/crates/ide-completion/src/context/analysis.rs b/crates/ide-completion/src/context/analysis.rs
index dc50d82f19..c672c2761a 100644
--- a/crates/ide-completion/src/context/analysis.rs
+++ b/crates/ide-completion/src/context/analysis.rs
@@ -327,7 +327,8 @@ impl<'a> CompletionContext<'a> {
return None;
}
- self.previous_token = previous_token(syntax_element.clone());
+ self.previous_token =
+ syntax_element.clone().into_token().and_then(previous_non_trivia_token);
self.incomplete_let =
syntax_element.ancestors().take(6).find_map(ast::LetStmt::cast).map_or(false, |it| {
@@ -647,8 +648,8 @@ impl<'a> CompletionContext<'a> {
None
};
- let type_location = |it: Option<SyntaxNode>| {
- let parent = it?;
+ let type_location = |node: &SyntaxNode| {
+ let parent = node.parent()?;
let res = match_ast! {
match parent {
ast::Const(it) => {
@@ -690,6 +691,15 @@ impl<'a> CompletionContext<'a> {
}
TypeLocation::TypeAscription(TypeAscriptionTarget::Let(find_opt_node_in_file(original_file, it.pat())))
},
+ ast::Impl(it) => {
+ match it.trait_() {
+ Some(t) if t.syntax() == node => TypeLocation::ImplTrait,
+ _ => match it.self_ty() {
+ Some(t) if t.syntax() == node => TypeLocation::ImplTarget,
+ _ => return None,
+ },
+ }
+ },
ast::TypeBound(_) => TypeLocation::TypeBound,
// is this case needed?
ast::TypeBoundList(_) => TypeLocation::TypeBound,
@@ -703,16 +713,49 @@ impl<'a> CompletionContext<'a> {
Some(res)
};
+ let is_in_condition = |it: &ast::Expr| {
+ (|| {
+ let parent = it.syntax().parent()?;
+ if let Some(expr) = ast::WhileExpr::cast(parent.clone()) {
+ Some(expr.condition()? == *it)
+ } else if let Some(expr) = ast::IfExpr::cast(parent) {
+ Some(expr.condition()? == *it)
+ } else {
+ None
+ }
+ })()
+ .unwrap_or(false)
+ };
+
+ let make_path_kind_expr = |expr: ast::Expr| {
+ let it = expr.syntax();
+ let in_block_expr = is_in_block(it);
+ let in_loop_body = is_in_loop_body(it);
+ let after_if_expr = after_if_expr(it.clone());
+ let ref_expr_parent =
+ path.as_single_name_ref().and_then(|_| it.parent()).and_then(ast::RefExpr::cast);
+ let is_func_update = func_update_record(it);
+ let in_condition = is_in_condition(&expr);
+
+ PathKind::Expr {
+ in_block_expr,
+ in_loop_body,
+ after_if_expr,
+ in_condition,
+ ref_expr_parent,
+ is_func_update,
+ }
+ };
+ let make_path_kind_type = |ty: ast::Type| {
+ let location = type_location(ty.syntax());
+ PathKind::Type { location: location.unwrap_or(TypeLocation::Other) }
+ };
+
// Infer the path kind
let kind = path.syntax().parent().and_then(|it| {
match_ast! {
match it {
- ast::PathType(it) => {
- let location = type_location(it.syntax().parent());
- Some(PathKind::Type {
- location: location.unwrap_or(TypeLocation::Other),
- })
- },
+ ast::PathType(it) => Some(make_path_kind_type(it.into())),
ast::PathExpr(it) => {
if let Some(p) = it.syntax().parent() {
if ast::ExprStmt::can_cast(p.kind()) {
@@ -724,14 +767,8 @@ impl<'a> CompletionContext<'a> {
}
path_ctx.has_call_parens = it.syntax().parent().map_or(false, |it| ast::CallExpr::can_cast(it.kind()));
- let in_block_expr = is_in_block(it.syntax());
- let in_loop_body = is_in_loop_body(it.syntax());
- let after_if_expr = after_if_expr(it.syntax().clone());
- let ref_expr_parent = path.as_single_name_ref()
- .and_then(|_| it.syntax().parent()).and_then(ast::RefExpr::cast);
- let is_func_update = func_update_record(it.syntax());
-
- Some(PathKind::Expr { in_block_expr, in_loop_body, after_if_expr, ref_expr_parent, is_func_update })
+
+ Some(make_path_kind_expr(it.into()))
},
ast::TupleStructPat(it) => {
path_ctx.has_call_parens = true;
@@ -748,50 +785,41 @@ impl<'a> CompletionContext<'a> {
Some(PathKind::Pat)
},
ast::MacroCall(it) => {
+ // A macro call in this position is usually a result of parsing recovery, so check that
if let Some(kind) = inbetween_body_and_decl_check(it.syntax().clone()) {
nameref_ctx.kind = Some(NameRefKind::Keyword(kind));
return None;
}
path_ctx.has_macro_bang = it.excl_token().is_some();
- let parent = it.syntax().parent();
- match parent.as_ref().map(|it| it.kind()) {
- Some(SyntaxKind::MACRO_PAT) => Some(PathKind::Pat),
- Some(SyntaxKind::MACRO_TYPE) => {
- let location = type_location(parent.unwrap().parent());
- Some(PathKind::Type {
- location: location.unwrap_or(TypeLocation::Other),
- })
- },
- Some(SyntaxKind::ITEM_LIST) => Some(PathKind::Item { kind: ItemListKind::Module }),
- Some(SyntaxKind::ASSOC_ITEM_LIST) => Some(PathKind::Item { kind: match parent.and_then(|it| it.parent()) {
- Some(it) => match_ast! {
- match it {
- ast::Trait(_) => ItemListKind::Trait,
- ast::Impl(it) => if it.trait_().is_some() {
- ItemListKind::TraitImpl
- } else {
- ItemListKind::Impl
- },
- _ => return None
- }
- },
- None => return None,
- } }),
- Some(SyntaxKind::EXTERN_ITEM_LIST) => Some(PathKind::Item { kind: ItemListKind::ExternBlock }),
- Some(SyntaxKind::SOURCE_FILE) => Some(PathKind::Item { kind: ItemListKind::SourceFile }),
- _ => {
- return parent.and_then(ast::MacroExpr::cast).map(|it| {
- let in_loop_body = is_in_loop_body(it.syntax());
- let in_block_expr = is_in_block(it.syntax());
- let after_if_expr = after_if_expr(it.syntax().clone());
- let ref_expr_parent = path.as_single_name_ref()
- .and_then(|_| it.syntax().parent()).and_then(ast::RefExpr::cast);
- let is_func_update = func_update_record(it.syntax());
- PathKind::Expr { in_block_expr, in_loop_body, after_if_expr, ref_expr_parent, is_func_update }
- });
- },
- }
+ let parent = it.syntax().parent()?;
+ // Any path in an item list will be treated as a macro call by the parser
+ let res = match_ast! {
+ match parent {
+ ast::MacroExpr(expr) => make_path_kind_expr(expr.into()),
+ ast::MacroPat(_) => PathKind::Pat,
+ ast::MacroType(ty) => make_path_kind_type(ty.into()),
+ ast::ItemList(_) => PathKind::Item { kind: ItemListKind::Module },
+ ast::AssocItemList(_) => PathKind::Item { kind: match parent.parent() {
+ Some(it) => match_ast! {
+ match it {
+ ast::Trait(_) => ItemListKind::Trait,
+ ast::Impl(it) => if it.trait_().is_some() {
+ ItemListKind::TraitImpl
+ } else {
+ ItemListKind::Impl
+ },
+ _ => return None
+ }
+ },
+ None => return None,
+ } },
+ ast::ExternItemList(_) => PathKind::Item { kind: ItemListKind::ExternBlock },
+ ast::SourceFile(_) => PathKind::Item { kind: ItemListKind::SourceFile },
+ _ => return None,
+ }
+ };
+ Some(res)
},
ast::Meta(meta) => (|| {
let attr = meta.parent_attr()?;
@@ -818,10 +846,13 @@ impl<'a> CompletionContext<'a> {
match kind {
Some(kind) => path_ctx.kind = kind,
+ // unresolved path kind, so this isn't really a path we should be completing,
+ // just some random identifier which might be in keyword position
None => return res,
}
path_ctx.has_type_args = segment.generic_arg_list().is_some();
+ // calculate the qualifier context
if let Some((path, use_tree_parent)) = path_or_use_tree_qualifier(&path) {
if !use_tree_parent {
path_ctx.is_absolute_path =
@@ -1034,10 +1065,6 @@ fn has_ref(token: &SyntaxToken) -> bool {
token.kind() == T![&]
}
-pub(crate) fn previous_token(element: SyntaxElement) -> Option<SyntaxToken> {
- element.into_token().and_then(previous_non_trivia_token)
-}
-
pub(crate) fn is_in_token_of_for_loop(element: SyntaxElement) -> bool {
// oh my ...
(|| {