-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathcompiledb.rs
99 lines (90 loc) · 3.48 KB
/
compiledb.rs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
use clang_rs_binding::clang::Clang;
use clang_rs_binding::index::{
from_payload, to_payload, ChildVisitResult, Cursor, Payload, SpellingLocation,
};
use clang_rs_binding::with_chdir;
use std::path::Path;
fn visitor(cursor: &Cursor, _parent: &Cursor, payload: Payload) -> i32 {
if cursor.is_from_main_file() {
return ChildVisitResult::CONTINUE;
}
let spelling = cursor.spelling();
if cursor.is_cxx_method() || cursor.is_function_decl() || cursor.is_function_template() {
let extent = cursor.extent();
let start_loc = extent.start();
let end_loc = extent.end();
let start_spelling_loc = start_loc.spelling_location();
let end_spelling_loc = end_loc.spelling_location();
let payload = unsafe { from_payload::<AstDataPayload>(payload) };
let sl_to_string = |sl: SpellingLocation| {
format!(
"line: {}, column: {}, offset: {}",
sl.line, sl.column, sl.offset
)
};
let new_buf = payload.borrow().clone()
+ &format!(
"{}: {} - {}\n",
spelling,
sl_to_string(start_spelling_loc),
sl_to_string(end_spelling_loc)
);
*payload.borrow_mut() = new_buf;
}
ChildVisitResult::RECURSIVE
}
fn get_system_headers() -> Vec<String> {
let output = std::process::Command::new("clang++")
.arg("-v")
.arg("-c")
.arg("-fsyntax-only")
.arg("main.cpp")
.output()
.unwrap_or_else(|e| panic!("failed to get system headers, {}", e));
std::str::from_utf8(&output.stderr)
.unwrap()
.lines()
.skip_while(|&e| e != "#include <...> search starts here:")
.skip(1)
.take_while(|&e| e != "End of search list.")
.map(|e| e.trim_start().to_owned())
.collect::<Vec<_>>()
}
type AstDataPayload = std::cell::RefCell<String>;
fn collect_ast(cursor: &Cursor<'_>) -> String {
let data = AstDataPayload::default();
cursor.visit_children(visitor, to_payload(&data));
data.take()
}
fn read_test_oracle<P: AsRef<Path>>(filename: P) -> String {
std::fs::read_to_string(filename).unwrap()
}
#[test]
fn compile_db_works() {
let compile_db_dir = std::path::Path::new("tests/artifacts/compiledb");
let oracle = read_test_oracle(compile_db_dir.join("compiledb.test_oracle"));
with_chdir(compile_db_dir, || {
std::process::Command::new("cmake")
.arg("-Bbuild")
.arg("-S.")
.output()
.unwrap_or_else(|e| panic!("failed to create compile database, {}", e));
// must add `clang++ -v -c main.cpp`, otherwise system headers will not be found
let system_headers = get_system_headers().join(":");
assert!(!system_headers.is_empty());
std::env::set_var("CPLUS_INCLUDE_PATH", system_headers);
});
let clang = Clang::new();
let compiledb = clang
.compilation_database_from_directory(compile_db_dir.join("build"))
.unwrap();
let compile_commands = compiledb
.get_compile_commands(std::fs::canonicalize(compile_db_dir.join("main.cpp")).unwrap());
assert_eq!(compile_commands.get_size(), 1);
let compile_command = compile_commands.get_command(0);
let index = clang.create_index_with_display_diagnostics();
let tu = index.parse_translation_unit_from_compile_command(compile_command);
let cursor = tu.create_cursor();
let buf = collect_ast(&cursor);
assert_eq!(buf, oracle);
}