#![allow(clippy::expect_used, clippy::print_stderr)]
use std::path::Path;
use diffler::lsp::{LspClient, Resolution, resolve};
fn fixture_crate(root: &Path) {
std::fs::create_dir_all(root.join("src")).expect("mkdir");
std::fs::write(
root.join("Cargo.toml"),
"[package]\nname = \"blast\"\nversion = \"0.0.0\"\nedition = \"2021\"\n",
)
.expect("manifest");
std::fs::write(
root.join("src/main.rs"),
"fn target() -> u32 { 7 }\n\nfn caller_one() -> u32 { target() }\n\nfn main() { let _ = caller_one() + target(); }\n",
)
.expect("source");
}
#[cfg(unix)]
#[tokio::test]
async fn finds_references_for_a_changed_function() {
let Resolution::Found(spec) = resolve("rs") else {
eprintln!("rust-analyzer not installed; skipping");
return;
};
let dir = tempfile::tempdir().expect("tempdir");
fixture_crate(dir.path());
let root = dir.path().canonicalize().expect("canonical root");
let Ok(mut client) = LspClient::spawn(spec.bin, spec.argv, &root).await else {
eprintln!("rust-analyzer on PATH but not runnable; skipping");
return;
};
let source = std::fs::read_to_string(root.join("src/main.rs")).expect("read");
client
.sync_document(Path::new("src/main.rs"), &source)
.await
.expect("open");
let symbols = client
.document_symbols(Path::new("src/main.rs"))
.await
.expect("symbols");
let target = symbols
.iter()
.find(|s| s.name == "target")
.expect("target symbol");
let mut refs = Vec::new();
for _ in 0..40 {
if let Ok(result) = client
.references(
Path::new("src/main.rs"),
target.select_line,
target.select_character,
)
.await
{
refs = result;
if refs.len() >= 2 {
break;
}
}
tokio::time::sleep(std::time::Duration::from_millis(250)).await;
}
assert_eq!(refs.len(), 2, "two callers of target(): {refs:?}");
assert!(refs.iter().all(|r| r.path == "src/main.rs"));
let mut callers = Vec::new();
let mut names: Vec<String> = Vec::new();
for _ in 0..40 {
if let Ok(result) = client
.incoming_calls(
Path::new("src/main.rs"),
target.select_line,
target.select_character,
)
.await
{
callers = result;
names = callers.iter().map(|c| c.name.clone()).collect();
names.sort_unstable();
if names == ["caller_one", "main"] {
break;
}
}
tokio::time::sleep(std::time::Duration::from_millis(500)).await;
}
assert_eq!(names, ["caller_one", "main"], "direct callers of target()");
let one = callers
.iter()
.find(|c| c.name == "caller_one")
.expect("caller_one");
let mut second = Vec::new();
for _ in 0..20 {
if let Ok(result) = client
.incoming_calls(Path::new(&one.path), one.select_line, one.select_character)
.await
{
second = result;
if second.len() == 1 {
break;
}
}
tokio::time::sleep(std::time::Duration::from_millis(500)).await;
}
assert_eq!(second.len(), 1, "one caller of caller_one: {second:?}");
assert_eq!(second[0].name, "main", "the chain continues past depth 1");
}