]> git.lizzy.rs Git - rust.git/commitdiff
internal: completion: split out more PathKinds from ImmediateLocation
authorLukas Wirth <lukastw97@gmail.com>
Mon, 6 Dec 2021 14:51:33 +0000 (15:51 +0100)
committerLukas Wirth <lukastw97@gmail.com>
Tue, 7 Dec 2021 21:49:42 +0000 (22:49 +0100)
crates/ide_completion/src/completions/flyimport.rs
crates/ide_completion/src/completions/keyword.rs
crates/ide_completion/src/completions/pattern.rs
crates/ide_completion/src/completions/qualified_path.rs
crates/ide_completion/src/completions/unqualified_path.rs
crates/ide_completion/src/context.rs
crates/ide_completion/src/patterns.rs
crates/ide_completion/src/render/builder_ext.rs
crates/ide_completion/src/render/macro_.rs
crates/ide_completion/src/tests/flyimport.rs

index 446a808de84384656c572ba51d9b3fc48a777a0c..3a9c1b3beb738f7446c29f657350012686350d95 100644 (file)
@@ -110,7 +110,7 @@ pub(crate) fn import_on_the_fly(acc: &mut Completions, ctx: &CompletionContext)
     if !ctx.config.enable_imports_on_the_fly {
         return None;
     }
-    if ctx.in_use_tree()
+    if matches!(ctx.path_kind(), Some(PathKind::Vis { .. } | PathKind::Use))
         || ctx.is_path_disallowed()
         || ctx.expects_item()
         || ctx.expects_assoc_item()
@@ -118,6 +118,11 @@ pub(crate) fn import_on_the_fly(acc: &mut Completions, ctx: &CompletionContext)
     {
         return None;
     }
+    // FIXME: This should be encoded in a different way
+    if ctx.pattern_ctx.is_none() && ctx.path_context.is_none() && !ctx.has_dot_receiver() {
+        // completion inside `ast::Name` of a item declaration
+        return None;
+    }
     let potential_import_name = {
         let token_kind = ctx.token.kind();
         if matches!(token_kind, T![.] | T![::]) {
@@ -147,14 +152,25 @@ pub(crate) fn import_on_the_fly(acc: &mut Completions, ctx: &CompletionContext)
             }
         };
         match (kind, import.original_item) {
+            // Aren't handled in flyimport
+            (PathKind::Vis { .. } | PathKind::Use, _) => false,
+            // modules are always fair game
+            (_, ItemInNs::Types(hir::ModuleDef::Module(_))) => true,
+            // and so are macros(except for attributes)
+            (
+                PathKind::Expr | PathKind::Type | PathKind::Mac | PathKind::Pat,
+                ItemInNs::Macros(mac),
+            ) => mac.is_fn_like(),
+            (PathKind::Mac, _) => true,
+
             (PathKind::Expr, ItemInNs::Types(_) | ItemInNs::Values(_)) => true,
 
+            (PathKind::Pat, ItemInNs::Types(_)) => true,
+            (PathKind::Pat, ItemInNs::Values(def)) => matches!(def, hir::ModuleDef::Const(_)),
+
             (PathKind::Type, ItemInNs::Types(_)) => true,
             (PathKind::Type, ItemInNs::Values(_)) => false,
 
-            (PathKind::Expr | PathKind::Type, ItemInNs::Macros(mac)) => mac.is_fn_like(),
-
-            (PathKind::Attr, ItemInNs::Types(hir::ModuleDef::Module(_))) => true,
             (PathKind::Attr, ItemInNs::Macros(mac)) => mac.is_attr(),
             (PathKind::Attr, _) => false,
         }
index 36d25d5d029334e8423607883e04df13c6506df5..1c686dc0a63efb0d95da2dcf38e8d96356c40a8f 100644 (file)
@@ -5,8 +5,9 @@
 use syntax::{SyntaxKind, T};
 
 use crate::{
-    context::PathCompletionContext, patterns::ImmediateLocation, CompletionContext, CompletionItem,
-    CompletionItemKind, Completions,
+    context::{PathCompletionContext, PathKind},
+    patterns::ImmediateLocation,
+    CompletionContext, CompletionItem, CompletionItemKind, Completions,
 };
 
 pub(crate) fn complete_expr_keyword(acc: &mut Completions, ctx: &CompletionContext) {
@@ -33,8 +34,8 @@ pub(crate) fn complete_expr_keyword(acc: &mut Completions, ctx: &CompletionConte
     let has_block_expr_parent = ctx.has_block_expr_parent();
     let expects_item = ctx.expects_item();
 
-    if let Some(ImmediateLocation::Visibility(vis)) = &ctx.completion_location {
-        if vis.in_token().is_none() {
+    if let Some(PathKind::Vis { has_in_token }) = ctx.path_kind() {
+        if !has_in_token {
             cov_mark::hit!(kw_completion_in);
             add_keyword("in", "in");
         }
index c63c274151cec0a70cc7b5e91146155e394c0410..047659014286cfaff8a7ac2de37cc6ae0b365e70 100644 (file)
@@ -1,15 +1,17 @@
-//! Completes constants and paths in patterns.
+//! Completes constants and paths in unqualified patterns.
 
 use crate::{
     context::{PatternContext, PatternRefutability},
     CompletionContext, Completions,
 };
 
-/// Completes constants and paths in patterns.
+/// Completes constants and paths in unqualified patterns.
 pub(crate) fn complete_pattern(acc: &mut Completions, ctx: &CompletionContext) {
     let refutable = match ctx.pattern_ctx {
-        Some(PatternContext { refutability, .. }) => refutability == PatternRefutability::Refutable,
-        None => return,
+        Some(PatternContext { refutability, .. }) if ctx.path_context.is_none() => {
+            refutability == PatternRefutability::Refutable
+        }
+        _ => return,
     };
 
     if refutable {
index 4abf7374b3b2a66140a990f6dbac9761cfe88fef..b5c3d83c16849d2237a152b8a5a151e6bed7f015 100644 (file)
@@ -7,17 +7,23 @@
 use syntax::{ast, AstNode};
 
 use crate::{
-    context::PathCompletionContext, patterns::ImmediateLocation, CompletionContext, Completions,
+    context::{PathCompletionContext, PathKind},
+    patterns::ImmediateLocation,
+    CompletionContext, Completions,
 };
 
 pub(crate) fn complete_qualified_path(acc: &mut Completions, ctx: &CompletionContext) {
     if ctx.is_path_disallowed() || ctx.has_impl_or_trait_prev_sibling() {
         return;
     }
-    let (path, use_tree_parent) = match &ctx.path_context {
-        Some(PathCompletionContext { qualifier: Some(qualifier), use_tree_parent, .. }) => {
-            (qualifier, *use_tree_parent)
-        }
+    let (path, use_tree_parent, kind) = match ctx.path_context {
+        // let ... else, syntax would come in really handy here right now
+        Some(PathCompletionContext {
+            qualifier: Some(ref qualifier),
+            use_tree_parent,
+            kind,
+            ..
+        }) => (qualifier, use_tree_parent, kind),
         _ => return,
     };
 
@@ -44,7 +50,11 @@ pub(crate) fn complete_qualified_path(acc: &mut Completions, ctx: &CompletionCon
             }
             return;
         }
-        Some(ImmediateLocation::Visibility(_)) => {
+        _ => (),
+    }
+
+    match kind {
+        Some(PathKind::Vis { .. }) => {
             if let hir::PathResolution::Def(hir::ModuleDef::Module(module)) = resolution {
                 if let Some(current_module) = ctx.scope.module() {
                     if let Some(next) = current_module
@@ -61,7 +71,7 @@ pub(crate) fn complete_qualified_path(acc: &mut Completions, ctx: &CompletionCon
             }
             return;
         }
-        Some(ImmediateLocation::Attribute(_)) => {
+        Some(PathKind::Attr) => {
             if let hir::PathResolution::Def(hir::ModuleDef::Module(module)) = resolution {
                 for (name, def) in module.scope(ctx.db, context_module) {
                     let add_resolution = match def {
@@ -76,37 +86,38 @@ pub(crate) fn complete_qualified_path(acc: &mut Completions, ctx: &CompletionCon
             }
             return;
         }
+        Some(PathKind::Use) => {
+            if iter::successors(Some(path.clone()), |p| p.qualifier())
+                .all(|p| p.segment().and_then(|s| s.super_token()).is_some())
+            {
+                acc.add_keyword(ctx, "super::");
+            }
+            // only show `self` in a new use-tree when the qualifier doesn't end in self
+            if use_tree_parent
+                && !matches!(
+                    path.segment().and_then(|it| it.kind()),
+                    Some(ast::PathSegmentKind::SelfKw)
+                )
+            {
+                acc.add_keyword(ctx, "self");
+            }
+        }
         _ => (),
     }
 
-    if ctx.in_use_tree() {
-        if iter::successors(Some(path.clone()), |p| p.qualifier())
-            .all(|p| p.segment().and_then(|s| s.super_token()).is_some())
-        {
-            acc.add_keyword(ctx, "super::");
-        }
-        // only show `self` in a new use-tree when the qualifier doesn't end in self
-        if use_tree_parent
-            && !matches!(
-                path.segment().and_then(|it| it.kind()),
-                Some(ast::PathSegmentKind::SelfKw)
-            )
-        {
-            acc.add_keyword(ctx, "self");
-        }
+    if !matches!(kind, Some(PathKind::Pat)) {
+        // Add associated types on type parameters and `Self`.
+        resolution.assoc_type_shorthand_candidates(ctx.db, |_, alias| {
+            acc.add_type_alias(ctx, alias);
+            None::<()>
+        });
     }
 
-    // Add associated types on type parameters and `Self`.
-    resolution.assoc_type_shorthand_candidates(ctx.db, |_, alias| {
-        acc.add_type_alias(ctx, alias);
-        None::<()>
-    });
-
     match resolution {
         hir::PathResolution::Def(hir::ModuleDef::Module(module)) => {
             let module_scope = module.scope(ctx.db, context_module);
             for (name, def) in module_scope {
-                if ctx.in_use_tree() {
+                if let Some(PathKind::Use) = kind {
                     if let ScopeDef::Unknown = def {
                         if let Some(ast::NameLike::NameRef(name_ref)) = ctx.name_syntax.as_ref() {
                             if name_ref.syntax().text() == name.to_smol_str().as_str() {
index 414c1d961bbf3991d640c47118f6e6dd535e315f..ac1ba2da7c4348ecf0c1e7f439bc3e096ae24c44 100644 (file)
@@ -3,15 +3,23 @@
 use hir::ScopeDef;
 use syntax::{ast, AstNode};
 
-use crate::{patterns::ImmediateLocation, CompletionContext, Completions};
+use crate::{
+    context::{PathCompletionContext, PathKind},
+    patterns::ImmediateLocation,
+    CompletionContext, Completions,
+};
 
 pub(crate) fn complete_unqualified_path(acc: &mut Completions, ctx: &CompletionContext) {
     let _p = profile::span("complete_unqualified_path");
-    if ctx.is_path_disallowed() || !ctx.is_trivial_path() || ctx.has_impl_or_trait_prev_sibling() {
+    if ctx.is_path_disallowed() || ctx.has_impl_or_trait_prev_sibling() {
         return;
     }
+    let kind = match ctx.path_context {
+        Some(PathCompletionContext { is_trivial_path: true, kind, .. }) => kind,
+        _ => return,
+    };
 
-    if ctx.in_use_tree() {
+    if let Some(PathKind::Use) = kind {
         // only show modules in a fresh UseTree
         cov_mark::hit!(unqualified_path_only_modules_in_import);
         ctx.process_all_names(&mut |name, res| {
@@ -25,8 +33,25 @@ pub(crate) fn complete_unqualified_path(acc: &mut Completions, ctx: &CompletionC
     }
     ["self", "super", "crate"].into_iter().for_each(|kw| acc.add_keyword(ctx, kw));
 
+    match kind {
+        Some(PathKind::Vis { .. }) => return,
+        Some(PathKind::Attr) => {
+            ctx.process_all_names(&mut |name, res| {
+                let add_resolution = match res {
+                    ScopeDef::MacroDef(mac) => mac.is_attr(),
+                    ScopeDef::ModuleDef(hir::ModuleDef::Module(_)) => true,
+                    _ => false,
+                };
+                if add_resolution {
+                    acc.add_resolution(ctx, name, &res);
+                }
+            });
+            return;
+        }
+        _ => (),
+    }
+
     match &ctx.completion_location {
-        Some(ImmediateLocation::Visibility(_)) => return,
         Some(ImmediateLocation::ItemList | ImmediateLocation::Trait | ImmediateLocation::Impl) => {
             // only show macros in {Assoc}ItemList
             ctx.process_all_names(&mut |name, res| {
@@ -56,19 +81,6 @@ pub(crate) fn complete_unqualified_path(acc: &mut Completions, ctx: &CompletionC
             });
             return;
         }
-        Some(ImmediateLocation::Attribute(_)) => {
-            ctx.process_all_names(&mut |name, res| {
-                let add_resolution = match res {
-                    ScopeDef::MacroDef(mac) => mac.is_attr(),
-                    ScopeDef::ModuleDef(hir::ModuleDef::Module(_)) => true,
-                    _ => false,
-                };
-                if add_resolution {
-                    acc.add_resolution(ctx, name, &res);
-                }
-            });
-            return;
-        }
         _ => (),
     }
 
index b3ce1f8e9c4bca8ec94143ec897d642b5051996f..e8566b80b6f2564b8f8e098e6eb39b04272f22ab 100644 (file)
@@ -35,16 +35,23 @@ pub(super) enum PathKind {
     Expr,
     Type,
     Attr,
+    Mac,
+    Pat,
+    Vis { has_in_token: bool },
+    Use,
 }
 
 #[derive(Debug)]
 pub(crate) struct PathCompletionContext {
     /// If this is a call with () already there
-    call_kind: Option<CallKind>,
+    has_call_parens: bool,
     /// A single-indent path, like `foo`. `::foo` should not be considered a trivial path.
     pub(super) is_trivial_path: bool,
     /// If not a trivial path, the prefix (qualifier).
     pub(super) qualifier: Option<ast::Path>,
+    #[allow(dead_code)]
+    /// If not a trivial path, the suffix (parent).
+    pub(super) parent: Option<ast::Path>,
     /// Whether the qualifier comes from a use tree parent or not
     pub(super) use_tree_parent: bool,
     pub(super) kind: Option<PathKind>,
@@ -70,13 +77,6 @@ pub(super) enum LifetimeContext {
     LabelDef,
 }
 
-#[derive(Copy, Clone, Debug, PartialEq, Eq)]
-pub(crate) enum CallKind {
-    Pat,
-    Mac,
-    Expr,
-}
-
 #[derive(Copy, Clone, Debug, PartialEq, Eq)]
 pub(crate) enum ParamKind {
     Function,
@@ -206,13 +206,6 @@ pub(crate) fn expect_field(&self) -> bool {
         )
     }
 
-    pub(crate) fn in_use_tree(&self) -> bool {
-        matches!(
-            self.completion_location,
-            Some(ImmediateLocation::Use | ImmediateLocation::UseTree)
-        )
-    }
-
     pub(crate) fn has_impl_or_trait_prev_sibling(&self) -> bool {
         matches!(
             self.prev_sibling,
@@ -257,8 +250,8 @@ pub(crate) fn expects_type(&self) -> bool {
         matches!(self.path_context, Some(PathCompletionContext { kind: Some(PathKind::Type), .. }))
     }
 
-    pub(crate) fn path_call_kind(&self) -> Option<CallKind> {
-        self.path_context.as_ref().and_then(|it| it.call_kind)
+    pub(crate) fn path_is_call(&self) -> bool {
+        self.path_context.as_ref().map_or(false, |it| it.has_call_parens)
     }
 
     pub(crate) fn is_trivial_path(&self) -> bool {
@@ -673,7 +666,12 @@ fn fill(
                     Self::classify_lifetime(&self.sema, original_file, lifetime, offset);
             }
             ast::NameLike::NameRef(name_ref) => {
-                self.path_context = Self::classify_name_ref(&self.sema, original_file, name_ref);
+                if let Some((path_ctx, pat_ctx)) =
+                    Self::classify_name_ref(&self.sema, original_file, name_ref)
+                {
+                    self.path_context = Some(path_ctx);
+                    self.pattern_ctx = pat_ctx;
+                }
             }
             ast::NameLike::Name(name) => {
                 self.pattern_ctx = Self::classify_name(&self.sema, name);
@@ -716,83 +714,61 @@ fn classify_name(_sema: &Semantics<RootDatabase>, name: ast::Name) -> Option<Pat
         if !bind_pat.is_simple_ident() {
             return None;
         }
-        let mut is_param = None;
-        let (refutability, has_type_ascription) = bind_pat
-            .syntax()
-            .ancestors()
-            .skip_while(|it| ast::Pat::can_cast(it.kind()))
-            .next()
-            .map_or((PatternRefutability::Irrefutable, false), |node| {
-                let refutability = match_ast! {
-                    match node {
-                        ast::LetStmt(let_) => return (PatternRefutability::Irrefutable, let_.ty().is_some()),
-                        ast::Param(param) => {
-                            let is_closure_param = param
-                                .syntax()
-                                .ancestors()
-                                .nth(2)
-                                .and_then(ast::ClosureExpr::cast)
-                                .is_some();
-                            is_param = Some(if is_closure_param {
-                                ParamKind::Closure
-                            } else {
-                                ParamKind::Function
-                            });
-                            return (PatternRefutability::Irrefutable, param.ty().is_some())
-                        },
-                        ast::MatchArm(__) => PatternRefutability::Refutable,
-                        ast::Condition(__) => PatternRefutability::Refutable,
-                        ast::ForExpr(__) => PatternRefutability::Irrefutable,
-                        _ => PatternRefutability::Irrefutable,
-                    }
-                };
-                (refutability, false)
-            });
-        Some(PatternContext { refutability, is_param, has_type_ascription })
+        Some(pattern_context_for(bind_pat.into()))
     }
 
     fn classify_name_ref(
         _sema: &Semantics<RootDatabase>,
         original_file: &SyntaxNode,
         name_ref: ast::NameRef,
-    ) -> Option<PathCompletionContext> {
+    ) -> Option<(PathCompletionContext, Option<PatternContext>)> {
         let parent = name_ref.syntax().parent()?;
         let segment = ast::PathSegment::cast(parent)?;
+        let path = segment.parent_path();
 
         let mut path_ctx = PathCompletionContext {
-            call_kind: None,
+            has_call_parens: false,
             is_trivial_path: false,
             qualifier: None,
+            parent: None,
             has_type_args: false,
             can_be_stmt: false,
             in_loop_body: false,
             use_tree_parent: false,
             kind: None,
         };
+        let mut pat_ctx = None;
         path_ctx.in_loop_body = is_in_loop_body(name_ref.syntax());
-        let path = segment.parent_path();
-
-        if let Some(p) = path.syntax().parent() {
-            path_ctx.call_kind = match_ast! {
-                match p {
-                    ast::PathExpr(it) => it.syntax().parent().and_then(ast::CallExpr::cast).map(|_| CallKind::Expr),
-                    ast::MacroCall(it) => it.excl_token().and(Some(CallKind::Mac)),
-                    ast::TupleStructPat(_it) => Some(CallKind::Pat),
-                    _ => None
-                }
-            };
-        }
 
-        if let Some(parent) = path.syntax().parent() {
-            path_ctx.kind = match_ast! {
-                match parent {
+        path_ctx.kind  = path.syntax().ancestors().find_map(|it| {
+            match_ast! {
+                match it {
                     ast::PathType(_it) => Some(PathKind::Type),
-                    ast::PathExpr(_it) => Some(PathKind::Expr),
+                    ast::PathExpr(it) => {
+                        path_ctx.has_call_parens = it.syntax().parent().map_or(false, |it| ast::CallExpr::can_cast(it.kind()));
+                        Some(PathKind::Expr)
+                    },
+                    ast::TupleStructPat(it) => {
+                        path_ctx.has_call_parens = true;
+                        pat_ctx = Some(pattern_context_for(it.into()));
+                        Some(PathKind::Pat)
+                    },
+                    ast::RecordPat(it) => {
+                        pat_ctx = Some(pattern_context_for(it.into()));
+                        Some(PathKind::Pat)
+                    },
+                    ast::PathPat(it) => {
+                        pat_ctx = Some(pattern_context_for(it.into()));
+                        Some(PathKind::Pat)
+                    },
+                    ast::MacroCall(it) => it.excl_token().and(Some(PathKind::Mac)),
                     ast::Meta(_it) => Some(PathKind::Attr),
+                    ast::Visibility(it) => Some(PathKind::Vis { has_in_token: it.in_token().is_some() }),
+                    ast::UseTree(_it) => Some(PathKind::Use),
                     _ => None,
                 }
-            };
-        }
+            }
+        });
         path_ctx.has_type_args = segment.generic_arg_list().is_some();
 
         if let Some((path, use_tree_parent)) = path_or_use_tree_qualifier(&path) {
@@ -806,12 +782,12 @@ fn classify_name_ref(
                     )
                 })
                 .map(|it| it.parent_path());
-            return Some(path_ctx);
+            return Some((path_ctx, pat_ctx));
         }
 
         if let Some(segment) = path.segment() {
             if segment.coloncolon_token().is_some() {
-                return Some(path_ctx);
+                return Some((path_ctx, pat_ctx));
             }
         }
 
@@ -835,10 +811,46 @@ fn classify_name_ref(
                 None
             })
             .unwrap_or(false);
-        Some(path_ctx)
+        Some((path_ctx, pat_ctx))
     }
 }
 
+fn pattern_context_for(pat: ast::Pat) -> PatternContext {
+    let mut is_param = None;
+    let (refutability, has_type_ascription) =
+    pat
+        .syntax()
+        .ancestors()
+        .skip_while(|it| ast::Pat::can_cast(it.kind()))
+        .next()
+        .map_or((PatternRefutability::Irrefutable, false), |node| {
+            let refutability = match_ast! {
+                match node {
+                    ast::LetStmt(let_) => return (PatternRefutability::Irrefutable, let_.ty().is_some()),
+                    ast::Param(param) => {
+                        let is_closure_param = param
+                            .syntax()
+                            .ancestors()
+                            .nth(2)
+                            .and_then(ast::ClosureExpr::cast)
+                            .is_some();
+                        is_param = Some(if is_closure_param {
+                            ParamKind::Closure
+                        } else {
+                            ParamKind::Function
+                        });
+                        return (PatternRefutability::Irrefutable, param.ty().is_some())
+                    },
+                    ast::MatchArm(__) => PatternRefutability::Refutable,
+                    ast::Condition(__) => PatternRefutability::Refutable,
+                    ast::ForExpr(__) => PatternRefutability::Irrefutable,
+                    _ => PatternRefutability::Irrefutable,
+                }
+            };
+            (refutability, false)
+        });
+    PatternContext { refutability, is_param, has_type_ascription }
+}
 fn find_node_with_range<N: AstNode>(syntax: &SyntaxNode, range: TextRange) -> Option<N> {
     syntax.covering_element(range).ancestors().find_map(N::cast)
 }
index 13c739325c806c7d1a9968cec6d8e2fa580b9465..b50f76b9116a737824a4c90f520cea9d8a7b70b8 100644 (file)
@@ -33,8 +33,6 @@ pub(crate) enum ImmediatePrevSibling {
 /// from which file the nodes are.
 #[derive(Clone, Debug, PartialEq, Eq)]
 pub(crate) enum ImmediateLocation {
-    Use,
-    UseTree,
     Rename,
     Impl,
     Trait,
@@ -47,10 +45,7 @@ pub(crate) enum ImmediateLocation {
     TypeBound,
     Variant,
     /// Fake file ast node
-    Attribute(ast::Attr),
-    /// Fake file ast node
     ModDeclaration(ast::Module),
-    Visibility(ast::Visibility),
     /// Original file ast node
     MethodCall {
         receiver: Option<ast::Expr>,
@@ -206,9 +201,6 @@ pub(crate) fn determine_location(
     let res = match_ast! {
         match parent {
             ast::IdentPat(_it) => ImmediateLocation::IdentPat,
-            ast::Use(_it) => ImmediateLocation::Use,
-            ast::UseTree(_it) => ImmediateLocation::UseTree,
-            ast::UseTreeList(_it) => ImmediateLocation::UseTree,
             ast::Rename(_it) => ImmediateLocation::Rename,
             ast::StmtList(_it) => ImmediateLocation::StmtList,
             ast::SourceFile(_it) => ImmediateLocation::ItemList,
@@ -242,7 +234,6 @@ pub(crate) fn determine_location(
                     return None;
                 }
             },
-            ast::Attr(it) => ImmediateLocation::Attribute(it),
             ast::FieldExpr(it) => {
                 let receiver = it
                     .expr()
@@ -268,8 +259,6 @@ pub(crate) fn determine_location(
                     .and_then(|r| find_node_with_range(original_file, r)),
                 has_parens: it.arg_list().map_or(false, |it| it.l_paren_token().is_some())
             },
-            ast::Visibility(it) => it.pub_token()
-                .and_then(|t| (t.text_range().end() < offset).then(|| ImmediateLocation::Visibility(it)))?,
             _ => return None,
         }
     };
@@ -417,14 +406,6 @@ fn test_impl_loc() {
         check_location(r"impl A { fn f$0 }", None);
     }
 
-    #[test]
-    fn test_use_loc() {
-        check_location(r"use f$0", ImmediateLocation::Use);
-        check_location(r"use f$0;", ImmediateLocation::Use);
-        check_location(r"use f::{f$0}", ImmediateLocation::UseTree);
-        check_location(r"use {f$0}", ImmediateLocation::UseTree);
-    }
-
     #[test]
     fn test_record_field_loc() {
         check_location(r"struct Foo { f$0 }", ImmediateLocation::RecordField);
index ea8222f76a7909ebdc8b1f82b89ea09101b18045..4d0405444542141ab45594251c18f432ec1b4c78 100644 (file)
@@ -4,7 +4,7 @@
 use itertools::Itertools;
 use syntax::ast::{self, HasName};
 
-use crate::{context::CallKind, item::Builder, patterns::ImmediateLocation, CompletionContext};
+use crate::{context::PathKind, item::Builder, patterns::ImmediateLocation, CompletionContext};
 
 #[derive(Debug)]
 pub(super) enum Params {
@@ -30,11 +30,11 @@ fn should_add_parens(&self, ctx: &CompletionContext) -> bool {
         if !ctx.config.add_call_parenthesis {
             return false;
         }
-        if ctx.in_use_tree() {
+        if let Some(PathKind::Use) = ctx.path_kind() {
             cov_mark::hit!(no_parens_in_use_item);
             return false;
         }
-        if matches!(ctx.path_call_kind(), Some(CallKind::Expr | CallKind::Pat))
+        if matches!(ctx.path_kind(), Some(PathKind::Expr | PathKind::Pat) if ctx.path_is_call())
             | matches!(
                 ctx.completion_location,
                 Some(ImmediateLocation::MethodCall { has_parens: true, .. })
index 22fb1f4825193cb23f0558cc81f268d29d136b35..6eb982a43eee28a9f11527ba034574523b443914 100644 (file)
@@ -9,7 +9,7 @@
 };
 
 use crate::{
-    context::CallKind,
+    context::PathKind,
     item::{CompletionItem, ImportEdit},
     render::RenderContext,
 };
@@ -67,9 +67,8 @@ fn render(self, import_to_add: Option<ImportEdit>) -> Option<CompletionItem> {
         }
 
         let needs_bang = self.macro_.is_fn_like()
-            && !(self.ctx.completion.in_use_tree()
-                || matches!(self.ctx.completion.path_call_kind(), Some(CallKind::Mac)));
-        let has_parens = self.ctx.completion.path_call_kind().is_some();
+            && !matches!(self.ctx.completion.path_kind(), Some(PathKind::Mac | PathKind::Use));
+        let has_parens = self.ctx.completion.path_is_call();
 
         match self.ctx.snippet_cap() {
             Some(cap) if needs_bang && !has_parens => {
@@ -92,8 +91,7 @@ fn render(self, import_to_add: Option<ImportEdit>) -> Option<CompletionItem> {
     }
 
     fn needs_bang(&self) -> bool {
-        !self.ctx.completion.in_use_tree()
-            && !matches!(self.ctx.completion.path_call_kind(), Some(CallKind::Mac))
+        !matches!(self.ctx.completion.path_kind(), Some(PathKind::Mac | PathKind::Use))
     }
 
     fn label(&self) -> SmolStr {
index ff46dda5e239e9a0ebd56a8069bb21f198067038..23e5da463c7a9eac4b4d4972ceee65674e2c9d44 100644 (file)
@@ -1000,6 +1000,19 @@ fn function() {
     );
 }
 
+#[test]
+fn flyimport_item_name() {
+    check(
+        r#"
+mod module {
+    pub struct Struct;
+}
+struct Str$0
+    "#,
+        expect![[r#""#]],
+    );
+}
+
 #[test]
 fn flyimport_rename() {
     check(