about summary refs log tree commit diff
path: root/src/tools/rust-analyzer/crates/query-group-macro/tests
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2025-04-30 20:42:22 +0000
committerbors <bors@rust-lang.org>2025-04-30 20:42:22 +0000
commitb45dd71d1824f176fba88f6c40467030a16afa2c (patch)
treee411711ed4353d3828f34aebca7bdeda8f357d28 /src/tools/rust-analyzer/crates/query-group-macro/tests
parent251cda5e1f0057eb04fd9fc1653f2f1e010e8f97 (diff)
parenta477172cedb83e1b8295bb20f4bff6e79f9e7bb1 (diff)
downloadrust-b45dd71d1824f176fba88f6c40467030a16afa2c.tar.gz
rust-b45dd71d1824f176fba88f6c40467030a16afa2c.zip
Auto merge of #140529 - matthiaskrgr:rollup-jpaa2ky, r=matthiaskrgr
Rollup of 10 pull requests

Successful merges:

 - #140385 (Subtree update of `rust-analyzer`)
 - #140458 (Fix for async drop ice with partly dropped tuple)
 - #140465 (chore: edit and move tests)
 - #140467 (Don't FCW assoc consts in patterns)
 - #140468 (Minor tweaks to make some normalization (adjacent) code less confusing)
 - #140470 (CI: rfl: move job forward to Linux v6.15-rc4)
 - #140476 (chore: delete unused ui/auxiliary crates)
 - #140481 (Require sanitizers be enabled for asan_odr_windows.rs)
 - #140486 (rustfmt: Also allow bool literals as first item of let chain)
 - #140494 (Parser: Document restrictions)

r? `@ghost`
`@rustbot` modify labels: rollup
Diffstat (limited to 'src/tools/rust-analyzer/crates/query-group-macro/tests')
-rw-r--r--src/tools/rust-analyzer/crates/query-group-macro/tests/arity.rs32
-rw-r--r--src/tools/rust-analyzer/crates/query-group-macro/tests/hello_world.rs129
-rw-r--r--src/tools/rust-analyzer/crates/query-group-macro/tests/interned.rs50
-rw-r--r--src/tools/rust-analyzer/crates/query-group-macro/tests/logger_db.rs60
-rw-r--r--src/tools/rust-analyzer/crates/query-group-macro/tests/lru.rs68
-rw-r--r--src/tools/rust-analyzer/crates/query-group-macro/tests/multiple_dbs.rs25
-rw-r--r--src/tools/rust-analyzer/crates/query-group-macro/tests/old_and_new.rs109
-rw-r--r--src/tools/rust-analyzer/crates/query-group-macro/tests/result.rs52
-rw-r--r--src/tools/rust-analyzer/crates/query-group-macro/tests/supertrait.rs20
-rw-r--r--src/tools/rust-analyzer/crates/query-group-macro/tests/tuples.rs39
10 files changed, 584 insertions, 0 deletions
diff --git a/src/tools/rust-analyzer/crates/query-group-macro/tests/arity.rs b/src/tools/rust-analyzer/crates/query-group-macro/tests/arity.rs
new file mode 100644
index 00000000000..f1b29612a17
--- /dev/null
+++ b/src/tools/rust-analyzer/crates/query-group-macro/tests/arity.rs
@@ -0,0 +1,32 @@
+use query_group_macro::query_group;
+
+#[query_group]
+pub trait ArityDb: salsa::Database {
+    #[salsa::invoke_interned(one)]
+    fn one(&self, a: ()) -> String;
+
+    #[salsa::invoke_interned(two)]
+    fn two(&self, a: (), b: ()) -> String;
+
+    #[salsa::invoke_interned(three)]
+    fn three(&self, a: (), b: (), c: ()) -> String;
+
+    #[salsa::invoke_interned(none)]
+    fn none(&self) -> String;
+}
+
+fn one(_db: &dyn ArityDb, _a: ()) -> String {
+    String::new()
+}
+
+fn two(_db: &dyn ArityDb, _a: (), _b: ()) -> String {
+    String::new()
+}
+
+fn three(_db: &dyn ArityDb, _a: (), _b: (), _c: ()) -> String {
+    String::new()
+}
+
+fn none(_db: &dyn ArityDb) -> String {
+    String::new()
+}
diff --git a/src/tools/rust-analyzer/crates/query-group-macro/tests/hello_world.rs b/src/tools/rust-analyzer/crates/query-group-macro/tests/hello_world.rs
new file mode 100644
index 00000000000..b0aec8dc53b
--- /dev/null
+++ b/src/tools/rust-analyzer/crates/query-group-macro/tests/hello_world.rs
@@ -0,0 +1,129 @@
+use expect_test::expect;
+use query_group_macro::query_group;
+
+mod logger_db;
+use logger_db::LoggerDb;
+
+#[query_group]
+pub trait HelloWorldDatabase: salsa::Database {
+    // input
+    // // input with no params
+    #[salsa::input]
+    fn input_string(&self) -> String;
+
+    // unadorned query
+    #[salsa::invoke_interned(length_query)]
+    fn length_query(&self, key: ()) -> usize;
+
+    // unadorned query
+    fn length_query_with_no_params(&self) -> usize;
+
+    // renamed/invoke query
+    #[salsa::invoke_interned(invoke_length_query_actual)]
+    fn invoke_length_query(&self, key: ()) -> usize;
+
+    // not a query. should not invoked
+    #[salsa::transparent]
+    fn transparent_length(&self, key: ()) -> usize;
+
+    #[salsa::transparent]
+    #[salsa::invoke_interned(transparent_and_invoke_length_actual)]
+    fn transparent_and_invoke_length(&self, key: ()) -> usize;
+}
+
+fn length_query(db: &dyn HelloWorldDatabase, _key: ()) -> usize {
+    db.input_string().len()
+}
+
+fn length_query_with_no_params(db: &dyn HelloWorldDatabase) -> usize {
+    db.input_string().len()
+}
+
+fn invoke_length_query_actual(db: &dyn HelloWorldDatabase, _key: ()) -> usize {
+    db.input_string().len()
+}
+
+fn transparent_length(db: &dyn HelloWorldDatabase, _key: ()) -> usize {
+    db.input_string().len()
+}
+
+fn transparent_and_invoke_length_actual(db: &dyn HelloWorldDatabase, _key: ()) -> usize {
+    db.input_string().len()
+}
+
+#[test]
+fn unadorned_query() {
+    let mut db = LoggerDb::default();
+
+    db.set_input_string(String::from("Hello, world!"));
+    let len = db.length_query(());
+
+    assert_eq!(len, 13);
+    db.assert_logs(expect![[r#"
+        [
+            "salsa_event(WillCheckCancellation)",
+            "salsa_event(WillExecute { database_key: create_data_HelloWorldDatabase(Id(0)) })",
+            "salsa_event(WillCheckCancellation)",
+            "salsa_event(DidValidateMemoizedValue { database_key: create_data_HelloWorldDatabase(Id(0)) })",
+            "salsa_event(WillCheckCancellation)",
+            "salsa_event(WillExecute { database_key: length_query_shim(Id(800)) })",
+            "salsa_event(WillCheckCancellation)",
+        ]"#]]);
+}
+
+#[test]
+fn invoke_query() {
+    let mut db = LoggerDb::default();
+
+    db.set_input_string(String::from("Hello, world!"));
+    let len = db.invoke_length_query(());
+
+    assert_eq!(len, 13);
+    db.assert_logs(expect![[r#"
+        [
+            "salsa_event(WillCheckCancellation)",
+            "salsa_event(WillExecute { database_key: create_data_HelloWorldDatabase(Id(0)) })",
+            "salsa_event(WillCheckCancellation)",
+            "salsa_event(DidValidateMemoizedValue { database_key: create_data_HelloWorldDatabase(Id(0)) })",
+            "salsa_event(WillCheckCancellation)",
+            "salsa_event(WillExecute { database_key: invoke_length_query_shim(Id(800)) })",
+            "salsa_event(WillCheckCancellation)",
+        ]"#]]);
+}
+
+#[test]
+fn transparent() {
+    let mut db = LoggerDb::default();
+
+    db.set_input_string(String::from("Hello, world!"));
+    let len = db.transparent_length(());
+
+    assert_eq!(len, 13);
+    db.assert_logs(expect![[r#"
+        [
+            "salsa_event(WillCheckCancellation)",
+            "salsa_event(WillExecute { database_key: create_data_HelloWorldDatabase(Id(0)) })",
+            "salsa_event(WillCheckCancellation)",
+            "salsa_event(DidValidateMemoizedValue { database_key: create_data_HelloWorldDatabase(Id(0)) })",
+        ]"#]]);
+}
+
+#[test]
+fn transparent_invoke() {
+    let mut db = LoggerDb::default();
+
+    db.set_input_string(String::from("Hello, world!"));
+    let len = db.transparent_and_invoke_length(());
+
+    assert_eq!(len, 13);
+    db.assert_logs(expect![[r#"
+        [
+            "salsa_event(WillCheckCancellation)",
+            "salsa_event(WillExecute { database_key: create_data_HelloWorldDatabase(Id(0)) })",
+            "salsa_event(WillCheckCancellation)",
+            "salsa_event(DidValidateMemoizedValue { database_key: create_data_HelloWorldDatabase(Id(0)) })",
+            "salsa_event(WillCheckCancellation)",
+            "salsa_event(WillExecute { database_key: transparent_and_invoke_length_shim(Id(800)) })",
+            "salsa_event(WillCheckCancellation)",
+        ]"#]]);
+}
diff --git a/src/tools/rust-analyzer/crates/query-group-macro/tests/interned.rs b/src/tools/rust-analyzer/crates/query-group-macro/tests/interned.rs
new file mode 100644
index 00000000000..26ed316122a
--- /dev/null
+++ b/src/tools/rust-analyzer/crates/query-group-macro/tests/interned.rs
@@ -0,0 +1,50 @@
+use query_group_macro::query_group;
+
+use expect_test::expect;
+use salsa::plumbing::AsId;
+
+mod logger_db;
+use logger_db::LoggerDb;
+
+#[salsa::interned(no_lifetime)]
+pub struct InternedString {
+    data: String,
+}
+
+#[query_group]
+pub trait InternedDB: salsa::Database {
+    #[salsa::interned]
+    fn intern_string(&self, data: String) -> InternedString;
+
+    fn interned_len(&self, id: InternedString) -> usize;
+}
+
+fn interned_len(db: &dyn InternedDB, id: InternedString) -> usize {
+    db.lookup_intern_string(id).len()
+}
+
+#[test]
+fn intern_round_trip() {
+    let db = LoggerDb::default();
+
+    let id = db.intern_string(String::from("Hello, world!"));
+    let s = db.lookup_intern_string(id);
+
+    assert_eq!(s.len(), 13);
+    db.assert_logs(expect![[r#"[]"#]]);
+}
+
+#[test]
+fn intern_with_query() {
+    let db = LoggerDb::default();
+
+    let id = db.intern_string(String::from("Hello, world!"));
+    let len = db.interned_len(id);
+
+    assert_eq!(len, 13);
+    db.assert_logs(expect![[r#"
+        [
+            "salsa_event(WillCheckCancellation)",
+            "salsa_event(WillExecute { database_key: interned_len_shim(Id(0)) })",
+        ]"#]]);
+}
diff --git a/src/tools/rust-analyzer/crates/query-group-macro/tests/logger_db.rs b/src/tools/rust-analyzer/crates/query-group-macro/tests/logger_db.rs
new file mode 100644
index 00000000000..5cf9be36f70
--- /dev/null
+++ b/src/tools/rust-analyzer/crates/query-group-macro/tests/logger_db.rs
@@ -0,0 +1,60 @@
+use std::sync::{Arc, Mutex};
+
+#[salsa::db]
+#[derive(Default, Clone)]
+pub(crate) struct LoggerDb {
+    storage: salsa::Storage<Self>,
+    logger: Logger,
+}
+
+#[derive(Default, Clone)]
+struct Logger {
+    logs: Arc<Mutex<Vec<String>>>,
+}
+
+#[salsa::db]
+impl salsa::Database for LoggerDb {
+    fn salsa_event(&self, event: &dyn Fn() -> salsa::Event) {
+        let event = event();
+        match event.kind {
+            salsa::EventKind::WillExecute { .. }
+            | salsa::EventKind::WillCheckCancellation
+            | salsa::EventKind::DidValidateMemoizedValue { .. }
+            | salsa::EventKind::WillDiscardStaleOutput { .. }
+            | salsa::EventKind::DidDiscard { .. } => {
+                self.push_log(format!("salsa_event({:?})", event.kind));
+            }
+            _ => {}
+        }
+    }
+}
+
+impl LoggerDb {
+    /// Log an event from inside a tracked function.
+    pub(crate) fn push_log(&self, string: String) {
+        self.logger.logs.lock().unwrap().push(string);
+    }
+
+    /// Asserts what the (formatted) logs should look like,
+    /// clearing the logged events. This takes `&mut self` because
+    /// it is meant to be run from outside any tracked functions.
+    pub(crate) fn assert_logs(&self, expected: expect_test::Expect) {
+        let logs = std::mem::take(&mut *self.logger.logs.lock().unwrap());
+        expected.assert_eq(&format!("{:#?}", logs));
+    }
+}
+
+/// Test the logger database.
+///
+/// This test isn't very interesting, but it *does* remove a dead code warning.
+#[test]
+fn test_logger_db() {
+    let db = LoggerDb::default();
+    db.push_log("test".to_string());
+    db.assert_logs(expect_test::expect![
+        r#"
+        [
+            "test",
+        ]"#
+    ]);
+}
diff --git a/src/tools/rust-analyzer/crates/query-group-macro/tests/lru.rs b/src/tools/rust-analyzer/crates/query-group-macro/tests/lru.rs
new file mode 100644
index 00000000000..f56dd5c2f9b
--- /dev/null
+++ b/src/tools/rust-analyzer/crates/query-group-macro/tests/lru.rs
@@ -0,0 +1,68 @@
+use expect_test::expect;
+
+mod logger_db;
+use logger_db::LoggerDb;
+use query_group_macro::query_group;
+
+#[query_group]
+pub trait LruDB: salsa::Database {
+    // // input with no params
+    #[salsa::input]
+    fn input_string(&self) -> String;
+
+    #[salsa::lru(16)]
+    #[salsa::invoke_interned(length_query)]
+    fn length_query(&self, key: ()) -> usize;
+
+    #[salsa::lru(16)]
+    #[salsa::invoke_interned(invoked_query)]
+    fn length_query_invoke(&self, key: ()) -> usize;
+}
+
+fn length_query(db: &dyn LruDB, _key: ()) -> usize {
+    db.input_string().len()
+}
+
+fn invoked_query(db: &dyn LruDB, _key: ()) -> usize {
+    db.input_string().len()
+}
+
+#[test]
+fn plain_lru() {
+    let mut db = LoggerDb::default();
+
+    db.set_input_string(String::from("Hello, world!"));
+    let len = db.length_query(());
+
+    assert_eq!(len, 13);
+    db.assert_logs(expect![[r#"
+        [
+            "salsa_event(WillCheckCancellation)",
+            "salsa_event(WillExecute { database_key: create_data_LruDB(Id(0)) })",
+            "salsa_event(WillCheckCancellation)",
+            "salsa_event(DidValidateMemoizedValue { database_key: create_data_LruDB(Id(0)) })",
+            "salsa_event(WillCheckCancellation)",
+            "salsa_event(WillExecute { database_key: length_query_shim(Id(800)) })",
+            "salsa_event(WillCheckCancellation)",
+        ]"#]]);
+}
+
+#[test]
+fn invoke_lru() {
+    let mut db = LoggerDb::default();
+
+    db.set_input_string(String::from("Hello, world!"));
+    let len = db.length_query_invoke(());
+
+    assert_eq!(len, 13);
+    db.assert_logs(expect![[r#"
+        [
+            "salsa_event(WillCheckCancellation)",
+            "salsa_event(WillExecute { database_key: create_data_LruDB(Id(0)) })",
+            "salsa_event(WillCheckCancellation)",
+            "salsa_event(DidValidateMemoizedValue { database_key: create_data_LruDB(Id(0)) })",
+            "salsa_event(WillCheckCancellation)",
+            "salsa_event(WillExecute { database_key: length_query_invoke_shim(Id(800)) })",
+            "salsa_event(WillCheckCancellation)",
+        ]"#]]);
+}
diff --git a/src/tools/rust-analyzer/crates/query-group-macro/tests/multiple_dbs.rs b/src/tools/rust-analyzer/crates/query-group-macro/tests/multiple_dbs.rs
new file mode 100644
index 00000000000..f36e7fdbebf
--- /dev/null
+++ b/src/tools/rust-analyzer/crates/query-group-macro/tests/multiple_dbs.rs
@@ -0,0 +1,25 @@
+use query_group_macro::query_group;
+
+#[query_group]
+pub trait DatabaseOne: salsa::Database {
+    #[salsa::input]
+    fn input_string(&self) -> String;
+
+    // unadorned query
+    #[salsa::invoke_interned(length)]
+    fn length(&self, key: ()) -> usize;
+}
+
+#[query_group]
+pub trait DatabaseTwo: DatabaseOne {
+    #[salsa::invoke_interned(second_length)]
+    fn second_length(&self, key: ()) -> usize;
+}
+
+fn length(db: &dyn DatabaseOne, _key: ()) -> usize {
+    db.input_string().len()
+}
+
+fn second_length(db: &dyn DatabaseTwo, _key: ()) -> usize {
+    db.input_string().len()
+}
diff --git a/src/tools/rust-analyzer/crates/query-group-macro/tests/old_and_new.rs b/src/tools/rust-analyzer/crates/query-group-macro/tests/old_and_new.rs
new file mode 100644
index 00000000000..a18b23a7d8a
--- /dev/null
+++ b/src/tools/rust-analyzer/crates/query-group-macro/tests/old_and_new.rs
@@ -0,0 +1,109 @@
+use expect_test::expect;
+
+mod logger_db;
+use logger_db::LoggerDb;
+use query_group_macro::query_group;
+
+#[salsa::input]
+struct Input {
+    str: String,
+}
+
+#[query_group]
+trait PartialMigrationDatabase: salsa::Database {
+    fn length_query(&self, input: Input) -> usize;
+
+    // renamed/invoke query
+    #[salsa::invoke(invoke_length_query_actual)]
+    fn invoke_length_query(&self, input: Input) -> usize;
+
+    // invoke tracked function
+    #[salsa::invoke(invoke_length_tracked_actual)]
+    fn invoke_length_tracked(&self, input: Input) -> usize;
+}
+
+fn length_query(db: &dyn PartialMigrationDatabase, input: Input) -> usize {
+    input.str(db).len()
+}
+
+fn invoke_length_query_actual(db: &dyn PartialMigrationDatabase, input: Input) -> usize {
+    input.str(db).len()
+}
+
+#[salsa::tracked]
+fn invoke_length_tracked_actual(db: &dyn PartialMigrationDatabase, input: Input) -> usize {
+    input.str(db).len()
+}
+
+#[test]
+fn unadorned_query() {
+    let db = LoggerDb::default();
+
+    let input = Input::new(&db, String::from("Hello, world!"));
+    let len = db.length_query(input);
+
+    assert_eq!(len, 13);
+    db.assert_logs(expect![[r#"
+        [
+            "salsa_event(WillCheckCancellation)",
+            "salsa_event(WillExecute { database_key: length_query_shim(Id(0)) })",
+        ]"#]]);
+}
+
+#[test]
+fn invoke_query() {
+    let db = LoggerDb::default();
+
+    let input = Input::new(&db, String::from("Hello, world!"));
+    let len = db.invoke_length_query(input);
+
+    assert_eq!(len, 13);
+    db.assert_logs(expect![[r#"
+        [
+            "salsa_event(WillCheckCancellation)",
+            "salsa_event(WillExecute { database_key: invoke_length_query_shim(Id(0)) })",
+        ]"#]]);
+}
+
+// todo: does this even make sense?
+#[test]
+fn invoke_tracked_query() {
+    let db = LoggerDb::default();
+
+    let input = Input::new(&db, String::from("Hello, world!"));
+    let len = db.invoke_length_tracked(input);
+
+    assert_eq!(len, 13);
+    db.assert_logs(expect![[r#"
+        [
+            "salsa_event(WillCheckCancellation)",
+            "salsa_event(WillExecute { database_key: invoke_length_tracked_shim(Id(0)) })",
+            "salsa_event(WillCheckCancellation)",
+            "salsa_event(WillExecute { database_key: invoke_length_tracked_actual(Id(0)) })",
+        ]"#]]);
+}
+
+#[test]
+fn new_salsa_baseline() {
+    let db = LoggerDb::default();
+
+    #[salsa::input]
+    struct Input {
+        str: String,
+    }
+
+    #[salsa::tracked]
+    fn new_salsa_length_query(db: &dyn PartialMigrationDatabase, input: Input) -> usize {
+        input.str(db).len()
+    }
+
+    let input = Input::new(&db, String::from("Hello, world!"));
+    let len = new_salsa_length_query(&db, input);
+
+    assert_eq!(len, 13);
+    db.assert_logs(expect![[r#"
+        [
+            "salsa_event(WillCheckCancellation)",
+            "salsa_event(WillExecute { database_key: new_salsa_length_query(Id(0)) })",
+        ]"#]]);
+}
diff --git a/src/tools/rust-analyzer/crates/query-group-macro/tests/result.rs b/src/tools/rust-analyzer/crates/query-group-macro/tests/result.rs
new file mode 100644
index 00000000000..06f7f403c7e
--- /dev/null
+++ b/src/tools/rust-analyzer/crates/query-group-macro/tests/result.rs
@@ -0,0 +1,52 @@
+mod logger_db;
+use expect_test::expect;
+use logger_db::LoggerDb;
+
+use query_group_macro::query_group;
+
+#[derive(Clone, Debug, PartialEq, Eq)]
+pub struct Error;
+
+#[query_group]
+pub trait ResultDatabase: salsa::Database {
+    #[salsa::input]
+    fn input_string(&self) -> String;
+
+    #[salsa::invoke_interned(length)]
+    fn length(&self, key: ()) -> Result<usize, Error>;
+
+    #[salsa::invoke_interned(length2)]
+    fn length2(&self, key: ()) -> Result<usize, Error>;
+}
+
+fn length(db: &dyn ResultDatabase, _key: ()) -> Result<usize, Error> {
+    Ok(db.input_string().len())
+}
+
+fn length2(db: &dyn ResultDatabase, _key: ()) -> Result<usize, Error> {
+    Ok(db.input_string().len())
+}
+
+#[test]
+fn test_queries_with_results() {
+    let mut db = LoggerDb::default();
+    let input = "hello";
+    db.set_input_string(input.to_owned());
+    assert_eq!(db.length(()), Ok(input.len()));
+    assert_eq!(db.length2(()), Ok(input.len()));
+
+    db.assert_logs(expect![[r#"
+        [
+            "salsa_event(WillCheckCancellation)",
+            "salsa_event(WillExecute { database_key: create_data_ResultDatabase(Id(0)) })",
+            "salsa_event(WillCheckCancellation)",
+            "salsa_event(DidValidateMemoizedValue { database_key: create_data_ResultDatabase(Id(0)) })",
+            "salsa_event(WillCheckCancellation)",
+            "salsa_event(WillExecute { database_key: length_shim(Id(800)) })",
+            "salsa_event(WillCheckCancellation)",
+            "salsa_event(WillCheckCancellation)",
+            "salsa_event(WillCheckCancellation)",
+            "salsa_event(WillExecute { database_key: length2_shim(Id(c00)) })",
+            "salsa_event(WillCheckCancellation)",
+        ]"#]]);
+}
diff --git a/src/tools/rust-analyzer/crates/query-group-macro/tests/supertrait.rs b/src/tools/rust-analyzer/crates/query-group-macro/tests/supertrait.rs
new file mode 100644
index 00000000000..70073ac1de3
--- /dev/null
+++ b/src/tools/rust-analyzer/crates/query-group-macro/tests/supertrait.rs
@@ -0,0 +1,20 @@
+use query_group_macro::query_group;
+
+#[salsa::db]
+pub trait SourceDb: salsa::Database {
+    /// Text of the file.
+    fn file_text(&self, id: usize) -> String;
+}
+
+#[query_group]
+pub trait RootDb: SourceDb {
+    #[salsa::invoke_interned(parse)]
+    fn parse(&self, id: usize) -> String;
+}
+
+fn parse(db: &dyn RootDb, id: usize) -> String {
+    // this is the test: does the following compile?
+    db.file_text(id);
+
+    String::new()
+}
diff --git a/src/tools/rust-analyzer/crates/query-group-macro/tests/tuples.rs b/src/tools/rust-analyzer/crates/query-group-macro/tests/tuples.rs
new file mode 100644
index 00000000000..af0e852695e
--- /dev/null
+++ b/src/tools/rust-analyzer/crates/query-group-macro/tests/tuples.rs
@@ -0,0 +1,39 @@
+use query_group_macro::query_group;
+
+mod logger_db;
+use expect_test::expect;
+use logger_db::LoggerDb;
+
+#[query_group]
+pub trait HelloWorldDatabase: salsa::Database {
+    #[salsa::input]
+    fn input_string(&self) -> String;
+
+    #[salsa::invoke_interned(length_query)]
+    fn length_query(&self, key: ()) -> (usize, usize);
+}
+
+fn length_query(db: &dyn HelloWorldDatabase, _key: ()) -> (usize, usize) {
+    let len = db.input_string().len();
+    (len, len)
+}
+
+#[test]
+fn query() {
+    let mut db = LoggerDb::default();
+
+    db.set_input_string(String::from("Hello, world!"));
+    let len = db.length_query(());
+
+    assert_eq!(len, (13, 13));
+    db.assert_logs(expect![[r#"
+        [
+            "salsa_event(WillCheckCancellation)",
+            "salsa_event(WillExecute { database_key: create_data_HelloWorldDatabase(Id(0)) })",
+            "salsa_event(WillCheckCancellation)",
+            "salsa_event(DidValidateMemoizedValue { database_key: create_data_HelloWorldDatabase(Id(0)) })",
+            "salsa_event(WillCheckCancellation)",
+            "salsa_event(WillExecute { database_key: length_query_shim(Id(800)) })",
+            "salsa_event(WillCheckCancellation)",
+        ]"#]]);
+}