Repository: rust-analyzer/rust-analyzer
Pull request #2378 (2019-11-24 09:06:40)

Fix panic in batch analysis


Patch:
diff --git a/crates/ra_assists/src/assists/apply_demorgan.rs b/crates/ra_assists/src/assists/apply_demorgan.rs
index 7c57c0560d..068da1774d 100644
--- a/crates/ra_assists/src/assists/apply_demorgan.rs
+++ b/crates/ra_assists/src/assists/apply_demorgan.rs
@@ -1,6 +1,6 @@
-use super::invert_if::invert_boolean_expression;
 use hir::db::HirDatabase;
 use ra_syntax::ast::{self, AstNode};
+use ra_syntax::SyntaxNode;
 
 use crate::{Assist, AssistCtx, AssistId};
 
@@ -32,18 +32,18 @@ pub(crate) fn apply_demorgan(ctx: AssistCtx<impl HirDatabase>) -> Option<Assist>
     if !cursor_in_range {
         return None;
     }
-    let lhs = expr.lhs()?;
-    let lhs_range = lhs.syntax().text_range();
-    let rhs = expr.rhs()?;
-    let rhs_range = rhs.syntax().text_range();
-    let not_lhs = invert_boolean_expression(&lhs)?;
-    let not_rhs = invert_boolean_expression(&rhs)?;
+    let lhs = expr.lhs()?.syntax().clone();
+    let lhs_range = lhs.text_range();
+    let rhs = expr.rhs()?.syntax().clone();
+    let rhs_range = rhs.text_range();
+    let not_lhs = undo_negation(lhs)?;
+    let not_rhs = undo_negation(rhs)?;
 
     ctx.add_assist(AssistId("apply_demorgan"), "apply demorgan's law", |edit| {
         edit.target(op_range);
         edit.replace(op_range, opposite_op);
-        edit.replace(lhs_range, format!("!({}", not_lhs.syntax().text()));
-        edit.replace(rhs_range, format!("{})", not_rhs.syntax().text()));
+        edit.replace(lhs_range, format!("!({}", not_lhs));
+        edit.replace(rhs_range, format!("{})", not_rhs));
     })
 }
 
@@ -56,6 +56,28 @@ fn opposite_logic_op(kind: ast::BinOp) -> Option<&'static str> {
     }
 }
 
+// This function tries to undo unary negation, or inequality
+fn undo_negation(node: SyntaxNode) -> Option<String> {
+    match ast::Expr::cast(node)? {
+        ast::Expr::BinExpr(bin) => match bin.op_kind()? {
+            ast::BinOp::NegatedEqualityTest => {
+                let lhs = bin.lhs()?.syntax().text();
+                let rhs = bin.rhs()?.syntax().text();
+                Some(format!("{} == {}", lhs, rhs))
+            }
+            _ => None,
+        },
+        ast::Expr::PrefixExpr(pe) => match pe.op_kind()? {
+            ast::PrefixOp::Not => {
+                let child = pe.expr()?.syntax().text();
+                Some(String::from(child))
+            }
+            _ => None,
+        },
+        _ => None,
+    }
+}
+
 #[cfg(test)]
 mod tests {
     use super::*;
diff --git a/crates/ra_assists/src/assists/invert_if.rs b/crates/ra_assists/src/assists/invert_if.rs
deleted file mode 100644
index bababa3e25..0000000000
--- a/crates/ra_assists/src/assists/invert_if.rs
+++ /dev/null
@@ -1,102 +0,0 @@
-use hir::db::HirDatabase;
-use ra_syntax::ast::{self, AstNode};
-use ra_syntax::T;
-
-use crate::{Assist, AssistCtx, AssistId};
-
-// Assist: invert_if
-//
-// Apply invert_if
-// This transforms if expressions of the form `if !x {A} else {B}` into `if x {B} else {A}`
-// This also works with `!=`. This assist can only be applied with the cursor
-// on `if`.
-//
-// ```
-// fn main() {
-//     if<|> !y { A } else { B }
-// }
-// ```
-// ->
-// ```
-// fn main() {
-//     if y { B } else { A }
-// }
-// ```
-
-pub(crate) fn invert_if(ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> {
-    let if_keyword = ctx.find_token_at_offset(T![if])?;
-    let expr = ast::IfExpr::cast(if_keyword.parent())?;
-    let if_range = if_keyword.text_range();
-    let cursor_in_range = ctx.frange.range.is_subrange(&if_range);
-    if !cursor_in_range {
-        return None;
-    }
-
-    let cond = expr.condition()?.expr()?;
-    let then_node = expr.then_branch()?.syntax().clone();
-
-    if let ast::ElseBranch::Block(else_block) = expr.else_branch()? {
-        let flip_cond = invert_boolean_expression(&cond)?;
-        let cond_range = cond.syntax().text_range();
-        let else_node = else_block.syntax();
-        let else_range = else_node.text_range();
-        let then_range = then_node.text_range();
-        return ctx.add_assist(AssistId("invert_if"), "invert if branches", |edit| {
-            edit.target(if_range);
-            edit.replace(cond_range, flip_cond.syntax().text());
-            edit.replace(else_range, then_node.text());
-            edit.replace(then_range, else_node.text());
-        });
-    }
-
-    None
-}
-
-pub(crate) fn invert_boolean_expression(expr: &ast::Expr) -> Option<ast::Expr> {
-    match expr {
-        ast::Expr::BinExpr(bin) => match bin.op_kind()? {
-            ast::BinOp::NegatedEqualityTest => bin.replace_op(T![==]).map(|it| it.into()),
-            _ => None,
-        },
-        ast::Expr::PrefixExpr(pe) => match pe.op_kind()? {
-            ast::PrefixOp::Not => pe.expr(),
-            _ => None,
-        },
-        _ => None,
-    }
-}
-
-#[cfg(test)]
-mod tests {
-    use super::*;
-
-    use crate::helpers::{check_assist, check_assist_not_applicable};
-
-    #[test]
-    fn invert_if_remove_inequality() {
-        check_assist(
-            invert_if,
-            "fn f() { i<|>f x != 3 { 1 } else { 3 + 2 } }",
-            "fn f() { i<|>f x == 3 { 3 + 2 } else { 1 } }",
-        )
-    }
-
-    #[test]
-    fn invert_if_remove_not() {
-        check_assist(
-            invert_if,
-            "fn f() { <|>if !cond { 3 * 2 } else { 1 } }",
-            "fn f() { <|>if cond { 1 } else { 3 * 2 } }",
-        )
-    }
-
-    #[test]
-    fn invert_if_doesnt_apply_with_cursor_not_on_if() {
-        check_assist_not_applicable(invert_if, "fn f() { if !<|>cond { 3 * 2 } else { 1 } }")
-    }
-
-    #[test]
-    fn invert_if_doesnt_apply_without_negated() {
-        check_assist_not_applicable(invert_if, "fn f() { i<|>f cond { 3 * 2 } else { 1 } }")
-    }
-}
diff --git a/crates/ra_assists/src/lib.rs b/crates/ra_assists/src/lib.rs
index a372bd8b9d..f2f0dacbf7 100644
--- a/crates/ra_assists/src/lib.rs
+++ b/crates/ra_assists/src/lib.rs
@@ -97,7 +97,6 @@ mod assists {
     mod add_impl;
     mod add_new;
     mod apply_demorgan;
-    mod invert_if;
     mod flip_comma;
     mod flip_binexpr;
     mod flip_trait_bound;
@@ -123,7 +122,6 @@ mod assists {
             add_impl::add_impl,
             add_new::add_new,
             apply_demorgan::apply_demorgan,
-            invert_if::invert_if,
             change_visibility::change_visibility,
             fill_match_arms::fill_match_arms,
             merge_match_arms::merge_match_arms,
diff --git a/crates/ra_batch/src/lib.rs b/crates/ra_batch/src/lib.rs
index df49eb13d1..cb389eb261 100644
--- a/crates/ra_batch/src/lib.rs
+++ b/crates/ra_batch/src/lib.rs
@@ -117,9 +117,12 @@ pub fn load(
                         done = true;
                     }
                 }
-                VfsChange::AddFile { .. }
-                | VfsChange::RemoveFile { .. }
-                | VfsChange::ChangeFile { .. } => {
+                VfsChange::AddFile { root, file, path, text } => {
+                    let source_root_id = vfs_root_to_id(root);
+                    let file_id = vfs_file_to_id(file);
+                    analysis_change.add_file(source_root_id, file_id, path, text);
+                }
+                VfsChange::RemoveFile { .. } | VfsChange::ChangeFile { .. } => {
                     // We just need the first scan, so just ignore these
                 }
             }
diff --git a/crates/ra_hir_expand/src/builtin_macro.rs b/crates/ra_hir_expand/src/builtin_macro.rs
index c0e0436c0a..4da56529de 100644
--- a/crates/ra_hir_expand/src/builtin_macro.rs
+++ b/crates/ra_hir_expand/src/builtin_macro.rs
@@ -8,47 +8,47 @@ use crate::{
 
 use crate::quote;
 
-macro_rules! register_builtin {
-    ( $(($name:ident, $kind: ident) => $expand:ident),* ) => {
-        #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
-        pub enum BuiltinFnLikeExpander {
-            $($kind),*
-        }
-
-        impl BuiltinFnLikeExpander {
-            pub fn expand(
-                &self,
-                db: &dyn AstDatabase,
-                id: MacroCallId,
-                tt: &tt::Subtree,
-            ) -> Result<tt::Subtree, mbe::ExpandError> {
-                let expander = match *self {
-                    $( BuiltinFnLikeExpander::$kind => $expand, )*
-                };
-                expander(db, id, tt)
-            }
-        }
-
-        pub fn find_builtin_macro(
-            ident: &name::Name,
-            krate: CrateId,
-            ast_id: AstId<ast::MacroCall>,
-        ) -> Option<MacroDefId> {
-            let kind = match ident {
-                 $( id if id == &name::$name => BuiltinFnLikeExpander::$kind, )*
-                 _ => return None,
-            };
+#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
+pub enum BuiltinExpander {
+    Column,
+    File,
+    Line,
+    Stringify,
+}
 
-            Some(MacroDefId { krate, ast_id, kind: MacroDefKind::BuiltIn(kind) })
+impl BuiltinExpander {
+    pub fn expand(
+        &self,
+        db: &dyn AstDatabase,
+        id: MacroCallId,
+        tt: &tt::Subtree,
+    ) -> Result<tt::Subtree, mbe::ExpandError> {
+        match self {
+            BuiltinExpander::Column => column_expand(db, id, tt),
+            BuiltinExpander::File => file_expand(db, id, tt),
+            BuiltinExpander::Line => line_expand(db, id, tt),
+            BuiltinExpander::Stringify => stringify_expand(db, id, tt),
         }
-    };
+    }
 }
 
-register_builtin! {
-    (COLUMN_MACRO, Column) => column_expand,
-    (FILE_MACRO, File) => file_expand,
-    (LINE_MACRO, Line) => line_expand,
-    (STRINGIFY_MACRO, Stringify) => stringify_expand
+pub fn find_builtin_macro(
+    ident: &name::Name,
+    krate: CrateId,
+    ast_id: AstId<ast::MacroCall>,
+) -> Option<MacroDefId> {
+    // FIXME: Better registering method
+    if ident == &name::COLUMN_MACRO {
+        Some(MacroDefId { krate, ast_id, kind: MacroDefKind::BuiltIn(BuiltinExpander::Column) })
+    } else if ident == &name::FILE_MACRO {
+        Some(MacroDefId { krate, ast_id, kind: MacroDefKind::BuiltIn(BuiltinExpander::File) })
+    } else if ident == &name::LINE_MACRO {
+        Some(MacroDefId { krate, ast_id, kind: MacroDefKind::BuiltIn(BuiltinExpander::Line) })
+    } else if ident == &name::STRINGIFY_MACRO {
+        Some(MacroDefId { krate, ast_id, kind: MacroDefKind::BuiltIn(BuiltinExpander::Stringify) })
+    } else {
+        None
+    }
 }
 
 fn to_line_number(db: &dyn AstDatabase, file: HirFileId, pos: TextUnit) -> usize {
@@ -171,92 +171,3 @@ fn file_expand(
 
     Ok(expanded)
 }
-
-#[cfg(test)]
-mod tests {
-    use super::*;
-    use crate::{test_db::TestDB, MacroCallLoc};
-    use ra_db::{fixture::WithFixture, SourceDatabase};
-
-    fn expand_builtin_macro(s: &str, expander: BuiltinFnLikeExpander) -> String {
-        let (db, file_id) = TestDB::with_single_file(&s);
-        let parsed = db.parse(file_id);
-        let macro_calls: Vec<_> =
-            parsed.syntax_node().descendants().filter_map(|it| ast::MacroCall::cast(it)).collect();
-
-        let ast_id_map = db.ast_id_map(file_id.into());
-
-        // the first one should be a macro_rules
-        let def = MacroDefId {
-            krate: CrateId(0),
-            ast_id: AstId::new(file_id.into(), ast_id_map.ast_id(&macro_calls[0])),
-            kind: MacroDefKind::BuiltIn(expander),
-        };
-
-        let loc = MacroCallLoc {
-            def,
-            ast_id: AstId::new(file_id.into(), ast_id_map.ast_id(&macro_calls[1])),
-        };
-
-        let id = db.intern_macro(loc);
-        let parsed = db.parse_or_expand(id.as_file(MacroFileKind::Expr)).unwrap();
-
-        parsed.text().to_string()
-    }
-
-    #[test]
-    fn test_column_expand() {
-        let expanded = expand_builtin_macro(
-            r#"
-        #[rustc_builtin_macro]
-        macro_rules! column {() => {}}
-        column!()
-"#,
-            BuiltinFnLikeExpander::Column,
-        );
-
-        assert_eq!(expanded, "9");
-    }
-
-    #[test]
-    fn test_line_expand() {
-        let expanded = expand_builtin_macro(
-            r#"
-        #[rustc_builtin_macro]
-        macro_rules! line {() => {}}
-        line!()
-"#,
-            BuiltinFnLikeExpander::Line,
-        );
-
-        assert_eq!(expanded, "4");
-    }
-
-    #[test]
-    fn test_stringify_expand() {
-        let expanded = expand_builtin_macro(
-            r#"
-        #[rustc_builtin_macro]
-        macro_rules! stringify {() => {}}
-        stringify!(a b c)
-"#,
-            BuiltinFnLikeExpander::Stringify,
-        );
-
-        assert_eq!(expanded, "\"a b c\"");
-    }
-
-    #[test]
-    fn test_file_expand() {
-        let expanded = expand_builtin_macro(
-            r#"
-        #[rustc_builtin_macro]
-        macro_rules! file {() => {}}
-        file!()
-"#,
-            BuiltinFnLikeExpander::File,
-        );
-
-        assert_eq!(expanded, "\"\"");
-    }
-}
diff --git a/crates/ra_hir_expand/src/db.rs b/crates/ra_hir_expand/src/db.rs
index 8e46fa177d..e1d93a8ef5 100644
--- a/crates/ra_hir_expand/src/db.rs
+++ b/crates/ra_hir_expand/src/db.rs
@@ -9,14 +9,14 @@ use ra_prof::profile;
 use ra_syntax::{AstNode, Parse, SyntaxNode};
 
 use crate::{
-    ast_id_map::AstIdMap, BuiltinFnLikeExpander, HirFileId, HirFileIdRepr, MacroCallId,
-    MacroCallLoc, MacroDefId, MacroDefKind, MacroFile, MacroFileKind,
+    ast_id_map::AstIdMap, BuiltinExpander, HirFileId, HirFileIdRepr, MacroCallId, MacroCallLoc,
+    MacroDefId, MacroDefKind, MacroFile, MacroFileKind,
 };
 
 #[derive(Debug, Clone, Eq, PartialEq)]
 pub enum TokenExpander {
     MacroRules(mbe::MacroRules),
-    Builtin(BuiltinFnLikeExpander),
+    Builtin(BuiltinExpander),
 }
 
 impl TokenExpander {
diff --git a/crates/ra_hir_expand/src/lib.rs b/crates/ra_hir_expand/src/lib.rs
index 4f3ccf1d0d..126d12fbbe 100644
--- a/crates/ra_hir_expand/src/lib.rs
+++ b/crates/ra_hir_expand/src/lib.rs
@@ -24,10 +24,7 @@ use ra_syntax::{
 };
 
 use crate::ast_id_map::FileAstId;
-use crate::builtin_macro::BuiltinFnLikeExpander;
-
-#[cfg(test)]
-mod test_db;
+use crate::builtin_macro::BuiltinExpander;
 
 /// Input to the analyzer is a set of files, where each file is identified by
 /// `FileId` and contains source code. However, another source of source code in
@@ -138,7 +135,7 @@ pub struct MacroDefId {
 #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
 pub enum MacroDefKind {
     Declarative,
-    BuiltIn(BuiltinFnLikeExpander),
+    BuiltIn(BuiltinExpander),
 }
 
 #[derive(Debug, Clone, PartialEq, Eq, Hash)]
diff --git a/crates/ra_ide_api/src/expand_macro.rs b/crates/ra_ide_api/src/expand_macro.rs
index 0b540b8cde..673301b10a 100644
--- a/crates/ra_ide_api/src/expand_macro.rs
+++ b/crates/ra_ide_api/src/expand_macro.rs
@@ -47,15 +47,15 @@ fn expand_macro_recur(
 
     for child in children.into_iter() {
         let node = hir::Source::new(macro_file_id, &child);
-        if let Some(new_node) = expand_macro_recur(db, source, node) {
-            // Replace the whole node if it is root
-            // `replace_descendants` will not replace the parent node
-            // but `SyntaxNode::descendants include itself
-            if expanded == *child.syntax() {
-                expanded = new_node;
-            } else {
-                replaces.insert(child.syntax().clone().into(), new_node.into());
-            }
+        let new_node = expand_macro_recur(db, source, node)?;
+
+        // Replace the whole node if it is root
+        // `replace_descendants` will not replace the parent node
+        // but `SyntaxNode::descendants include itself
+        if expanded == *child.syntax() {
+            expanded = new_node;
+        } else {
+            replaces.insert(child.syntax().clone().into(), new_node.into());
         }
     }
 
@@ -247,26 +247,4 @@ fn some_thing() -> u32 {
         assert_eq!(res.name, "match_ast");
         assert_snapshot!(res.expansion, @r###"{}"###);
     }
-
-    #[test]
-    fn macro_expand_inner_macro_fail_to_expand() {
-        let res = check_expand_macro(
-            r#"
-        //- /lib.rs
-        macro_rules! bar {
-            (BAD) => {};
-        }
-        macro_rules! foo {
-            () => {bar!()};
-        }        
-
-        fn main() {        
-            let res = fo<|>o!();
-        }
-        "#,
-        );
-
-        assert_eq!(res.name, "foo");
-        assert_snapshot!(res.expansion, @r###"bar!()"###);
-    }
 }
diff --git a/crates/ra_syntax/src/ast/edit.rs b/crates/ra_syntax/src/ast/edit.rs
index 95bf9db147..6f005a2d88 100644
--- a/crates/ra_syntax/src/ast/edit.rs
+++ b/crates/ra_syntax/src/ast/edit.rs
@@ -13,21 +13,11 @@ use crate::{
         make::{self, tokens},
         AstNode, TypeBoundsOwner,
     },
-    AstToken, Direction, InsertPosition, SmolStr, SyntaxElement, SyntaxKind,
+    AstToken, Direction, InsertPosition, SmolStr, SyntaxElement,
     SyntaxKind::{ATTR, COMMENT, WHITESPACE},
     SyntaxNode, SyntaxToken, T,
 };
 
-impl ast::BinExpr {
-    #[must_use]
-    pub fn replace_op(&self, op: SyntaxKind) -> Option<ast::BinExpr> {
-        let op_node: SyntaxElement = self.op_details()?.0.into();
-        let to_insert: Option<SyntaxElement> = Some(tokens::op(op).into());
-        let replace_range = RangeInclusive::new(op_node.clone(), op_node);
-        Some(replace_children(self, replace_range, to_insert.into_iter()))
-    }
-}
-
 impl ast::FnDef {
     #[must_use]
     pub fn with_body(&self, body: ast::Block) -> ast::FnDef {
diff --git a/crates/ra_syntax/src/ast/expr_extensions.rs b/crates/ra_syntax/src/ast/expr_extensions.rs
index 2fd0398378..7c53aa9344 100644
--- a/crates/ra_syntax/src/ast/expr_extensions.rs
+++ b/crates/ra_syntax/src/ast/expr_extensions.rs
@@ -127,7 +127,7 @@ pub enum BinOp {
 }
 
 impl ast::BinExpr {
-    pub fn op_details(&self) -> Option<(SyntaxToken, BinOp)> {
+    fn op_details(&self) -> Option<(SyntaxToken, BinOp)> {
         self.syntax().children_with_tokens().filter_map(|it| it.into_token()).find_map(|c| {
             let bin_op = match c.kind() {
                 T![||] => BinOp::BooleanOr,
diff --git a/crates/ra_syntax/src/ast/make.rs b/crates/ra_syntax/src/ast/make.rs
index 40db570da0..9749327fa4 100644
--- a/crates/ra_syntax/src/ast/make.rs
+++ b/crates/ra_syntax/src/ast/make.rs
@@ -173,21 +173,10 @@ fn ast_from_text<N: AstNode>(text: &str) -> N {
 }
 
 pub mod tokens {
-    use crate::{AstNode, Parse, SourceFile, SyntaxKind, SyntaxKind::*, SyntaxToken, T};
+    use crate::{AstNode, Parse, SourceFile, SyntaxKind::*, SyntaxToken, T};
     use once_cell::sync::Lazy;
 
-    static SOURCE_FILE: Lazy<Parse<SourceFile>> =
-        Lazy::new(|| SourceFile::parse("const C: () = (1 != 1, 2 == 2)\n;"));
-
-    pub fn op(op: SyntaxKind) -> SyntaxToken {
-        SOURCE_FILE
-            .tree()
-            .syntax()
-            .descendants_with_tokens()
-            .filter_map(|it| it.into_token())
-            .find(|it| it.kind() == op)
-            .unwrap()
-    }
+    static SOURCE_FILE: Lazy<Parse<SourceFile>> = Lazy::new(|| SourceFile::parse(",\n; ;"));
 
     pub fn comma() -> SyntaxToken {
         SOURCE_FILE
diff --git a/docs/user/assists.md b/docs/user/assists.md
index 6f4c30bee3..8da7578e2f 100644
--- a/docs/user/assists.md
+++ b/docs/user/assists.md
@@ -329,25 +329,6 @@ fn main() {
 }
 ```
 
-## `invert_if`
-
-Apply invert_if
-This transforms if expressions of the form `if !x {A} else {B}` into `if x {B} else {A}`
-This also works with `!=`. This assist can only be applied with the cursor
-on `if`.
-
-```rust
-// BEFORE
-fn main() {
-    if┃ !y { A } else { B }
-}
-
-// AFTER
-fn main() {
-    if y { B } else { A }
-}
-```
-
 ## `make_raw_string`
 
 Adds `r#` to a plain string literal.


Test patch:
diff --git a/crates/ra_assists/src/doc_tests/generated.rs b/crates/ra_assists/src/doc_tests/generated.rs
index 3c716c2d12..176761efb9 100644
--- a/crates/ra_assists/src/doc_tests/generated.rs
+++ b/crates/ra_assists/src/doc_tests/generated.rs
@@ -341,23 +341,6 @@ fn main() {
     )
 }
 
-#[test]
-fn doctest_invert_if() {
-    check(
-        "invert_if",
-        r#####"
-fn main() {
-    if<|> !y { A } else { B }
-}
-"#####,
-        r#####"
-fn main() {
-    if y { B } else { A }
-}
-"#####,
-    )
-}
-
 #[test]
 fn doctest_make_raw_string() {
     check(
diff --git a/crates/ra_hir_expand/src/test_db.rs b/crates/ra_hir_expand/src/test_db.rs
deleted file mode 100644
index d23e75d9ee..0000000000
--- a/crates/ra_hir_expand/src/test_db.rs
+++ /dev/null
@@ -1,50 +0,0 @@
-//! Database used for testing `hir_expand`.
-
-use std::{
-    panic,
-    sync::{Arc, Mutex},
-};
-
-use ra_db::{salsa, CrateId, FileId, FileLoader, FileLoaderDelegate, RelativePath};
-
-#[salsa::database(
-    ra_db::SourceDatabaseExtStorage,
-    ra_db::SourceDatabaseStorage,
-    crate::db::AstDatabaseStorage
-)]
-#[derive(Debug, Default)]
-pub struct TestDB {
-    runtime: salsa::Runtime<TestDB>,
-    events: Mutex<Option<Vec<salsa::Event<TestDB>>>>,
-}
-
-impl salsa::Database for TestDB {
-    fn salsa_runtime(&self) -> &salsa::Runtime<Self> {
-        &self.runtime
-    }
-
-    fn salsa_event(&self, event: impl Fn() -> salsa::Event<TestDB>) {
-        let mut events = self.events.lock().unwrap();
-        if let Some(events) = &mut *events {
-            events.push(event());
-        }
-    }
-}
-
-impl panic::RefUnwindSafe for TestDB {}
-
-impl FileLoader for TestDB {
-    fn file_text(&self, file_id: FileId) -> Arc<String> {
-        FileLoaderDelegate(self).file_text(file_id)
-    }
-    fn resolve_relative_path(
-        &self,
-        anchor: FileId,
-        relative_path: &RelativePath,
-    ) -> Option<FileId> {
-        FileLoaderDelegate(self).resolve_relative_path(anchor, relative_path)
-    }
-    fn relevant_crates(&self, file_id: FileId) -> Arc<Vec<CrateId>> {
-        FileLoaderDelegate(self).relevant_crates(file_id)
-    }
-}