1use std::cmp;
18use std::fmt;
19use std::hash;
20use std::mem;
21use std::slice;
22use std::path::{Path, PathBuf};
23
24use clang_sys::*;
25
26use libc::{c_uint, time_t};
27
28use utility::{self, Nullable};
29use super::{Entity, TranslationUnit};
30use super::token::{Token};
31
32#[derive(Copy, Clone)]
40pub struct File<'tu> {
41 ptr: CXFile,
42 tu: &'tu TranslationUnit<'tu>,
43}
44
45impl<'tu> File<'tu> {
46 #[doc(hidden)]
49 pub fn from_ptr(ptr: CXFile, tu: &'tu TranslationUnit<'tu>) -> File<'tu> {
50 assert!(!ptr.is_null());
51 File { ptr, tu }
52 }
53
54 pub fn get_path(&self) -> PathBuf {
58 unsafe { Path::new(&utility::to_string(clang_getFileName(self.ptr))).into() }
59 }
60
61 pub fn get_time(&self) -> time_t {
63 unsafe { clang_getFileTime(self.ptr) }
64 }
65
66 pub fn get_id(&self) -> (u64, u64, u64) {
68 unsafe {
69 let mut id = mem::MaybeUninit::uninit();
70 clang_getFileUniqueID(self.ptr, id.as_mut_ptr());
71 let id = id.assume_init();
72 (id.data[0] as u64, id.data[1] as u64, id.data[2] as u64)
73 }
74 }
75
76 #[cfg(feature="clang_6_0")]
78 pub fn get_contents(&self) -> Option<String> {
79 use std::ptr;
80 use std::ffi::CStr;
81
82 unsafe {
83 let c = clang_getFileContents(self.tu.ptr, self.ptr, ptr::null_mut());
84 if !c.is_null() {
85 Some(CStr::from_ptr(c).to_str().expect("invalid Rust string").into())
86 } else {
87 None
88 }
89 }
90 }
91
92 pub fn get_module(&self) -> Option<Module<'tu>> {
94 let module = unsafe { clang_getModuleForFile(self.tu.ptr, self.ptr) };
95 module.map(|m| Module::from_ptr(m, self.tu))
96 }
97
98 pub fn get_skipped_ranges(&self) -> Vec<SourceRange<'tu>> {
103 unsafe {
104 let raw = clang_getSkippedRanges(self.tu.ptr, self.ptr);
105 let raws = slice::from_raw_parts((*raw).ranges, (*raw).count as usize);
106 let ranges = raws.iter().map(|r| SourceRange::from_raw(*r, self.tu)).collect();
107 clang_disposeSourceRangeList(raw);
108 ranges
109 }
110 }
111
112 pub fn is_include_guarded(&self) -> bool {
114 unsafe { clang_isFileMultipleIncludeGuarded(self.tu.ptr, self.ptr) != 0 }
115 }
116
117 pub fn get_location(&self, line: u32, column: u32) -> SourceLocation<'tu> {
123 if line == 0 || column == 0 {
124 panic!("`line` or `column` is `0`");
125 }
126
127 let (line, column) = (line, column) as (c_uint, c_uint);
128 let location = unsafe { clang_getLocation(self.tu.ptr, self.ptr, line, column) };
129 SourceLocation::from_raw(location, self.tu)
130 }
131
132 pub fn get_offset_location(&self, offset: u32) -> SourceLocation<'tu> {
134 let offset = offset as c_uint;
135 let location = unsafe { clang_getLocationForOffset(self.tu.ptr, self.ptr, offset) };
136 SourceLocation::from_raw(location, self.tu)
137 }
138
139 pub fn get_includes(&self) -> Vec<Entity<'tu>> {
141 let mut includes = vec![];
142 self.visit_includes(|e, _| {
143 includes.push(e);
144 true
145 });
146 includes
147 }
148
149 pub fn get_references(&self, entity: Entity<'tu>) -> Vec<Entity<'tu>> {
151 let mut references = vec![];
152 self.visit_references(entity, |e, _| {
153 references.push(e);
154 true
155 });
156 references
157 }
158
159 pub fn visit_includes<F: FnMut(Entity<'tu>, SourceRange<'tu>) -> bool>(&self, f: F) -> bool {
162 visit(self.tu, f, |v| unsafe { clang_findIncludesInFile(self.tu.ptr, self.ptr, v) })
163 }
164
165 pub fn visit_references<F: FnMut(Entity<'tu>, SourceRange<'tu>) -> bool>(
168 &self, entity: Entity<'tu>, f: F
169 ) -> bool {
170 visit(self.tu, f, |v| unsafe { clang_findReferencesInFile(entity.raw, self.ptr, v) })
171 }
172}
173
174impl<'tu> fmt::Debug for File<'tu> {
175 fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
176 formatter.debug_struct("File").field("path", &self.get_path()).finish()
177 }
178}
179
180impl<'tu> cmp::PartialEq for File<'tu> {
181 fn eq(&self, other: &File<'tu>) -> bool {
182 self.get_id() == other.get_id()
183 }
184}
185
186impl<'tu> cmp::Eq for File<'tu> { }
187
188impl<'tu> hash::Hash for File<'tu> {
189 fn hash<H: hash::Hasher>(&self, hasher: &mut H) {
190 self.get_id().hash(hasher);
191 }
192}
193
194#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
198pub struct Location<'tu> {
199 pub file: Option<File<'tu>>,
201 pub line: u32,
203 pub column: u32,
205 pub offset: u32,
207}
208
209#[derive(Copy, Clone)]
213pub struct Module<'tu> {
214 ptr: CXModule,
215 tu: &'tu TranslationUnit<'tu>,
216}
217
218impl<'tu> Module<'tu> {
219 #[doc(hidden)]
222 pub fn from_ptr(ptr: CXModule, tu: &'tu TranslationUnit<'tu>) -> Module<'tu> {
223 assert!(!ptr.is_null());
224 Module { ptr, tu }
225 }
226
227 pub fn get_name(&self) -> String {
231 unsafe { utility::to_string(clang_Module_getName(self.ptr)) }
232 }
233
234 pub fn get_full_name(&self) -> String {
236 unsafe { utility::to_string(clang_Module_getFullName(self.ptr)) }
237 }
238
239 pub fn get_parent(&self) -> Option<Module<'tu>> {
241 unsafe { clang_Module_getParent(self.ptr).map(|p| Module::from_ptr(p, self.tu)) }
242 }
243
244 pub fn get_file(&self) -> File<'tu> {
246 unsafe { File::from_ptr(clang_Module_getASTFile(self.ptr), self.tu) }
247 }
248
249 pub fn get_top_level_headers(&self) -> Vec<File<'tu>> {
251 iter!(
252 clang_Module_getNumTopLevelHeaders(self.tu.ptr, self.ptr),
253 clang_Module_getTopLevelHeader(self.tu.ptr, self.ptr),
254 ).map(|h| File::from_ptr(h, self.tu)).collect()
255 }
256
257 pub fn is_system(&self) -> bool {
259 unsafe { clang_Module_isSystem(self.ptr) != 0 }
260 }
261}
262
263impl<'tu> fmt::Debug for Module<'tu> {
264 fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
265 formatter.debug_struct("Module")
266 .field("file", &self.get_file())
267 .field("full_name", &self.get_full_name())
268 .finish()
269 }
270}
271
272impl<'tu> cmp::PartialEq for Module<'tu> {
273 fn eq(&self, other: &Module<'tu>) -> bool {
274 self.get_file() == other.get_file() && self.get_full_name() == other.get_full_name()
275 }
276}
277
278impl<'tu> cmp::Eq for Module<'tu> { }
279
280macro_rules! location {
283 ($function:ident, $location:expr, $tu:expr) => ({
284 fn uninit<T>() -> mem::MaybeUninit<T> { mem::MaybeUninit::uninit() }
285 let (mut file, mut line, mut column, mut offset) = (uninit(), uninit(), uninit(), uninit());
286 $function(
287 $location,
288 file.as_mut_ptr(),
289 line.as_mut_ptr(),
290 column.as_mut_ptr(),
291 offset.as_mut_ptr(),
292 );
293 Location {
294 file: file.assume_init().map(|f| File::from_ptr(f, $tu)),
295 line: line.assume_init() as u32,
296 column: column.assume_init() as u32,
297 offset: offset.assume_init() as u32,
298 }
299 });
300}
301
302#[derive(Copy, Clone)]
304pub struct SourceLocation<'tu> {
305 raw: CXSourceLocation,
306 tu: &'tu TranslationUnit<'tu>,
307}
308
309impl<'tu> SourceLocation<'tu> {
310 #[doc(hidden)]
313 pub fn from_raw(raw: CXSourceLocation, tu: &'tu TranslationUnit<'tu>) -> SourceLocation<'tu> {
314 SourceLocation { raw, tu }
315 }
316
317 pub fn get_expansion_location(&self) -> Location<'tu> {
324 unsafe { location!(clang_getExpansionLocation, self.raw, self.tu) }
325 }
326
327 pub fn get_file_location(&self) -> Location<'tu> {
333 unsafe { location!(clang_getFileLocation, self.raw, self.tu) }
334 }
335
336 pub fn get_presumed_location(&self) -> (String, u32, u32) {
339 unsafe {
340 fn uninit<T>() -> mem::MaybeUninit<T> { mem::MaybeUninit::uninit() }
341 let (mut file, mut line, mut column) = (uninit(), uninit(), uninit());
342 clang_getPresumedLocation(
343 self.raw, file.as_mut_ptr(), line.as_mut_ptr(), column.as_mut_ptr());
344 (
345 utility::to_string(file.assume_init()),
346 line.assume_init() as u32,
347 column.assume_init() as u32
348 )
349 }
350 }
351
352 pub fn get_spelling_location(&self) -> Location<'tu> {
354 unsafe { location!(clang_getSpellingLocation, self.raw, self.tu) }
355 }
356
357 pub fn get_entity(&self) -> Option<Entity<'tu>> {
359 unsafe { clang_getCursor(self.tu.ptr, self.raw).map(|c| Entity::from_raw(c, self.tu)) }
360 }
361
362 pub fn is_in_main_file(&self) -> bool {
364 unsafe { clang_Location_isFromMainFile(self.raw) != 0 }
365 }
366
367 pub fn is_in_system_header(&self) -> bool {
369 unsafe { clang_Location_isInSystemHeader(self.raw) != 0 }
370 }
371}
372
373impl<'tu> fmt::Debug for SourceLocation<'tu> {
374 fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
375 let location = self.get_spelling_location();
376 formatter.debug_struct("SourceLocation")
377 .field("file", &location.file)
378 .field("line", &location.line)
379 .field("column", &location.column)
380 .field("offset", &location.offset)
381 .finish()
382 }
383}
384
385impl<'tu> cmp::PartialEq for SourceLocation<'tu> {
386 fn eq(&self, other: &SourceLocation<'tu>) -> bool {
387 unsafe { clang_equalLocations(self.raw, other.raw) != 0 }
388 }
389}
390
391impl<'tu> cmp::Eq for SourceLocation<'tu> { }
392
393impl<'tu> hash::Hash for SourceLocation<'tu> {
394 fn hash<H: hash::Hasher>(&self, hasher: &mut H) {
395 self.get_spelling_location().hash(hasher)
396 }
397}
398
399#[derive(Copy, Clone)]
403pub struct SourceRange<'tu> {
404 raw: CXSourceRange,
405 tu: &'tu TranslationUnit<'tu>,
406}
407
408impl<'tu> SourceRange<'tu> {
409 #[doc(hidden)]
412 pub fn from_raw(raw: CXSourceRange, tu: &'tu TranslationUnit<'tu>) -> SourceRange<'tu> {
413 SourceRange { raw, tu }
414 }
415
416 pub fn new(start: SourceLocation<'tu>, end: SourceLocation<'tu>) -> SourceRange<'tu> {
418 unsafe { SourceRange::from_raw(clang_getRange(start.raw, end.raw), start.tu) }
419 }
420
421 pub fn get_start(&self) -> SourceLocation<'tu> {
425 unsafe { SourceLocation::from_raw(clang_getRangeStart(self.raw), self.tu) }
426 }
427
428 pub fn get_end(&self) -> SourceLocation<'tu> {
430 unsafe { SourceLocation::from_raw(clang_getRangeEnd(self.raw), self.tu) }
431 }
432
433 pub fn is_in_main_file(&self) -> bool {
435 self.get_start().is_in_main_file()
436 }
437
438 pub fn is_in_system_header(&self) -> bool {
440 self.get_start().is_in_system_header()
441 }
442
443 pub fn tokenize(&self) -> Vec<Token<'tu>> {
445 unsafe {
446 let (mut raw, mut count) = (mem::MaybeUninit::uninit(), mem::MaybeUninit::uninit());
447 clang_tokenize(self.tu.ptr, self.raw, raw.as_mut_ptr(), count.as_mut_ptr());
448 let (raw, count) = (raw.assume_init(), count.assume_init());
449 let raws = slice::from_raw_parts(raw, count as usize);
450 let tokens = raws.iter().map(|t| Token::from_raw(*t, self.tu)).collect();
451 clang_disposeTokens(self.tu.ptr, raw, count);
452 tokens
453 }
454 }
455}
456
457impl<'tu> fmt::Debug for SourceRange<'tu> {
458 fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
459 formatter.debug_struct("SourceRange")
460 .field("start", &self.get_start())
461 .field("end", &self.get_end())
462 .finish()
463 }
464}
465
466impl<'tu> cmp::PartialEq for SourceRange<'tu> {
467 fn eq(&self, other: &SourceRange<'tu>) -> bool {
468 unsafe { clang_equalRanges(self.raw, other.raw) != 0 }
469 }
470}
471
472impl<'tu> cmp::Eq for SourceRange<'tu> { }
473
474impl<'tu> hash::Hash for SourceRange<'tu> {
475 fn hash<H: hash::Hasher>(&self, hasher: &mut H) {
476 self.get_start().hash(hasher);
477 self.get_end().hash(hasher);
478 }
479}
480
481fn visit<'tu, F, G>(tu: &'tu TranslationUnit<'tu>, f: F, g: G) -> bool
486 where F: FnMut(Entity<'tu>, SourceRange<'tu>) -> bool,
487 G: Fn(CXCursorAndRangeVisitor) -> CXResult
488{
489 trait Callback<'tu> {
490 fn call(&mut self, entity: Entity<'tu>, range: SourceRange<'tu>) -> bool;
491 }
492
493 impl<'tu, F: FnMut(Entity<'tu>, SourceRange<'tu>) -> bool> Callback<'tu> for F {
494 fn call(&mut self, entity: Entity<'tu>, range: SourceRange<'tu>) -> bool {
495 self(entity, range)
496 }
497 }
498
499 extern fn visit(data: CXClientData, cursor: CXCursor, range: CXSourceRange) -> CXVisitorResult {
500 unsafe {
501 let &mut (tu, ref mut callback):
502 &mut (&TranslationUnit, Box<dyn Callback>) =
503 &mut *(data as *mut (&TranslationUnit, Box<dyn Callback>));
504
505 if callback.call(Entity::from_raw(cursor, tu), SourceRange::from_raw(range, tu)) {
506 CXVisit_Continue
507 } else {
508 CXVisit_Break
509 }
510 }
511 }
512
513 let mut data = (tu, Box::new(f) as Box<dyn Callback>);
514 let visitor = CXCursorAndRangeVisitor { context: utility::addressof(&mut data), visit: Some(visit) };
515 g(visitor) == CXResult_VisitBreak
516}