use std::path::PathBuf;
use cpp_code_analysis::file::coordinates::ReadingOffset;
use cpp_code_analysis::{DiskFiles, Known, OpenDocuments, Session, SessionFiles, WatchFilter};
use cpp_ls::handlers::implementation::definitions_of;
struct Project {
root: PathBuf,
}
impl Project {
fn new(name: &str) -> Project {
let root = std::env::temp_dir()
.join("cpp_ls-module-implementation")
.join(format!("{}-{name}", std::process::id()));
let _ = std::fs::remove_dir_all(&root);
std::fs::create_dir_all(&root).expect("a scratch directory");
Project { root }
}
fn root(&self) -> &std::path::Path {
&self.root
}
fn write(&self, name: &str, text: &str) -> PathBuf {
let path = self.root.join(name);
if let Some(parent) = path.parent() {
std::fs::create_dir_all(parent).expect("a scratch directory");
}
std::fs::write(&path, text).expect("the file is written");
path
}
}
impl Drop for Project {
fn drop(&mut self) {
let _ = std::fs::remove_dir_all(&self.root);
}
}
#[test]
fn a_cppm_declaration_answers_the_cpp_that_defines_it() {
let project = Project::new("cppm-to-cpp");
let interface_text = "\
export module report;
namespace report {
export struct Report {
void add(int line);
int joined(int separator) const;
};
}
";
let implementation_text = "\
module report;
namespace report {
void Report::add(int line) {
line = line + 1;
}
int Report::joined(int separator) const {
return separator;
}
}
";
let interface = project.write("report.cppm", interface_text);
let implementation = project.write("report.cpp", implementation_text);
let mut session = Session::open(
project.root().to_path_buf(),
SessionFiles::new(OpenDocuments::new(), DiskFiles),
WatchFilter::new(project.root()),
);
session.add_project_files([interface.clone(), implementation.clone()]);
session.index_everything();
for (path, expected) in [
(&interface, "report::Report"),
(&implementation, "report::Report"),
] {
let summary = session.index().summary(path).expect("the file is indexed");
let scopes: Vec<Option<String>> = summary
.declarations
.iter()
.filter(|fact| fact.name.as_str() == "joined")
.map(|fact| fact.scope.clone())
.collect();
assert!(
scopes
.iter()
.any(|scope| scope.as_deref() == Some(expected)),
"{}: `joined` is filed under `{expected}`: {scopes:?}",
path.display()
);
}
let view = session
.view_of_the_file(&interface)
.expect("the interface has a view");
let at = interface_text.find("joined").expect("the declaration") + 1;
let definition = session.definition(&view, ReadingOffset::new(at));
assert!(
matches!(definition, Known::Yes(_)),
"the declaration resolves: {definition:?}"
);
let found = definitions_of(&session, &view, at);
assert!(
found
.iter()
.any(|location| location.uri.path().ends_with("report.cpp")),
"Go to Implementation answers `report.cpp`: {found:?}"
);
}