about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--crates/ide-completion/src/completions/expr.rs16
-rw-r--r--crates/ide-completion/src/context/analysis.rs187
2 files changed, 118 insertions, 85 deletions
diff --git a/crates/ide-completion/src/completions/expr.rs b/crates/ide-completion/src/completions/expr.rs
index d4f2766602f..bafaeb502ad 100644
--- a/crates/ide-completion/src/completions/expr.rs
+++ b/crates/ide-completion/src/completions/expr.rs
@@ -11,7 +11,14 @@ pub(crate) fn complete_expr_path(
     acc: &mut Completions,
     ctx: &CompletionContext<'_>,
     path_ctx @ PathCompletionCtx { qualified, .. }: &PathCompletionCtx,
-    &ExprCtx {
+    expr_ctx: &ExprCtx,
+) {
+    let _p = profile::span("complete_expr_path");
+    if !ctx.qualifier_ctx.none() {
+        return;
+    }
+
+    let &ExprCtx {
         in_block_expr,
         in_loop_body,
         after_if_expr,
@@ -23,12 +30,7 @@ pub(crate) fn complete_expr_path(
         ref impl_,
         in_match_guard,
         ..
-    }: &ExprCtx,
-) {
-    let _p = profile::span("complete_expr_path");
-    if !ctx.qualifier_ctx.none() {
-        return;
-    }
+    } = expr_ctx;
 
     let wants_mut_token =
         ref_expr_parent.as_ref().map(|it| it.mut_token().is_none()).unwrap_or(false);
diff --git a/crates/ide-completion/src/context/analysis.rs b/crates/ide-completion/src/context/analysis.rs
index 3e7e637dd9e..76fc74c01d2 100644
--- a/crates/ide-completion/src/context/analysis.rs
+++ b/crates/ide-completion/src/context/analysis.rs
@@ -592,7 +592,7 @@ impl<'a> CompletionContext<'a> {
             has_call_parens: false,
             has_macro_bang: false,
             qualified: Qualified::No,
-            parent: path.parent_path(),
+            parent: None,
             path: path.clone(),
             kind: PathKind::Item { kind: ItemListKind::SourceFile },
             has_type_args: false,
@@ -827,92 +827,123 @@ impl<'a> CompletionContext<'a> {
             PathKind::Type { location: location.unwrap_or(TypeLocation::Other) }
         };
 
+        let mut kind_macro_call = |it: ast::MacroCall| {
+            path_ctx.has_macro_bang = it.excl_token().is_some();
+            let parent = it.syntax().parent()?;
+            // Any path in an item list will be treated as a macro call by the parser
+            let kind = match_ast! {
+                match parent {
+                    ast::MacroExpr(expr) => make_path_kind_expr(expr.into()),
+                    ast::MacroPat(it) => PathKind::Pat { pat_ctx: pattern_context_for(sema, original_file, it.into())},
+                    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(find_node_in_file_compensated(sema, original_file, &it))
+                                } 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(kind)
+        };
+        let make_path_kind_attr = |meta: ast::Meta| {
+            let attr = meta.parent_attr()?;
+            let kind = attr.kind();
+            let attached = attr.syntax().parent()?;
+            let is_trailing_outer_attr = kind != AttrKind::Inner
+                && non_trivia_sibling(attr.syntax().clone().into(), syntax::Direction::Next)
+                    .is_none();
+            let annotated_item_kind =
+                if is_trailing_outer_attr { None } else { Some(attached.kind()) };
+            Some(PathKind::Attr { attr_ctx: AttrCtx { kind, annotated_item_kind } })
+        };
+
         // Infer the path kind
         let parent = path.syntax().parent()?;
         let kind = match_ast! {
-                match parent {
-                    ast::PathType(it) => make_path_kind_type(it.into()),
-                    ast::PathExpr(it) => {
-                        if let Some(p) = it.syntax().parent() {
-                            if ast::ExprStmt::can_cast(p.kind()) {
-                                if let Some(kind) = inbetween_body_and_decl_check(p) {
-                                    return Some(make_res(NameRefKind::Keyword(kind)));
-                                }
+            match parent {
+                ast::PathType(it) => make_path_kind_type(it.into()),
+                ast::PathExpr(it) => {
+                    if let Some(p) = it.syntax().parent() {
+                        if ast::ExprStmt::can_cast(p.kind()) {
+                            if let Some(kind) = inbetween_body_and_decl_check(p) {
+                                return Some(make_res(NameRefKind::Keyword(kind)));
                             }
                         }
+                    }
 
-                        path_ctx.has_call_parens = it.syntax().parent().map_or(false, |it| ast::CallExpr::can_cast(it.kind()));
+                    path_ctx.has_call_parens = it.syntax().parent().map_or(false, |it| ast::CallExpr::can_cast(it.kind()));
 
-                        make_path_kind_expr(it.into())
-                    },
-                    ast::TupleStructPat(it) => {
-                        path_ctx.has_call_parens = true;
-                        PathKind::Pat { pat_ctx: pattern_context_for(sema, original_file, it.into())}
-                    },
-                    ast::RecordPat(it) => {
-                        path_ctx.has_call_parens = true;
-                        PathKind::Pat { pat_ctx: pattern_context_for(sema, original_file, it.into())}
-                    },
-                    ast::PathPat(it) => {
-                        PathKind::Pat { pat_ctx: pattern_context_for(sema, original_file, it.into())}
-                    },
-                    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()) {
-                            return Some(make_res(NameRefKind::Keyword(kind)));
-                        }
+                    make_path_kind_expr(it.into())
+                },
+                ast::TupleStructPat(it) => {
+                    path_ctx.has_call_parens = true;
+                    PathKind::Pat { pat_ctx: pattern_context_for(sema, original_file, it.into()) }
+                },
+                ast::RecordPat(it) => {
+                    path_ctx.has_call_parens = true;
+                    PathKind::Pat { pat_ctx: pattern_context_for(sema, original_file, it.into()) }
+                },
+                ast::PathPat(it) => {
+                    PathKind::Pat { pat_ctx: pattern_context_for(sema, original_file, it.into())}
+                },
+                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()) {
+                        return Some(make_res(NameRefKind::Keyword(kind)));
+                    }
 
-                        path_ctx.has_macro_bang = it.excl_token().is_some();
-                        let parent = it.syntax().parent()?;
-                        // Any path in an item list will be treated as a macro call by the parser
-                        match_ast! {
-                            match parent {
-                                ast::MacroExpr(expr) => make_path_kind_expr(expr.into()),
-                                ast::MacroPat(it) => PathKind::Pat { pat_ctx: pattern_context_for(sema, original_file, it.into())},
-                                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(find_node_in_file_compensated(sema, original_file, &it))
-                                            } else {
-                                                ItemListKind::Impl
-                                            },
-                                            _ => return None
-                                        }
-                                    },
-                                    None => return None,
-                                } },
-                                ast::ExternItemList(_) => PathKind::Item { kind: ItemListKind::ExternBlock },
-                                ast::SourceFile(_) => PathKind::Item { kind: ItemListKind::SourceFile },
-                                _ => return None,
-                            }
-                        }
-                    },
-                    ast::Meta(meta) => {
-                        let attr = meta.parent_attr()?;
-                        let kind = attr.kind();
-                        let attached = attr.syntax().parent()?;
-                        let is_trailing_outer_attr = kind != AttrKind::Inner
-                            && non_trivia_sibling(attr.syntax().clone().into(), syntax::Direction::Next).is_none();
-                        let annotated_item_kind = if is_trailing_outer_attr {
-                            None
-                        } else {
-                            Some(attached.kind())
-                        };
-                        PathKind::Attr {
-                            attr_ctx: AttrCtx {
-                                kind,
-                                annotated_item_kind,
-                            }
+                    kind_macro_call(it)?
+                },
+                ast::Meta(meta) => make_path_kind_attr(meta)?,
+                ast::Visibility(it) => PathKind::Vis { has_in_token: it.in_token().is_some() },
+                ast::UseTree(_) => PathKind::Use,
+                // completing inside a qualifier
+                ast::Path(parent) => {
+                    path_ctx.parent = Some(parent.clone());
+                    let parent = iter::successors(Some(parent), |it| it.parent_path()).last()?.syntax().parent()?;
+                    match_ast! {
+                        match parent {
+                            ast::PathType(it) => make_path_kind_type(it.into()),
+                            ast::PathExpr(it) => {
+                                path_ctx.has_call_parens = it.syntax().parent().map_or(false, |it| ast::CallExpr::can_cast(it.kind()));
+
+                                make_path_kind_expr(it.into())
+                            },
+                            ast::TupleStructPat(it) => {
+                                path_ctx.has_call_parens = true;
+                                PathKind::Pat { pat_ctx: pattern_context_for(sema, original_file, it.into()) }
+                            },
+                            ast::RecordPat(it) => {
+                                path_ctx.has_call_parens = true;
+                                PathKind::Pat { pat_ctx: pattern_context_for(sema, original_file, it.into()) }
+                            },
+                            ast::PathPat(it) => {
+                                PathKind::Pat { pat_ctx: pattern_context_for(sema, original_file, it.into())}
+                            },
+                            ast::MacroCall(it) => {
+                                kind_macro_call(it)?
+                            },
+                            ast::Meta(meta) => make_path_kind_attr(meta)?,
+                            ast::Visibility(it) => PathKind::Vis { has_in_token: it.in_token().is_some() },
+                            ast::UseTree(_) => PathKind::Use,
+                            _ => return None,
                         }
-                    },
-                    ast::Visibility(it) => PathKind::Vis { has_in_token: it.in_token().is_some() },
-                    ast::UseTree(_) => PathKind::Use,
-                    _ => return None,
-
+                    }
+                },
+                _ => return None,
             }
         };