Repository: rust-analyzer/rust-analyzer
Pull request #9032 (2021-05-28 00:41:28)

Only complete modules in empty use-statements
bors r+
Part of #8518


Patch:
diff --git a/crates/ide_completion/src/completions/unqualified_path.rs b/crates/ide_completion/src/completions/unqualified_path.rs
index c901b358b2..ede07f605f 100644
--- a/crates/ide_completion/src/completions/unqualified_path.rs
+++ b/crates/ide_completion/src/completions/unqualified_path.rs
@@ -1,7 +1,6 @@
 //! Completion of names from the current scope, e.g. locals and imported items.
 
 use hir::ScopeDef;
-use syntax::AstNode;
 
 use crate::{CompletionContext, Completions};
 
@@ -24,6 +23,15 @@ pub(crate) fn complete_unqualified_path(acc: &mut Completions, ctx: &CompletionC
         return;
     }
 
+    if ctx.expects_use_tree() {
+        cov_mark::hit!(only_completes_modules_in_import);
+        ctx.scope.process_all_names(&mut |name, res| {
+            if let ScopeDef::ModuleDef(hir::ModuleDef::Module(_)) = res {
+                acc.add_resolution(ctx, name.to_string(), &res);
+            }
+        });
+        return;
+    }
     if let Some(hir::Adt::Enum(e)) =
         ctx.expected_type.as_ref().and_then(|ty| ty.strip_references().as_adt())
     {
@@ -37,14 +45,6 @@ pub(crate) fn complete_unqualified_path(acc: &mut Completions, ctx: &CompletionC
             cov_mark::hit!(skip_lifetime_completion);
             return;
         }
-        if ctx.use_item_syntax.is_some() {
-            if let (ScopeDef::Unknown, Some(name_ref)) = (&res, &ctx.name_ref_syntax) {
-                if name_ref.syntax().text() == name.to_string().as_str() {
-                    cov_mark::hit!(self_fulfilling_completion);
-                    return;
-                }
-            }
-        }
         acc.add_resolution(ctx, name.to_string(), &res);
     });
 }
@@ -68,15 +68,17 @@ mod tests {
     }
 
     #[test]
-    fn self_fulfilling_completion() {
-        cov_mark::check!(self_fulfilling_completion);
+    fn only_completes_modules_in_import() {
+        cov_mark::check!(only_completes_modules_in_import);
         check(
             r#"
-use foo$0
-use std::collections;
+use f$0
+
+struct Foo;
+mod foo {}
 "#,
             expect![[r#"
-                ?? collections
+                md foo
             "#]],
         );
     }
diff --git a/crates/ide_completion/src/context.rs b/crates/ide_completion/src/context.rs
index fbef544085..923e35dbb9 100644
--- a/crates/ide_completion/src/context.rs
+++ b/crates/ide_completion/src/context.rs
@@ -276,6 +276,10 @@ impl<'a> CompletionContext<'a> {
         )
     }
 
+    pub(crate) fn expects_use_tree(&self) -> bool {
+        matches!(self.completion_location, Some(ImmediateLocation::Use))
+    }
+
     pub(crate) fn expects_non_trait_assoc_item(&self) -> bool {
         matches!(self.completion_location, Some(ImmediateLocation::Impl))
     }
diff --git a/crates/ide_completion/src/patterns.rs b/crates/ide_completion/src/patterns.rs
index 19e42ba432..7bae7d12c3 100644
--- a/crates/ide_completion/src/patterns.rs
+++ b/crates/ide_completion/src/patterns.rs
@@ -14,6 +14,7 @@ use crate::test_utils::{check_pattern_is_applicable, check_pattern_is_not_applic
 /// Direct parent container of the cursor position
 #[derive(Copy, Clone, Debug, PartialEq, Eq)]
 pub(crate) enum ImmediateLocation {
+    Use,
     Impl,
     Trait,
     RecordField,
@@ -58,6 +59,7 @@ pub(crate) fn determine_location(tok: SyntaxToken) -> Option<ImmediateLocation>
     let res = match_ast! {
         match parent {
             ast::IdentPat(_it) => ImmediateLocation::IdentPat,
+            ast::Use(_it) => ImmediateLocation::Use,
             ast::BlockExpr(_it) => ImmediateLocation::BlockExpr,
             ast::SourceFile(_it) => ImmediateLocation::ItemList,
             ast::ItemList(_it) => ImmediateLocation::ItemList,
@@ -87,6 +89,11 @@ fn test_has_trait_parent() {
     check_location(r"trait A { f$0 }", ImmediateLocation::Trait);
 }
 
+#[test]
+fn test_has_use_parent() {
+    check_location(r"use f$0", ImmediateLocation::Use);
+}
+
 #[test]
 fn test_has_impl_parent() {
     check_location(r"impl A { f$0 }", ImmediateLocation::Impl);


Test patch:
diff --git a/crates/hir_def/src/nameres/tests/incremental.rs b/crates/hir_def/src/nameres/tests/incremental.rs
index 7bf152e26c..d884a6eb49 100644
--- a/crates/hir_def/src/nameres/tests/incremental.rs
+++ b/crates/hir_def/src/nameres/tests/incremental.rs
@@ -1,8 +1,6 @@
 use std::sync::Arc;
 
-use base_db::{salsa::SweepStrategy, SourceDatabaseExt};
-
-use crate::{AdtId, ModuleDefId};
+use base_db::SourceDatabaseExt;
 
 use super::*;
 
@@ -165,73 +163,3 @@ m!(Z);
         assert_eq!(n_reparsed_macros, 0);
     }
 }
-
-#[test]
-fn item_tree_prevents_reparsing() {
-    // The `ItemTree` is used by both name resolution and the various queries in `adt.rs` and
-    // `data.rs`. After computing the `ItemTree` and deleting the parse tree, we should be able to
-    // run those other queries without triggering a reparse.
-
-    let (db, pos) = TestDB::with_position(
-        r#"
-pub struct S;
-pub union U {}
-pub enum E {
-    Variant,
-}
-pub fn f(_: S) { $0 }
-pub trait Tr {}
-impl Tr for () {}
-pub const C: u8 = 0;
-pub static ST: u8 = 0;
-pub type Ty = ();
-"#,
-    );
-    let krate = db.test_crate();
-    {
-        let events = db.log_executed(|| {
-            db.file_item_tree(pos.file_id.into());
-        });
-        let n_calculated_item_trees = events.iter().filter(|it| it.contains("item_tree")).count();
-        assert_eq!(n_calculated_item_trees, 1);
-        let n_parsed_files = events.iter().filter(|it| it.contains("parse(")).count();
-        assert_eq!(n_parsed_files, 1);
-    }
-
-    // Delete the parse tree.
-    let sweep = SweepStrategy::default().discard_values().sweep_all_revisions();
-    base_db::ParseQuery.in_db(&db).sweep(sweep);
-
-    {
-        let events = db.log_executed(|| {
-            let crate_def_map = db.crate_def_map(krate);
-            let (_, module_data) = crate_def_map.modules.iter().last().unwrap();
-            assert_eq!(module_data.scope.resolutions().count(), 8);
-            assert_eq!(module_data.scope.impls().count(), 1);
-
-            for imp in module_data.scope.impls() {
-                db.impl_data(imp);
-            }
-
-            for (_, res) in module_data.scope.resolutions() {
-                match res.values.or(res.types).unwrap().0 {
-                    ModuleDefId::FunctionId(f) => drop(db.function_data(f)),
-                    ModuleDefId::AdtId(adt) => match adt {
-                        AdtId::StructId(it) => drop(db.struct_data(it)),
-                        AdtId::UnionId(it) => drop(db.union_data(it)),
-                        AdtId::EnumId(it) => drop(db.enum_data(it)),
-                    },
-                    ModuleDefId::ConstId(it) => drop(db.const_data(it)),
-                    ModuleDefId::StaticId(it) => drop(db.static_data(it)),
-                    ModuleDefId::TraitId(it) => drop(db.trait_data(it)),
-                    ModuleDefId::TypeAliasId(it) => drop(db.type_alias_data(it)),
-                    ModuleDefId::EnumVariantId(_)
-                    | ModuleDefId::ModuleId(_)
-                    | ModuleDefId::BuiltinType(_) => unreachable!(),
-                }
-            }
-        });
-        let n_reparsed_files = events.iter().filter(|it| it.contains("parse(")).count();
-        assert_eq!(n_reparsed_files, 0);
-    }
-}
diff --git a/crates/hir_ty/src/tests.rs b/crates/hir_ty/src/tests.rs
index 9d726b0248..cc819373c6 100644
--- a/crates/hir_ty/src/tests.rs
+++ b/crates/hir_ty/src/tests.rs
@@ -7,7 +7,6 @@ mod traits;
 mod method_resolution;
 mod macros;
 mod display_source_code;
-mod incremental;
 
 use std::{env, sync::Arc};
 
@@ -318,6 +317,50 @@ fn ellipsize(mut text: String, max_len: usize) -> String {
     text
 }
 
+#[test]
+fn typing_whitespace_inside_a_function_should_not_invalidate_types() {
+    let (mut db, pos) = TestDB::with_position(
+        "
+        //- /lib.rs
+        fn foo() -> i32 {
+            $01 + 1
+        }
+    ",
+    );
+    {
+        let events = db.log_executed(|| {
+            let module = db.module_for_file(pos.file_id);
+            let crate_def_map = module.def_map(&db);
+            visit_module(&db, &crate_def_map, module.local_id, &mut |def| {
+                db.infer(def);
+            });
+        });
+        assert!(format!("{:?}", events).contains("infer"))
+    }
+
+    let new_text = "
+        fn foo() -> i32 {
+            1
+            +
+            1
+        }
+    "
+    .to_string();
+
+    db.set_file_text(pos.file_id, Arc::new(new_text));
+
+    {
+        let events = db.log_executed(|| {
+            let module = db.module_for_file(pos.file_id);
+            let crate_def_map = module.def_map(&db);
+            visit_module(&db, &crate_def_map, module.local_id, &mut |def| {
+                db.infer(def);
+            });
+        });
+        assert!(!format!("{:?}", events).contains("infer"), "{:#?}", events)
+    }
+}
+
 fn check_infer(ra_fixture: &str, expect: Expect) {
     let mut actual = infer(ra_fixture);
     actual.push('\n');
diff --git a/crates/hir_ty/src/tests/incremental.rs b/crates/hir_ty/src/tests/incremental.rs
deleted file mode 100644
index 3e08e83e89..0000000000
--- a/crates/hir_ty/src/tests/incremental.rs
+++ /dev/null
@@ -1,51 +0,0 @@
-use std::sync::Arc;
-
-use base_db::{fixture::WithFixture, SourceDatabaseExt};
-
-use crate::{db::HirDatabase, test_db::TestDB};
-
-use super::visit_module;
-
-#[test]
-fn typing_whitespace_inside_a_function_should_not_invalidate_types() {
-    let (mut db, pos) = TestDB::with_position(
-        "
-        //- /lib.rs
-        fn foo() -> i32 {
-            $01 + 1
-        }
-    ",
-    );
-    {
-        let events = db.log_executed(|| {
-            let module = db.module_for_file(pos.file_id);
-            let crate_def_map = module.def_map(&db);
-            visit_module(&db, &crate_def_map, module.local_id, &mut |def| {
-                db.infer(def);
-            });
-        });
-        assert!(format!("{:?}", events).contains("infer"))
-    }
-
-    let new_text = "
-        fn foo() -> i32 {
-            1
-            +
-            1
-        }
-    "
-    .to_string();
-
-    db.set_file_text(pos.file_id, Arc::new(new_text));
-
-    {
-        let events = db.log_executed(|| {
-            let module = db.module_for_file(pos.file_id);
-            let crate_def_map = module.def_map(&db);
-            visit_module(&db, &crate_def_map, module.local_id, &mut |def| {
-                db.infer(def);
-            });
-        });
-        assert!(!format!("{:?}", events).contains("infer"), "{:#?}", events)
-    }
-}