clang/
source.rs

1// Copyright 2016 Kyle Mayes
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15//! Source files, locations, and ranges.
16
17use 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//================================================
33// Structs
34//================================================
35
36// File __________________________________________
37
38/// A source file.
39#[derive(Copy, Clone)]
40pub struct File<'tu> {
41    ptr: CXFile,
42    tu: &'tu TranslationUnit<'tu>,
43}
44
45impl<'tu> File<'tu> {
46    //- Constructors -----------------------------
47
48    #[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    //- Accessors --------------------------------
55
56    /// Returns the absolute path to this file.
57    pub fn get_path(&self) -> PathBuf {
58        unsafe { Path::new(&utility::to_string(clang_getFileName(self.ptr))).into() }
59    }
60
61    /// Returns the last modification time for this file.
62    pub fn get_time(&self) -> time_t {
63        unsafe { clang_getFileTime(self.ptr) }
64    }
65
66    /// Returns a unique identifier for this file.
67    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    /// Returns the contents of this file, if this file has been loaded.
77    #[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    /// Returns the module containing this file, if any.
93    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    /// Returns the source ranges in this file that were skipped by the preprocessor.
99    ///
100    /// This will always return an empty `Vec` if the translation unit that contains this file was
101    /// not constructed with a detailed preprocessing record.
102    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    /// Returns whether this file is guarded against multiple inclusions.
113    pub fn is_include_guarded(&self) -> bool {
114        unsafe { clang_isFileMultipleIncludeGuarded(self.tu.ptr, self.ptr) != 0 }
115    }
116
117    /// Returns the source location at the supplied line and column in this file.
118    ///
119    /// # Panics
120    ///
121    /// * `line` or `column` is `0`
122    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    /// Returns the source location at the supplied character offset in this file.
133    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    /// Returns the inclusion directives in this file.
140    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    /// Returns the references to the supplied entity in this file.
150    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    /// Visits the inclusion directives in this file and returns whether visitation was ended by the
160    /// callback returning `false`.
161    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    /// Visits the references to the supplied entity in this file and returns whether visitation was
166    /// ended by the callback returning `false`.
167    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// Location ______________________________________
195
196/// The file, line, column, and character offset of a source location.
197#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
198pub struct Location<'tu> {
199    /// The file of the source location, if it has any.
200    pub file: Option<File<'tu>>,
201    /// The line of the source location.
202    pub line: u32,
203    /// The column of the source location.
204    pub column: u32,
205    /// The character offset of the source location.
206    pub offset: u32,
207}
208
209// Module ________________________________________
210
211/// A collection of headers.
212#[derive(Copy, Clone)]
213pub struct Module<'tu> {
214    ptr: CXModule,
215    tu: &'tu TranslationUnit<'tu>,
216}
217
218impl<'tu> Module<'tu> {
219    //- Constructors -----------------------------
220
221    #[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    //- Accessors --------------------------------
228
229    /// Returns the name of this module (e.g., `vector` for the `std.vector` module).
230    pub fn get_name(&self) -> String {
231        unsafe { utility::to_string(clang_Module_getName(self.ptr)) }
232    }
233
234    /// Returns the full name of this module (e.g., `std.vector` for the `std.vector` module).
235    pub fn get_full_name(&self) -> String {
236        unsafe { utility::to_string(clang_Module_getFullName(self.ptr)) }
237    }
238
239    /// Returns the parent of this module, if any.
240    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    /// Returns the AST file this module came from.
245    pub fn get_file(&self) -> File<'tu> {
246        unsafe { File::from_ptr(clang_Module_getASTFile(self.ptr), self.tu) }
247    }
248
249    /// Returns the top-level headers in this module.
250    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    /// Returns whether this module is a system module.
258    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
280// SourceLocation ________________________________
281
282macro_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/// A location in a source file.
303#[derive(Copy, Clone)]
304pub struct SourceLocation<'tu> {
305    raw: CXSourceLocation,
306    tu: &'tu TranslationUnit<'tu>,
307}
308
309impl<'tu> SourceLocation<'tu> {
310    //- Constructors -----------------------------
311
312    #[doc(hidden)]
313    pub fn from_raw(raw: CXSourceLocation, tu: &'tu TranslationUnit<'tu>) -> SourceLocation<'tu> {
314        SourceLocation { raw, tu }
315    }
316
317    //- Accessors --------------------------------
318
319    /// Returns the file, line, column and character offset of this source location.
320    ///
321    /// If this source location is inside a macro expansion, the location of the macro expansion is
322    /// returned instead.
323    pub fn get_expansion_location(&self) -> Location<'tu> {
324        unsafe { location!(clang_getExpansionLocation, self.raw, self.tu) }
325    }
326
327    /// Returns the file, line, column and character offset of this source location.
328    ///
329    /// If this source location is inside a macro expansion, the location of the macro expansion is
330    /// returned instead unless this source location is inside a macro argument. In that case, the
331    /// location of the macro argument is returned.
332    pub fn get_file_location(&self) -> Location<'tu> {
333        unsafe { location!(clang_getFileLocation, self.raw, self.tu) }
334    }
335
336    /// Returns the file path, line, and column of this source location taking line directives into
337    /// account.
338    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    /// Returns the file, line, column and character offset of this source location.
353    pub fn get_spelling_location(&self) -> Location<'tu> {
354        unsafe { location!(clang_getSpellingLocation, self.raw, self.tu) }
355    }
356
357    /// Returns the AST entity at this source location, if any.
358    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    /// Returns whether this source location is in the main file of its translation unit.
363    pub fn is_in_main_file(&self) -> bool {
364        unsafe { clang_Location_isFromMainFile(self.raw) != 0 }
365    }
366
367    /// Returns whether this source location is in a system header.
368    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// SourceRange ___________________________________
400
401/// A half-open range in a source file.
402#[derive(Copy, Clone)]
403pub struct SourceRange<'tu> {
404    raw: CXSourceRange,
405    tu: &'tu TranslationUnit<'tu>,
406}
407
408impl<'tu> SourceRange<'tu> {
409    //- Constructors -----------------------------
410
411    #[doc(hidden)]
412    pub fn from_raw(raw: CXSourceRange, tu: &'tu TranslationUnit<'tu>) -> SourceRange<'tu> {
413        SourceRange { raw, tu }
414    }
415
416    /// Constructs a new `SourceRange` that spans [`start`, `end`).
417    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    //- Accessors --------------------------------
422
423    /// Returns the inclusive start of this source range.
424    pub fn get_start(&self) -> SourceLocation<'tu> {
425        unsafe { SourceLocation::from_raw(clang_getRangeStart(self.raw), self.tu) }
426    }
427
428    /// Returns the exclusive end of this source range.
429    pub fn get_end(&self) -> SourceLocation<'tu> {
430        unsafe { SourceLocation::from_raw(clang_getRangeEnd(self.raw), self.tu) }
431    }
432
433    /// Returns whether this source range is in the main file of its translation unit.
434    pub fn is_in_main_file(&self) -> bool {
435        self.get_start().is_in_main_file()
436    }
437
438    /// Returns whether this source range is in a system header.
439    pub fn is_in_system_header(&self) -> bool {
440        self.get_start().is_in_system_header()
441    }
442
443    /// Tokenizes the source code covered by this source range and returns the resulting tokens.
444    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
481//================================================
482// Functions
483//================================================
484
485fn 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}