mirror of
https://github.com/salsa-rs/salsa.git
synced 2025-07-07 21:35:17 +00:00
324 lines
8.8 KiB
Rust
324 lines
8.8 KiB
Rust
use std::collections::HashSet;
|
|
|
|
mod common;
|
|
use common::{LogDatabase, LoggerDatabase};
|
|
use expect_test::expect;
|
|
use salsa::{Accumulator, Setter};
|
|
use test_log::test;
|
|
|
|
#[salsa::input(debug)]
|
|
struct File {
|
|
name: String,
|
|
dependencies: Vec<File>,
|
|
issues: Vec<u32>,
|
|
}
|
|
|
|
#[salsa::accumulator]
|
|
#[derive(Debug)]
|
|
struct Diagnostic(#[allow(dead_code)] String);
|
|
|
|
#[salsa::tracked(cycle_fn = cycle_fn, cycle_initial = cycle_initial)]
|
|
fn check_file(db: &dyn LogDatabase, file: File) -> Vec<u32> {
|
|
db.push_log(format!(
|
|
"check_file(name = {}, issues = {:?})",
|
|
file.name(db),
|
|
file.issues(db)
|
|
));
|
|
|
|
let mut collected_issues = HashSet::<u32>::from_iter(file.issues(db).iter().copied());
|
|
|
|
for dep in file.dependencies(db) {
|
|
let issues = check_file(db, dep);
|
|
collected_issues.extend(issues);
|
|
}
|
|
|
|
let mut sorted_issues = collected_issues.iter().copied().collect::<Vec<_>>();
|
|
sorted_issues.sort();
|
|
|
|
for issue in &sorted_issues {
|
|
Diagnostic(format!("file {}: issue {}", file.name(db), issue)).accumulate(db);
|
|
}
|
|
|
|
sorted_issues
|
|
}
|
|
|
|
fn cycle_initial(_db: &dyn LogDatabase, _file: File) -> Vec<u32> {
|
|
vec![]
|
|
}
|
|
|
|
fn cycle_fn(
|
|
_db: &dyn LogDatabase,
|
|
_value: &[u32],
|
|
_count: u32,
|
|
_file: File,
|
|
) -> salsa::CycleRecoveryAction<Vec<u32>> {
|
|
salsa::CycleRecoveryAction::Iterate
|
|
}
|
|
|
|
#[test]
|
|
fn accumulate_once() {
|
|
let db = LoggerDatabase::default();
|
|
|
|
let file = File::new(&db, "fn".to_string(), vec![], vec![1]);
|
|
let diagnostics = check_file::accumulated::<Diagnostic>(&db, file);
|
|
db.assert_logs(expect![[r#"
|
|
[
|
|
"check_file(name = fn, issues = [1])",
|
|
]"#]]);
|
|
|
|
expect![[r#"
|
|
[
|
|
Diagnostic(
|
|
"file fn: issue 1",
|
|
),
|
|
]"#]]
|
|
.assert_eq(&format!("{diagnostics:#?}"));
|
|
}
|
|
|
|
#[test]
|
|
fn accumulate_with_dep() {
|
|
let db = LoggerDatabase::default();
|
|
|
|
let file_a = File::new(&db, "file_a".to_string(), vec![], vec![1]);
|
|
let file_b = File::new(&db, "file_b".to_string(), vec![file_a], vec![2]);
|
|
|
|
let diagnostics = check_file::accumulated::<Diagnostic>(&db, file_b);
|
|
db.assert_logs(expect![[r#"
|
|
[
|
|
"check_file(name = file_b, issues = [2])",
|
|
"check_file(name = file_a, issues = [1])",
|
|
]"#]]);
|
|
|
|
expect![[r#"
|
|
[
|
|
Diagnostic(
|
|
"file file_b: issue 1",
|
|
),
|
|
Diagnostic(
|
|
"file file_b: issue 2",
|
|
),
|
|
Diagnostic(
|
|
"file file_a: issue 1",
|
|
),
|
|
]"#]]
|
|
.assert_eq(&format!("{diagnostics:#?}"));
|
|
}
|
|
|
|
#[test]
|
|
fn accumulate_with_cycle() {
|
|
let mut db = LoggerDatabase::default();
|
|
|
|
let file_a = File::new(&db, "file_a".to_string(), vec![], vec![1]);
|
|
let file_b = File::new(&db, "file_b".to_string(), vec![file_a], vec![2]);
|
|
file_a.set_dependencies(&mut db).to(vec![file_b]);
|
|
|
|
let diagnostics = check_file::accumulated::<Diagnostic>(&db, file_b);
|
|
db.assert_logs(expect![[r#"
|
|
[
|
|
"check_file(name = file_b, issues = [2])",
|
|
"check_file(name = file_a, issues = [1])",
|
|
"check_file(name = file_b, issues = [2])",
|
|
"check_file(name = file_a, issues = [1])",
|
|
]"#]]);
|
|
|
|
expect![[r#"
|
|
[
|
|
Diagnostic(
|
|
"file file_b: issue 1",
|
|
),
|
|
Diagnostic(
|
|
"file file_b: issue 2",
|
|
),
|
|
Diagnostic(
|
|
"file file_a: issue 1",
|
|
),
|
|
Diagnostic(
|
|
"file file_a: issue 2",
|
|
),
|
|
]"#]]
|
|
.assert_eq(&format!("{diagnostics:#?}"));
|
|
}
|
|
|
|
#[test]
|
|
fn accumulate_with_cycle_second_revision() {
|
|
let mut db = LoggerDatabase::default();
|
|
|
|
let file_a = File::new(&db, "file_a".to_string(), vec![], vec![1]);
|
|
let file_b = File::new(&db, "file_b".to_string(), vec![file_a], vec![2]);
|
|
file_a.set_dependencies(&mut db).to(vec![file_b]);
|
|
|
|
let diagnostics = check_file::accumulated::<Diagnostic>(&db, file_b);
|
|
db.assert_logs(expect![[r#"
|
|
[
|
|
"check_file(name = file_b, issues = [2])",
|
|
"check_file(name = file_a, issues = [1])",
|
|
"check_file(name = file_b, issues = [2])",
|
|
"check_file(name = file_a, issues = [1])",
|
|
]"#]]);
|
|
|
|
expect![[r#"
|
|
[
|
|
Diagnostic(
|
|
"file file_b: issue 1",
|
|
),
|
|
Diagnostic(
|
|
"file file_b: issue 2",
|
|
),
|
|
Diagnostic(
|
|
"file file_a: issue 1",
|
|
),
|
|
Diagnostic(
|
|
"file file_a: issue 2",
|
|
),
|
|
]"#]]
|
|
.assert_eq(&format!("{diagnostics:#?}"));
|
|
|
|
file_b.set_issues(&mut db).to(vec![2, 3]);
|
|
|
|
let diagnostics = check_file::accumulated::<Diagnostic>(&db, file_a);
|
|
db.assert_logs(expect![[r#"
|
|
[
|
|
"check_file(name = file_b, issues = [2, 3])",
|
|
"check_file(name = file_a, issues = [1])",
|
|
"check_file(name = file_a, issues = [1])",
|
|
"check_file(name = file_b, issues = [2, 3])",
|
|
]"#]]);
|
|
|
|
expect![[r#"
|
|
[
|
|
Diagnostic(
|
|
"file file_a: issue 1",
|
|
),
|
|
Diagnostic(
|
|
"file file_a: issue 2",
|
|
),
|
|
Diagnostic(
|
|
"file file_a: issue 3",
|
|
),
|
|
Diagnostic(
|
|
"file file_b: issue 1",
|
|
),
|
|
Diagnostic(
|
|
"file file_b: issue 2",
|
|
),
|
|
Diagnostic(
|
|
"file file_b: issue 3",
|
|
),
|
|
]"#]]
|
|
.assert_eq(&format!("{diagnostics:#?}"));
|
|
}
|
|
|
|
#[test]
|
|
fn accumulate_add_cycle() {
|
|
let mut db = LoggerDatabase::default();
|
|
|
|
let file_a = File::new(&db, "file_a".to_string(), vec![], vec![1]);
|
|
let file_b = File::new(&db, "file_b".to_string(), vec![file_a], vec![2]);
|
|
|
|
let diagnostics = check_file::accumulated::<Diagnostic>(&db, file_b);
|
|
db.assert_logs(expect![[r#"
|
|
[
|
|
"check_file(name = file_b, issues = [2])",
|
|
"check_file(name = file_a, issues = [1])",
|
|
]"#]]);
|
|
|
|
expect![[r#"
|
|
[
|
|
Diagnostic(
|
|
"file file_b: issue 1",
|
|
),
|
|
Diagnostic(
|
|
"file file_b: issue 2",
|
|
),
|
|
Diagnostic(
|
|
"file file_a: issue 1",
|
|
),
|
|
]"#]]
|
|
.assert_eq(&format!("{diagnostics:#?}"));
|
|
|
|
file_a.set_dependencies(&mut db).to(vec![file_b]);
|
|
|
|
let diagnostics = check_file::accumulated::<Diagnostic>(&db, file_a);
|
|
db.assert_logs(expect![[r#"
|
|
[
|
|
"check_file(name = file_a, issues = [1])",
|
|
"check_file(name = file_b, issues = [2])",
|
|
"check_file(name = file_a, issues = [1])",
|
|
"check_file(name = file_b, issues = [2])",
|
|
]"#]]);
|
|
|
|
expect![[r#"
|
|
[
|
|
Diagnostic(
|
|
"file file_a: issue 1",
|
|
),
|
|
Diagnostic(
|
|
"file file_a: issue 2",
|
|
),
|
|
Diagnostic(
|
|
"file file_b: issue 1",
|
|
),
|
|
Diagnostic(
|
|
"file file_b: issue 2",
|
|
),
|
|
]"#]]
|
|
.assert_eq(&format!("{diagnostics:#?}"));
|
|
}
|
|
|
|
#[test]
|
|
fn accumulate_remove_cycle() {
|
|
let mut db = LoggerDatabase::default();
|
|
|
|
let file_a = File::new(&db, "file_a".to_string(), vec![], vec![1]);
|
|
let file_b = File::new(&db, "file_b".to_string(), vec![file_a], vec![2]);
|
|
file_a.set_dependencies(&mut db).to(vec![file_b]);
|
|
|
|
let diagnostics = check_file::accumulated::<Diagnostic>(&db, file_b);
|
|
db.assert_logs(expect![[r#"
|
|
[
|
|
"check_file(name = file_b, issues = [2])",
|
|
"check_file(name = file_a, issues = [1])",
|
|
"check_file(name = file_b, issues = [2])",
|
|
"check_file(name = file_a, issues = [1])",
|
|
]"#]]);
|
|
|
|
expect![[r#"
|
|
[
|
|
Diagnostic(
|
|
"file file_b: issue 1",
|
|
),
|
|
Diagnostic(
|
|
"file file_b: issue 2",
|
|
),
|
|
Diagnostic(
|
|
"file file_a: issue 1",
|
|
),
|
|
Diagnostic(
|
|
"file file_a: issue 2",
|
|
),
|
|
]"#]]
|
|
.assert_eq(&format!("{diagnostics:#?}"));
|
|
|
|
file_a.set_dependencies(&mut db).to(vec![]);
|
|
|
|
let diagnostics = check_file::accumulated::<Diagnostic>(&db, file_b);
|
|
db.assert_logs(expect![[r#"
|
|
[
|
|
"check_file(name = file_a, issues = [1])",
|
|
"check_file(name = file_b, issues = [2])",
|
|
]"#]]);
|
|
|
|
expect![[r#"
|
|
[
|
|
Diagnostic(
|
|
"file file_b: issue 1",
|
|
),
|
|
Diagnostic(
|
|
"file file_b: issue 2",
|
|
),
|
|
Diagnostic(
|
|
"file file_a: issue 1",
|
|
),
|
|
]"#]]
|
|
.assert_eq(&format!("{diagnostics:#?}"));
|
|
}
|