about summary refs log tree commit diff
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2022-06-17 14:28:52 +0000
committerbors <bors@rust-lang.org>2022-06-17 14:28:52 +0000
commit9c0b7277a604f5affde02e10122c5715faad468d (patch)
tree2973a26c18d07d6739f425a4b852dd4291d91f3a
parenta69b17be2eb981e8172bada48eb1593172528654 (diff)
parent85363d18e819c34b31b107096e80113e2a7a5891 (diff)
downloadrust-9c0b7277a604f5affde02e10122c5715faad468d.tar.gz
rust-9c0b7277a604f5affde02e10122c5715faad468d.zip
Auto merge of #12563 - Veykril:completion, r=Veykril
internal: Simplify
-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 3d92a0cceff..e97545eae50 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 9d3a1c24293..3b2e383a09d 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 d5f754d6658..767ea5c20de 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 dc50d82f199..c672c2761a5 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 ...
     (|| {