Repository: rust-analyzer/rust-analyzer
Pull request #9029 (2021-05-27 22:46:50)

minor: test that `ItemTree` makes `hir_def` queries syntax-independent
bors r+


Patch:
diff --git a/crates/ide_completion/src/completions/macro_in_item_position.rs b/crates/ide_completion/src/completions/macro_in_item_position.rs
index ec57aee30e..c5e3775000 100644
--- a/crates/ide_completion/src/completions/macro_in_item_position.rs
+++ b/crates/ide_completion/src/completions/macro_in_item_position.rs
@@ -2,7 +2,6 @@
 
 use crate::{CompletionContext, Completions};
 
-// Ideally this should be removed and moved into `(un)qualified_path` respectively
 pub(crate) fn complete_macro_in_item_position(acc: &mut Completions, ctx: &CompletionContext) {
     // Show only macros in top level.
     if !ctx.is_new_item {
@@ -13,10 +12,6 @@ pub(crate) fn complete_macro_in_item_position(acc: &mut Completions, ctx: &Compl
         if let hir::ScopeDef::MacroDef(mac) = res {
             acc.add_macro(ctx, Some(name.to_string()), mac);
         }
-        // FIXME: This should be done in qualified_path/unqualified_path instead?
-        if let hir::ScopeDef::ModuleDef(hir::ModuleDef::Module(_)) = res {
-            acc.add_resolution(ctx, name.to_string(), &res);
-        }
     })
 }
 
diff --git a/crates/ide_completion/src/completions/qualified_path.rs b/crates/ide_completion/src/completions/qualified_path.rs
index 7a0e1ead3f..c16bb215f7 100644
--- a/crates/ide_completion/src/completions/qualified_path.rs
+++ b/crates/ide_completion/src/completions/qualified_path.rs
@@ -7,7 +7,7 @@ use syntax::AstNode;
 use crate::{CompletionContext, Completions};
 
 pub(crate) fn complete_qualified_path(acc: &mut Completions, ctx: &CompletionContext) {
-    if ctx.is_path_disallowed() || ctx.expects_item() {
+    if ctx.is_path_disallowed() {
         return;
     }
     let path = match &ctx.path_qual {
@@ -20,16 +20,13 @@ pub(crate) fn complete_qualified_path(acc: &mut Completions, ctx: &CompletionCon
         None => return,
     };
     let context_module = ctx.scope.module();
-    if ctx.expects_assoc_item() {
+    if ctx.expects_item() || ctx.expects_assoc_item() {
         if let PathResolution::Def(hir::ModuleDef::Module(module)) = resolution {
             let module_scope = module.scope(ctx.db, context_module);
             for (name, def) in module_scope {
                 if let ScopeDef::MacroDef(macro_def) = def {
                     acc.add_macro(ctx, Some(name.to_string()), macro_def);
                 }
-                if let ScopeDef::ModuleDef(hir::ModuleDef::Module(_)) = def {
-                    acc.add_resolution(ctx, name.to_string(), &def);
-                }
             }
         }
         return;
@@ -617,43 +614,24 @@ fn main() { let _ = crate::$0 }
     }
 
     #[test]
-    fn completes_in_assoc_item_list() {
+    fn completes_qualified_macros_in_impl() {
         check(
             r#"
 #[macro_export]
 macro_rules! foo { () => {} }
-mod bar {}
 
 struct MyStruct {}
+
 impl MyStruct {
     crate::$0
 }
 "#,
             expect![[r##"
-                md bar
                 ma foo! #[macro_export] macro_rules! foo
             "##]],
         );
     }
 
-    #[test]
-    #[ignore] // FIXME doesn't complete anything atm
-    fn completes_in_item_list() {
-        check(
-            r#"
-struct MyStruct {}
-macro_rules! foo {}
-mod bar {}
-
-crate::$0
-"#,
-            expect![[r#"
-                md bar
-                ma foo! macro_rules! foo
-            "#]],
-        )
-    }
-
     #[test]
     fn test_super_super_completion() {
         check(
diff --git a/crates/ide_completion/src/completions/unqualified_path.rs b/crates/ide_completion/src/completions/unqualified_path.rs
index c901b358b2..cbac882407 100644
--- a/crates/ide_completion/src/completions/unqualified_path.rs
+++ b/crates/ide_completion/src/completions/unqualified_path.rs
@@ -9,17 +9,14 @@ pub(crate) fn complete_unqualified_path(acc: &mut Completions, ctx: &CompletionC
     if !ctx.is_trivial_path {
         return;
     }
-    if ctx.is_path_disallowed() || ctx.expects_item() {
+    if ctx.is_path_disallowed() {
         return;
     }
-    if ctx.expects_assoc_item() {
+    if ctx.expects_item() || ctx.expects_assoc_item() {
         ctx.scope.process_all_names(&mut |name, def| {
             if let ScopeDef::MacroDef(macro_def) = def {
                 acc.add_macro(ctx, Some(name.to_string()), macro_def);
             }
-            if let ScopeDef::ModuleDef(hir::ModuleDef::Module(_)) = def {
-                acc.add_resolution(ctx, name.to_string(), &def);
-            }
         });
         return;
     }
@@ -675,39 +672,19 @@ impl My$0
     }
 
     #[test]
-    fn completes_in_assoc_item_list() {
+    fn only_completes_macros_in_assoc_item_list() {
         check(
             r#"
+struct MyStruct {}
 macro_rules! foo {}
-mod bar {}
 
-struct MyStruct {}
 impl MyStruct {
     $0
 }
 "#,
             expect![[r#"
-                md bar
                 ma foo! macro_rules! foo
             "#]],
         )
     }
-
-    // FIXME: The completions here currently come from `macro_in_item_position`, but they shouldn't
-    #[test]
-    fn completes_in_item_list() {
-        check(
-            r#"
-struct MyStruct {}
-macro_rules! foo {}
-mod bar {}
-
-$0
-"#,
-            expect![[r#"
-                md bar
-                ma foo!(…) macro_rules! foo
-            "#]],
-        )
-    }
 }
diff --git a/crates/ide_completion/src/patterns.rs b/crates/ide_completion/src/patterns.rs
index 19e42ba432..ed289d5611 100644
--- a/crates/ide_completion/src/patterns.rs
+++ b/crates/ide_completion/src/patterns.rs
@@ -62,6 +62,7 @@ pub(crate) fn determine_location(tok: SyntaxToken) -> Option<ImmediateLocation>
             ast::SourceFile(_it) => ImmediateLocation::ItemList,
             ast::ItemList(_it) => ImmediateLocation::ItemList,
             ast::RefExpr(_it) => ImmediateLocation::RefExpr,
+            ast::RefPat(_it) => ImmediateLocation::RefExpr,
             ast::RecordField(_it) => ImmediateLocation::RecordField,
             ast::AssocItemList(it) => match it.syntax().parent().map(|it| it.kind()) {
                 Some(IMPL) => 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 d884a6eb49..7bf152e26c 100644
--- a/crates/hir_def/src/nameres/tests/incremental.rs
+++ b/crates/hir_def/src/nameres/tests/incremental.rs
@@ -1,6 +1,8 @@
 use std::sync::Arc;
 
-use base_db::SourceDatabaseExt;
+use base_db::{salsa::SweepStrategy, SourceDatabaseExt};
+
+use crate::{AdtId, ModuleDefId};
 
 use super::*;
 
@@ -163,3 +165,73 @@ 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 cc819373c6..9d726b0248 100644
--- a/crates/hir_ty/src/tests.rs
+++ b/crates/hir_ty/src/tests.rs
@@ -7,6 +7,7 @@ mod traits;
 mod method_resolution;
 mod macros;
 mod display_source_code;
+mod incremental;
 
 use std::{env, sync::Arc};
 
@@ -317,50 +318,6 @@ 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
new file mode 100644
index 0000000000..3e08e83e89
--- /dev/null
+++ b/crates/hir_ty/src/tests/incremental.rs
@@ -0,0 +1,51 @@
+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)
+    }
+}