summaryrefslogtreecommitdiff
path: root/src/clang.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/clang.rs')
-rw-r--r--src/clang.rs1371
1 files changed, 1371 insertions, 0 deletions
diff --git a/src/clang.rs b/src/clang.rs
new file mode 100644
index 00000000..491aaa07
--- /dev/null
+++ b/src/clang.rs
@@ -0,0 +1,1371 @@
+//! A higher level Clang API built on top of the generated bindings in the
+//! `clang_sys` module.
+
+#![allow(non_upper_case_globals, dead_code)]
+
+
+use cexpr;
+use clang_sys::*;
+use std::{mem, ptr, slice};
+use std::ffi::{CStr, CString};
+use std::fmt;
+use std::hash::Hash;
+use std::hash::Hasher;
+use std::os::raw::{c_char, c_int, c_uint, c_ulong};
+
+/// A cursor into the Clang AST, pointing to an AST node.
+///
+/// We call the AST node pointed to by the cursor the cursor's "referent".
+#[derive(Copy, Clone)]
+pub struct Cursor {
+ x: CXCursor,
+}
+
+impl fmt::Debug for Cursor {
+ fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
+ write!(fmt,
+ "Cursor({} kind: {}, loc: {}, usr: {:?})",
+ self.spelling(),
+ kind_to_str(self.kind()),
+ self.location(),
+ self.usr())
+ }
+}
+
+impl Cursor {
+ /// Get the Unified Symbol Resolution for this cursor's referent, if
+ /// available.
+ ///
+ /// The USR can be used to compare entities across translation units.
+ pub fn usr(&self) -> Option<String> {
+ let s = unsafe { cxstring_into_string(clang_getCursorUSR(self.x)) };
+ if s.is_empty() { None } else { Some(s) }
+ }
+
+ /// Is this cursor's referent a declaration?
+ pub fn is_declaration(&self) -> bool {
+ unsafe { clang_isDeclaration(self.kind()) != 0 }
+ }
+
+ /// Get the null cursor, which has no referent.
+ pub fn null() -> Self {
+ Cursor {
+ x: unsafe { clang_getNullCursor() },
+ }
+ }
+
+ /// Get this cursor's referent's spelling.
+ pub fn spelling(&self) -> String {
+ unsafe { cxstring_into_string(clang_getCursorSpelling(self.x)) }
+ }
+
+ /// Get this cursor's referent's display name.
+ ///
+ /// This is not necessarily a valid identifier. It includes extra
+ /// information, such as parameters for a function, etc.
+ pub fn display_name(&self) -> String {
+ unsafe { cxstring_into_string(clang_getCursorDisplayName(self.x)) }
+ }
+
+ /// Get the mangled name of this cursor's referent.
+ pub fn mangling(&self) -> String {
+ unsafe { cxstring_into_string(clang_Cursor_getMangling(self.x)) }
+ }
+
+ /// Get the `Cursor` for this cursor's referent's lexical parent.
+ ///
+ /// The lexical parent is the parent of the definition. The semantic parent
+ /// is the parent of the declaration. Generally, the lexical parent doesn't
+ /// have any effect on semantics, while the semantic parent does.
+ ///
+ /// In the following snippet, the `Foo` class would be the semantic parent
+ /// of the out-of-line `method` definition, while the lexical parent is the
+ /// translation unit.
+ ///
+ /// ```c++
+ /// class Foo {
+ /// void method();
+ /// };
+ ///
+ /// void Foo::method() { /* ... */ }
+ /// ```
+ pub fn lexical_parent(&self) -> Cursor {
+ unsafe {
+ Cursor {
+ x: clang_getCursorLexicalParent(self.x),
+ }
+ }
+ }
+
+ /// Get the referent's semantic parent, if one is available.
+ ///
+ /// See documentation for `lexical_parent` for details on semantic vs
+ /// lexical parents.
+ pub fn fallible_semantic_parent(&self) -> Option<Cursor> {
+ let sp = unsafe {
+ Cursor {
+ x: clang_getCursorSemanticParent(self.x),
+ }
+ };
+ if sp == *self || !sp.is_valid() {
+ return None;
+ }
+ Some(sp)
+ }
+
+ /// Get the referent's semantic parent.
+ ///
+ /// See documentation for `lexical_parent` for details on semantic vs
+ /// lexical parents.
+ pub fn semantic_parent(&self) -> Cursor {
+ self.fallible_semantic_parent().unwrap()
+ }
+
+ /// Return the number of template arguments used by this cursor's referent,
+ /// if the referent is either a template specialization or
+ /// declaration. Returns -1 otherwise.
+ ///
+ /// NOTE: This may not return `Some` for some non-fully specialized
+ /// templates, see #193 and #194.
+ pub fn num_template_args(&self) -> Option<u32> {
+ let n: c_int = unsafe { clang_Cursor_getNumTemplateArguments(self.x) };
+
+ if n >= 0 {
+ Some(n as u32)
+ } else {
+ debug_assert_eq!(n, -1);
+ None
+ }
+ }
+
+ /// Get a cursor pointing to this referent's containing translation unit.
+ ///
+ /// Note that we shouldn't create a `TranslationUnit` struct here, because
+ /// bindgen assumes there will only be one of them alive at a time, and
+ /// disposes it on drop. That can change if this would be required, but I
+ /// think we can survive fine without it.
+ pub fn translation_unit(&self) -> Cursor {
+ assert!(self.is_valid());
+ unsafe {
+ let tu = clang_Cursor_getTranslationUnit(self.x);
+ let cursor = Cursor {
+ x: clang_getTranslationUnitCursor(tu),
+ };
+ assert!(cursor.is_valid());
+ cursor
+ }
+ }
+
+ /// Is the referent a top level construct?
+ pub fn is_toplevel(&self) -> bool {
+ let mut semantic_parent = self.fallible_semantic_parent();
+
+ while semantic_parent.is_some() &&
+ (semantic_parent.unwrap().kind() == CXCursor_Namespace ||
+ semantic_parent.unwrap().kind() == CXCursor_NamespaceAlias ||
+ semantic_parent.unwrap().kind() == CXCursor_NamespaceRef) {
+ semantic_parent = semantic_parent.unwrap()
+ .fallible_semantic_parent();
+ }
+
+ let tu = self.translation_unit();
+ // Yes, this can happen with, e.g., macro definitions.
+ semantic_parent == tu.fallible_semantic_parent()
+ }
+
+ /// There are a few kinds of types that we need to treat specially, mainly
+ /// not tracking the type declaration but the location of the cursor, given
+ /// clang doesn't expose a proper declaration for these types.
+ pub fn is_template_like(&self) -> bool {
+ match self.kind() {
+ CXCursor_ClassTemplate |
+ CXCursor_ClassTemplatePartialSpecialization |
+ CXCursor_TypeAliasTemplateDecl => true,
+ _ => false,
+ }
+ }
+
+ /// Get the kind of referent this cursor is pointing to.
+ pub fn kind(&self) -> CXCursorKind {
+ unsafe { clang_getCursorKind(self.x) }
+ }
+
+ /// Is the referent an anonymous record definition?
+ pub fn is_anonymous(&self) -> bool {
+ unsafe { clang_Cursor_isAnonymous(self.x) != 0 }
+ }
+
+ /// Is the referent a template specialization?
+ pub fn is_template(&self) -> bool {
+ self.specialized().is_some()
+ }
+
+ /// Is the referent a fully specialized template specialization without any
+ /// remaining free template arguments?
+ pub fn is_fully_specialized_template(&self) -> bool {
+ self.is_template() && self.num_template_args().unwrap_or(0) > 0
+ }
+
+ /// Is the referent a template specialization that still has remaining free
+ /// template arguments?
+ pub fn is_in_non_fully_specialized_template(&self) -> bool {
+ if self.is_toplevel() {
+ return false;
+ }
+ let parent = self.semantic_parent();
+ (parent.is_template() && !parent.is_fully_specialized_template()) ||
+ parent.is_in_non_fully_specialized_template()
+ }
+
+ /// Is this cursor pointing a valid referent?
+ pub fn is_valid(&self) -> bool {
+ unsafe { clang_isInvalid(self.kind()) == 0 }
+ }
+
+ /// Get the source location for the referent.
+ pub fn location(&self) -> SourceLocation {
+ unsafe {
+ SourceLocation {
+ x: clang_getCursorLocation(self.x),
+ }
+ }
+ }
+
+ /// Get the source location range for the referent.
+ pub fn extent(&self) -> CXSourceRange {
+ unsafe { clang_getCursorExtent(self.x) }
+ }
+
+ /// Get the raw declaration comment for this referent, if one exists.
+ pub fn raw_comment(&self) -> Option<String> {
+ let s = unsafe {
+ cxstring_into_string(clang_Cursor_getRawCommentText(self.x))
+ };
+ if s.is_empty() { None } else { Some(s) }
+ }
+
+ /// Get the referent's parsed comment.
+ pub fn comment(&self) -> Comment {
+ unsafe {
+ Comment {
+ x: clang_Cursor_getParsedComment(self.x),
+ }
+ }
+ }
+
+ /// Get the referent's type.
+ pub fn cur_type(&self) -> Type {
+ unsafe {
+ Type {
+ x: clang_getCursorType(self.x),
+ }
+ }
+ }
+
+ /// Given that this cursor's referent is a reference to another type, or is
+ /// a declaration, get the cursor pointing to the referenced type or type of
+ /// the declared thing.
+ pub fn definition(&self) -> Option<Cursor> {
+ unsafe {
+ let ret = Cursor {
+ x: clang_getCursorDefinition(self.x),
+ };
+
+ if ret.is_valid() { Some(ret) } else { None }
+ }
+ }
+
+ /// Given that this cursor's referent is reference type, get the cursor
+ /// pointing to the referenced type.
+ pub fn referenced(&self) -> Option<Cursor> {
+ unsafe {
+ let ret = Cursor {
+ x: clang_getCursorReferenced(self.x),
+ };
+
+ if ret.is_valid() { Some(ret) } else { None }
+ }
+ }
+
+ /// Get the canonical cursor for this referent.
+ ///
+ /// Many types can be declared multiple times before finally being properly
+ /// defined. This method allows us to get the canonical cursor for the
+ /// referent type.
+ pub fn canonical(&self) -> Cursor {
+ unsafe {
+ Cursor {
+ x: clang_getCanonicalCursor(self.x),
+ }
+ }
+ }
+
+ /// Given that this cursor points to a template specialization, get a cursor
+ /// pointing to the template definition that is being specialized.
+ pub fn specialized(&self) -> Option<Cursor> {
+ unsafe {
+ let ret = Cursor {
+ x: clang_getSpecializedCursorTemplate(self.x),
+ };
+ if ret.is_valid() { Some(ret) } else { None }
+ }
+ }
+
+ /// Assuming that this cursor's referent is a template declaration, get the
+ /// kind of cursor that would be generated for its specializations.
+ pub fn template_kind(&self) -> CXCursorKind {
+ unsafe { clang_getTemplateCursorKind(self.x) }
+ }
+
+ /// Traverse this cursor's referent and its children.
+ ///
+ /// Call the given function on each AST node traversed.
+ pub fn visit<Visitor>(&self, mut visitor: Visitor)
+ where Visitor: FnMut(Cursor) -> CXChildVisitResult,
+ {
+ unsafe {
+ clang_visitChildren(self.x,
+ visit_children::<Visitor>,
+ mem::transmute(&mut visitor));
+ }
+ }
+
+ /// Returns whether the given location contains a cursor with the given
+ /// kind in the first level of nesting underneath (doesn't look
+ /// recursively).
+ pub fn contains_cursor(&self, kind: CXCursorKind) -> bool {
+ let mut found = false;
+
+ self.visit(|c| {
+ if c.kind() == kind {
+ found = true;
+ CXChildVisit_Break
+ } else {
+ CXChildVisit_Continue
+ }
+ });
+
+ found
+ }
+
+ /// Is the referent an inlined function?
+ pub fn is_inlined_function(&self) -> bool {
+ clang_Cursor_isFunctionInlined::is_loaded() &&
+ unsafe { clang_Cursor_isFunctionInlined(self.x) != 0 }
+ }
+
+ /// Get the width of this cursor's referent bit field, or `None` if the
+ /// referent is not a bit field.
+ pub fn bit_width(&self) -> Option<u32> {
+ unsafe {
+ let w = clang_getFieldDeclBitWidth(self.x);
+ if w == -1 { None } else { Some(w as u32) }
+ }
+ }
+
+ /// Get the integer representation type used to hold this cursor's referent
+ /// enum type.
+ pub fn enum_type(&self) -> Option<Type> {
+ unsafe {
+ let t = Type {
+ x: clang_getEnumDeclIntegerType(self.x),
+ };
+ if t.is_valid() { Some(t) } else { None }
+ }
+ }
+
+ /// Get the signed constant value for this cursor's enum variant referent.
+ ///
+ /// Returns None if the cursor's referent is not an enum variant.
+ pub fn enum_val_signed(&self) -> Option<i64> {
+ unsafe {
+ if self.kind() == CXCursor_EnumConstantDecl {
+ Some(clang_getEnumConstantDeclValue(self.x) as i64)
+ } else {
+ None
+ }
+ }
+ }
+
+ /// Get the unsigned constant value for this cursor's enum variant referent.
+ ///
+ /// Returns None if the cursor's referent is not an enum variant.
+ pub fn enum_val_unsigned(&self) -> Option<u64> {
+ unsafe {
+ if self.kind() == CXCursor_EnumConstantDecl {
+ Some(clang_getEnumConstantDeclUnsignedValue(self.x) as u64)
+ } else {
+ None
+ }
+ }
+ }
+
+ /// Given that this cursor's referent is a `typedef`, get the `Type` that is
+ /// being aliased.
+ pub fn typedef_type(&self) -> Option<Type> {
+ let inner = Type {
+ x: unsafe { clang_getTypedefDeclUnderlyingType(self.x) },
+ };
+
+ if inner.is_valid() { Some(inner) } else { None }
+ }
+
+ /// Get the linkage kind for this cursor's referent.
+ ///
+ /// This only applies to functions and variables.
+ pub fn linkage(&self) -> CXLinkageKind {
+ unsafe { clang_getCursorLinkage(self.x) }
+ }
+
+ /// Get the visibility of this cursor's referent.
+ pub fn visibility(&self) -> CXVisibilityKind {
+ unsafe { clang_getCursorVisibility(self.x) }
+ }
+
+ /// Given that this cursor's referent is a function, return cursors to its
+ /// parameters.
+ pub fn args(&self) -> Option<Vec<Cursor>> {
+ // XXX: We might want to use and keep num_args
+ // match self.kind() {
+ // CXCursor_FunctionDecl |
+ // CXCursor_CXXMethod => {
+ unsafe {
+ let w = clang_Cursor_getNumArguments(self.x);
+ if w == -1 {
+ None
+ } else {
+ let num = w as u32;
+
+ let mut args = vec![];
+ for i in 0..num {
+ args.push(Cursor {
+ x: clang_Cursor_getArgument(self.x, i as c_uint),
+ });
+ }
+ Some(args)
+ }
+ }
+ }
+
+ /// Given that this cursor's referent is a function/method call or
+ /// declaration, return the number of arguments it takes.
+ ///
+ /// Returns -1 if the cursor's referent is not a function/method call or
+ /// declaration.
+ pub fn num_args(&self) -> Result<u32, ()> {
+ unsafe {
+ let w = clang_Cursor_getNumArguments(self.x);
+ if w == -1 { Err(()) } else { Ok(w as u32) }
+ }
+ }
+
+ /// Get the access specifier for this cursor's referent.
+ pub fn access_specifier(&self) -> CX_CXXAccessSpecifier {
+ unsafe { clang_getCXXAccessSpecifier(self.x) }
+ }
+
+ /// Is this cursor's referent a field declaration that is marked as
+ /// `mutable`?
+ pub fn is_mutable_field(&self) -> bool {
+ unsafe { clang_CXXField_isMutable(self.x) != 0 }
+ }
+
+ /// Is this cursor's referent a member function that is declared `static`?
+ pub fn method_is_static(&self) -> bool {
+ unsafe { clang_CXXMethod_isStatic(self.x) != 0 }
+ }
+
+ /// Is this cursor's referent a member function that is declared `const`?
+ pub fn method_is_const(&self) -> bool {
+ unsafe { clang_CXXMethod_isConst(self.x) != 0 }
+ }
+
+ /// Is this cursor's referent a member function that is declared `const`?
+ pub fn method_is_virtual(&self) -> bool {
+ unsafe { clang_CXXMethod_isVirtual(self.x) != 0 }
+ }
+
+ /// Is this cursor's referent a struct or class with virtual members?
+ pub fn is_virtual_base(&self) -> bool {
+ unsafe { clang_isVirtualBase(self.x) != 0 }
+ }
+
+ /// Try to evaluate this cursor.
+ pub fn evaluate(&self) -> Option<EvalResult> {
+ EvalResult::new(*self)
+ }
+}
+
+extern "C" fn visit_children<Visitor>(cur: CXCursor,
+ _parent: CXCursor,
+ data: CXClientData)
+ -> CXChildVisitResult
+ where Visitor: FnMut(Cursor) -> CXChildVisitResult,
+{
+ let func: &mut Visitor = unsafe { mem::transmute(data) };
+ let child = Cursor {
+ x: cur,
+ };
+
+ (*func)(child)
+}
+
+impl PartialEq for Cursor {
+ fn eq(&self, other: &Cursor) -> bool {
+ unsafe { clang_equalCursors(self.x, other.x) == 1 }
+ }
+}
+
+impl Eq for Cursor {}
+
+impl Hash for Cursor {
+ fn hash<H: Hasher>(&self, state: &mut H) {
+ unsafe { clang_hashCursor(self.x) }.hash(state)
+ }
+}
+
+/// The type of a node in clang's AST.
+#[derive(Clone)]
+pub struct Type {
+ x: CXType,
+}
+
+impl PartialEq for Type {
+ fn eq(&self, other: &Self) -> bool {
+ unsafe { clang_equalTypes(self.x, other.x) != 0 }
+ }
+}
+
+impl Eq for Type {}
+
+impl fmt::Debug for Type {
+ fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
+ write!(fmt,
+ "Type({}, kind: {}, decl: {:?}, canon: {:?})",
+ self.spelling(),
+ type_to_str(self.kind()),
+ self.declaration(),
+ self.declaration().canonical())
+ }
+}
+
+/// An error about the layout of a struct, class, or type.
+#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
+pub enum LayoutError {
+ /// Asked for the layout of an invalid type.
+ Invalid,
+ /// Asked for the layout of an incomplete type.
+ Incomplete,
+ /// Asked for the layout of a dependent type.
+ Dependent,
+ /// Asked for the layout of a type that does not have constant size.
+ NotConstantSize,
+ /// Asked for the layout of a field in a type that does not have such a
+ /// field.
+ InvalidFieldName,
+ /// An unknown layout error.
+ Unknown,
+}
+
+impl ::std::convert::From<i32> for LayoutError {
+ fn from(val: i32) -> Self {
+ use self::LayoutError::*;
+ let val = match CXTypeLayoutError::from_raw(val) {
+ Some(val) => val,
+ None => return Unknown,
+ };
+
+ match val {
+ CXTypeLayoutError_Invalid => Invalid,
+ CXTypeLayoutError_Incomplete => Incomplete,
+ CXTypeLayoutError_Dependent => Dependent,
+ CXTypeLayoutError_NotConstantSize => NotConstantSize,
+ CXTypeLayoutError_InvalidFieldName => InvalidFieldName,
+ _ => Unknown,
+ }
+ }
+}
+
+impl Type {
+ /// Get this type's kind.
+ pub fn kind(&self) -> CXTypeKind {
+ self.x.kind
+ }
+
+ /// Get a cursor pointing to this type's declaration.
+ pub fn declaration(&self) -> Cursor {
+ unsafe {
+ Cursor {
+ x: clang_getTypeDeclaration(self.x),
+ }
+ }
+ }
+
+ /// Get a raw display name for this type.
+ pub fn spelling(&self) -> String {
+ unsafe { cxstring_into_string(clang_getTypeSpelling(self.x)) }
+ }
+
+ /// Is this type const qualified?
+ pub fn is_const(&self) -> bool {
+ unsafe { clang_isConstQualifiedType(self.x) != 0 }
+ }
+
+ /// What is the size of this type? Paper over invalid types by returning `0`
+ /// for them.
+ pub fn size(&self) -> usize {
+ unsafe {
+ let val = clang_Type_getSizeOf(self.x);
+ if val < 0 { 0 } else { val as usize }
+ }
+ }
+
+ /// What is the size of this type?
+ pub fn fallible_size(&self) -> Result<usize, LayoutError> {
+ let val = unsafe { clang_Type_getSizeOf(self.x) };
+ if val < 0 {
+ Err(LayoutError::from(val as i32))
+ } else {
+ Ok(val as usize)
+ }
+ }
+
+ /// What is the alignment of this type? Paper over invalid types by
+ /// returning `0`.
+ pub fn align(&self) -> usize {
+ unsafe {
+ let val = clang_Type_getAlignOf(self.x);
+ if val < 0 { 0 } else { val as usize }
+ }
+ }
+
+ /// What is the alignment of this type?
+ pub fn fallible_align(&self) -> Result<usize, LayoutError> {
+ unsafe {
+ let val = clang_Type_getAlignOf(self.x);
+ if val < 0 {
+ Err(LayoutError::from(val as i32))
+ } else {
+ Ok(val as usize)
+ }
+ }
+ }
+
+ /// Get the layout for this type, or an error describing why it does not
+ /// have a valid layout.
+ pub fn fallible_layout(&self) -> Result<::ir::layout::Layout, LayoutError> {
+ use ir::layout::Layout;
+ let size = try!(self.fallible_size());
+ let align = try!(self.fallible_align());
+ Ok(Layout::new(size, align))
+ }
+
+ /// If this type is a class template specialization, return its
+ /// template arguments. Otherwise, return None.
+ pub fn template_args(&self) -> Option<TypeTemplateArgIterator> {
+ let n = unsafe { clang_Type_getNumTemplateArguments(self.x) };
+ if n >= 0 {
+ Some(TypeTemplateArgIterator {
+ x: self.x,
+ length: n as u32,
+ index: 0,
+ })
+ } else {
+ debug_assert_eq!(n, -1);
+ None
+ }
+ }
+
+ /// Given that this type is a pointer type, return the type that it points
+ /// to.
+ pub fn pointee_type(&self) -> Option<Type> {
+ match self.kind() {
+ CXType_Pointer |
+ CXType_RValueReference |
+ CXType_LValueReference |
+ CXType_MemberPointer => {
+ let ret = Type {
+ x: unsafe { clang_getPointeeType(self.x) },
+ };
+ debug_assert!(ret.is_valid());
+ Some(ret)
+ }
+ _ => None,
+ }
+ }
+
+ /// Given that this type is an array, vector, or complex type, return the
+ /// type of its elements.
+ pub fn elem_type(&self) -> Option<Type> {
+ let current_type = Type {
+ x: unsafe { clang_getElementType(self.x) },
+ };
+ if current_type.is_valid() {
+ Some(current_type)
+ } else {
+ None
+ }
+ }
+
+ /// Given that this type is an array or vector type, return its number of
+ /// elements.
+ pub fn num_elements(&self) -> Option<usize> {
+ let num_elements_returned = unsafe { clang_getNumElements(self.x) };
+ if num_elements_returned != -1 {
+ Some(num_elements_returned as usize)
+ } else {
+ None
+ }
+ }
+
+ /// Get the canonical version of this type. This sees through `typdef`s and
+ /// aliases to get the underlying, canonical type.
+ pub fn canonical_type(&self) -> Type {
+ unsafe {
+ Type {
+ x: clang_getCanonicalType(self.x),
+ }
+ }
+ }
+
+ /// Is this type a variadic function type?
+ pub fn is_variadic(&self) -> bool {
+ unsafe { clang_isFunctionTypeVariadic(self.x) != 0 }
+ }
+
+ /// Given that this type is a function type, get the type of its return
+ /// value.
+ pub fn ret_type(&self) -> Option<Type> {
+ let rt = Type {
+ x: unsafe { clang_getResultType(self.x) },
+ };
+ if rt.is_valid() { Some(rt) } else { None }
+ }
+
+ /// Given that this type is a function type, get its calling convention. If
+ /// this is not a function type, `CXCallingConv_Invalid` is returned.
+ pub fn call_conv(&self) -> CXCallingConv {
+ unsafe { clang_getFunctionTypeCallingConv(self.x) }
+ }
+
+ /// For elaborated types (types which use `class`, `struct`, or `union` to
+ /// disambiguate types from local bindings), get the underlying type.
+ pub fn named(&self) -> Type {
+ unsafe {
+ Type {
+ x: clang_Type_getNamedType(self.x),
+ }
+ }
+ }
+
+ /// Is this a valid type?
+ pub fn is_valid(&self) -> bool {
+ self.kind() != CXType_Invalid
+ }
+
+ /// Is this a valid and exposed type?
+ pub fn is_valid_and_exposed(&self) -> bool {
+ self.is_valid() && self.kind() != CXType_Unexposed
+ }
+
+ /// Is this type a fully specialized template?
+ pub fn is_fully_specialized_template(&self) -> bool {
+ // Yep, the spelling of this containing type-parameter is extremely
+ // nasty... But can happen in <type_traits>. Unfortunately I couldn't
+ // reduce it enough :(
+ !self.spelling().contains("type-parameter") &&
+ self.template_args()
+ .map_or(false, |mut args| {
+ args.len() > 0 &&
+ !args.any(|t| t.spelling().contains("type-parameter"))
+ })
+ }
+}
+
+/// An iterator for a type's template arguments.
+pub struct TypeTemplateArgIterator {
+ x: CXType,
+ length: u32,
+ index: u32,
+}
+
+impl Iterator for TypeTemplateArgIterator {
+ type Item = Type;
+ fn next(&mut self) -> Option<Type> {
+ if self.index < self.length {
+ let idx = self.index as c_uint;
+ self.index += 1;
+ Some(Type {
+ x: unsafe { clang_Type_getTemplateArgumentAsType(self.x, idx) },
+ })
+ } else {
+ None
+ }
+ }
+}
+
+impl ExactSizeIterator for TypeTemplateArgIterator {
+ fn len(&self) -> usize {
+ assert!(self.index <= self.length);
+ (self.length - self.index) as usize
+ }
+}
+
+/// A `SourceLocation` is a file, line, column, and byte offset location for
+/// some source text.
+pub struct SourceLocation {
+ x: CXSourceLocation,
+}
+
+impl SourceLocation {
+ /// Get the (file, line, column, byte offset) tuple for this source
+ /// location.
+ pub fn location(&self) -> (File, usize, usize, usize) {
+ unsafe {
+ let mut file = mem::zeroed();
+ let mut line = 0;
+ let mut col = 0;
+ let mut off = 0;
+ clang_getSpellingLocation(self.x,
+ &mut file,
+ &mut line,
+ &mut col,
+ &mut off);
+ (File {
+ x: file,
+ },
+ line as usize,
+ col as usize,
+ off as usize)
+ }
+ }
+}
+
+impl fmt::Display for SourceLocation {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ let (file, line, col, _) = self.location();
+ if let Some(name) = file.name() {
+ write!(f, "{}:{}:{}", name, line, col)
+ } else {
+ "builtin definitions".fmt(f)
+ }
+ }
+}
+
+/// A comment in the source text.
+///
+/// Comments are sort of parsed by Clang, and have a tree structure.
+pub struct Comment {
+ x: CXComment,
+}
+
+impl Comment {
+ /// What kind of comment is this?
+ pub fn kind(&self) -> CXCommentKind {
+ unsafe { clang_Comment_getKind(self.x) }
+ }
+
+ /// Get this comment's children comment
+ pub fn get_children(&self) -> CommentChildrenIterator {
+ CommentChildrenIterator {
+ parent: self.x,
+ length: unsafe { clang_Comment_getNumChildren(self.x) },
+ index: 0,
+ }
+ }
+
+ /// Given that this comment is the start or end of an HTML tag, get its tag
+ /// name.
+ pub fn get_tag_name(&self) -> String {
+ unsafe { cxstring_into_string(clang_HTMLTagComment_getTagName(self.x)) }
+ }
+
+ /// Given that this comment is an HTML start tag, get its attributes.
+ pub fn get_tag_attrs(&self) -> CommentAttributesIterator {
+ CommentAttributesIterator {
+ x: self.x,
+ length: unsafe { clang_HTMLStartTag_getNumAttrs(self.x) },
+ index: 0,
+ }
+ }
+}
+
+/// An iterator for a comment's children
+pub struct CommentChildrenIterator {
+ parent: CXComment,
+ length: c_uint,
+ index: c_uint,
+}
+
+impl Iterator for CommentChildrenIterator {
+ type Item = Comment;
+ fn next(&mut self) -> Option<Comment> {
+ if self.index < self.length {
+ let idx = self.index;
+ self.index += 1;
+ Some(Comment {
+ x: unsafe { clang_Comment_getChild(self.parent, idx) },
+ })
+ } else {
+ None
+ }
+ }
+}
+
+/// An HTML start tag comment attribute
+pub struct CommentAttribute {
+ /// HTML start tag attribute name
+ pub name: String,
+ /// HTML start tag attribute value
+ pub value: String,
+}
+
+/// An iterator for a comment's attributes
+pub struct CommentAttributesIterator {
+ x: CXComment,
+ length: c_uint,
+ index: c_uint,
+}
+
+impl Iterator for CommentAttributesIterator {
+ type Item = CommentAttribute;
+ fn next(&mut self) -> Option<CommentAttribute> {
+ if self.index < self.length {
+ let idx = self.index;
+ self.index += 1;
+ Some(CommentAttribute {
+ name: unsafe {
+ cxstring_into_string(
+ clang_HTMLStartTag_getAttrName(self.x, idx))
+ },
+ value: unsafe {
+ cxstring_into_string(
+ clang_HTMLStartTag_getAttrValue(self.x, idx))
+ },
+ })
+ } else {
+ None
+ }
+ }
+}
+
+/// A source file.
+pub struct File {
+ x: CXFile,
+}
+
+impl File {
+ /// Get the name of this source file.
+ pub fn name(&self) -> Option<String> {
+ if self.x.is_null() {
+ return None;
+ }
+ Some(unsafe { cxstring_into_string(clang_getFileName(self.x)) })
+ }
+}
+
+fn cxstring_into_string(s: CXString) -> String {
+ if s.data.is_null() {
+ return "".to_owned();
+ }
+ unsafe {
+ let c_str = CStr::from_ptr(clang_getCString(s) as *const _);
+ let ret = c_str.to_string_lossy().into_owned();
+ clang_disposeString(s);
+ ret
+ }
+}
+
+/// An `Index` is an environment for a set of translation units that will
+/// typically end up linked together in one final binary.
+pub struct Index {
+ x: CXIndex,
+}
+
+impl Index {
+ /// Construct a new `Index`.
+ ///
+ /// The `pch` parameter controls whether declarations in pre-compiled
+ /// headers are included when enumerating a translation unit's "locals".
+ ///
+ /// The `diag` parameter controls whether debugging diagnostics are enabled.
+ pub fn new(pch: bool, diag: bool) -> Index {
+ unsafe {
+ Index {
+ x: clang_createIndex(pch as c_int, diag as c_int),
+ }
+ }
+ }
+}
+
+impl fmt::Debug for Index {
+ fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
+ write!(fmt, "Index {{ }}")
+ }
+}
+
+impl Drop for Index {
+ fn drop(&mut self) {
+ unsafe {
+ clang_disposeIndex(self.x);
+ }
+ }
+}
+
+/// A token emitted by clang's lexer.
+#[derive(Debug)]
+pub struct Token {
+ /// The kind of token this is.
+ pub kind: CXTokenKind,
+ /// A display name for this token.
+ pub spelling: String,
+}
+
+/// A translation unit (or "compilation unit").
+pub struct TranslationUnit {
+ x: CXTranslationUnit,
+}
+
+impl fmt::Debug for TranslationUnit {
+ fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
+ write!(fmt, "TranslationUnit {{ }}")
+ }
+}
+
+impl TranslationUnit {
+ /// Parse a source file into a translation unit.
+ pub fn parse(ix: &Index,
+ file: &str,
+ cmd_args: &[String],
+ unsaved: &[UnsavedFile],
+ opts: CXTranslationUnit_Flags)
+ -> Option<TranslationUnit> {
+ let fname = CString::new(file).unwrap();
+ let _c_args: Vec<CString> =
+ cmd_args.iter().map(|s| CString::new(s.clone()).unwrap()).collect();
+ let c_args: Vec<*const c_char> =
+ _c_args.iter().map(|s| s.as_ptr()).collect();
+ let mut c_unsaved: Vec<CXUnsavedFile> =
+ unsaved.iter().map(|f| f.x).collect();
+ let tu = unsafe {
+ clang_parseTranslationUnit(ix.x,
+ fname.as_ptr(),
+ c_args.as_ptr(),
+ c_args.len() as c_int,
+ c_unsaved.as_mut_ptr(),
+ c_unsaved.len() as c_uint,
+ opts)
+ };
+ if tu.is_null() {
+ None
+ } else {
+ Some(TranslationUnit {
+ x: tu,
+ })
+ }
+ }
+
+ /// Get the Clang diagnostic information associated with this translation
+ /// unit.
+ pub fn diags(&self) -> Vec<Diagnostic> {
+ unsafe {
+ let num = clang_getNumDiagnostics(self.x) as usize;
+ let mut diags = vec![];
+ for i in 0..num {
+ diags.push(Diagnostic {
+ x: clang_getDiagnostic(self.x, i as c_uint),
+ });
+ }
+ diags
+ }
+ }
+
+ /// Get a cursor pointing to the root of this translation unit's AST.
+ pub fn cursor(&self) -> Cursor {
+ unsafe {
+ Cursor {
+ x: clang_getTranslationUnitCursor(self.x),
+ }
+ }
+ }
+
+ /// Is this the null translation unit?
+ pub fn is_null(&self) -> bool {
+ self.x.is_null()
+ }
+
+ /// Invoke Clang's lexer on this translation unit and get the stream of
+ /// tokens that come out.
+ pub fn tokens(&self, cursor: &Cursor) -> Option<Vec<Token>> {
+ let range = cursor.extent();
+ let mut tokens = vec![];
+ unsafe {
+ let mut token_ptr = ptr::null_mut();
+ let mut num_tokens: c_uint = 0;
+ clang_tokenize(self.x, range, &mut token_ptr, &mut num_tokens);
+ if token_ptr.is_null() {
+ return None;
+ }
+
+ let token_array = slice::from_raw_parts(token_ptr,
+ num_tokens as usize);
+ for &token in token_array.iter() {
+ let kind = clang_getTokenKind(token);
+ let spelling =
+ cxstring_into_string(clang_getTokenSpelling(self.x, token));
+
+ tokens.push(Token {
+ kind: kind,
+ spelling: spelling,
+ });
+ }
+ clang_disposeTokens(self.x, token_ptr, num_tokens);
+ }
+ Some(tokens)
+ }
+
+ /// Convert a set of tokens from clang into `cexpr` tokens, for further
+ /// processing.
+ pub fn cexpr_tokens(&self,
+ cursor: &Cursor)
+ -> Option<Vec<cexpr::token::Token>> {
+ use cexpr::token;
+
+ let mut tokens = match self.tokens(cursor) {
+ Some(tokens) => tokens,
+ None => return None,
+ };
+
+ // FIXME(emilio): LLVM 3.9 at least always include an extra token for no
+ // good reason (except if we're at EOF). So we do this kind of hack,
+ // where we skip known-to-cause problems trailing punctuation and
+ // trailing keywords.
+ //
+ // This is sort of unfortunate, though :(.
+ //
+ // I'll try to get it fixed in LLVM if I have the time to submit a
+ // patch.
+ let mut trim_last_token = false;
+ if let Some(token) = tokens.last() {
+ // The starting of the next macro.
+ trim_last_token |= token.spelling == "#" &&
+ token.kind == CXToken_Punctuation;
+
+ // A following keyword of any kind, like a following declaration.
+ trim_last_token |= token.kind == CXToken_Keyword;
+ }
+
+ if trim_last_token {
+ tokens.pop().unwrap();
+ }
+
+ Some(tokens.into_iter()
+ .filter_map(|token| {
+ let kind = match token.kind {
+ CXToken_Punctuation => token::Kind::Punctuation,
+ CXToken_Literal => token::Kind::Literal,
+ CXToken_Identifier => token::Kind::Identifier,
+ CXToken_Keyword => token::Kind::Keyword,
+ // NB: cexpr is not too happy about comments inside
+ // expressions, so we strip them down here.
+ CXToken_Comment => return None,
+ _ => {
+ panic!("Found unexpected token kind: {:?}", token.kind)
+ }
+ };
+
+ Some(token::Token {
+ kind: kind,
+ raw: token.spelling.into_bytes().into_boxed_slice(),
+ })
+ })
+ .collect::<Vec<_>>())
+ }
+}
+
+impl Drop for TranslationUnit {
+ fn drop(&mut self) {
+ unsafe {
+ clang_disposeTranslationUnit(self.x);
+ }
+ }
+}
+
+
+/// A diagnostic message generated while parsing a translation unit.
+pub struct Diagnostic {
+ x: CXDiagnostic,
+}
+
+impl Diagnostic {
+ /// Format this diagnostic message as a string, using the given option bit
+ /// flags.
+ pub fn format(&self) -> String {
+ unsafe {
+ let opts = clang_defaultDiagnosticDisplayOptions();
+ cxstring_into_string(clang_formatDiagnostic(self.x, opts))
+ }
+ }
+
+ /// What is the severity of this diagnostic message?
+ pub fn severity(&self) -> CXDiagnosticSeverity {
+ unsafe { clang_getDiagnosticSeverity(self.x) }
+ }
+}
+
+impl Drop for Diagnostic {
+ /// Destroy this diagnostic message.
+ fn drop(&mut self) {
+ unsafe {
+ clang_disposeDiagnostic(self.x);
+ }
+ }
+}
+
+/// A file which has not been saved to disk.
+pub struct UnsavedFile {
+ x: CXUnsavedFile,
+ name: CString,
+ contents: CString,
+}
+
+impl UnsavedFile {
+ /// Construct a new unsaved file with the given `name` and `contents`.
+ pub fn new(name: &str, contents: &str) -> UnsavedFile {
+ let name = CString::new(name).unwrap();
+ let contents = CString::new(contents).unwrap();
+ let x = CXUnsavedFile {
+ Filename: name.as_ptr(),
+ Contents: contents.as_ptr(),
+ Length: contents.as_bytes().len() as c_ulong,
+ };
+ UnsavedFile {
+ x: x,
+ name: name,
+ contents: contents,
+ }
+ }
+}
+
+/// Convert a cursor kind into a static string.
+pub fn kind_to_str(x: CXCursorKind) -> String {
+ unsafe { cxstring_into_string(clang_getCursorKindSpelling(x)) }
+}
+
+/// Convert a type kind to a static string.
+pub fn type_to_str(x: CXTypeKind) -> String {
+ unsafe { cxstring_into_string(clang_getTypeKindSpelling(x)) }
+}
+
+/// Dump the Clang AST to stdout for debugging purposes.
+pub fn ast_dump(c: &Cursor, depth: isize) -> CXChildVisitResult {
+ fn print_indent(depth: isize, s: &str) {
+ for _ in 0..depth {
+ print!("\t");
+ }
+ println!("{}", s);
+ }
+
+ print_indent(depth,
+ &format!("(kind: {}, spelling: {}, type: {}",
+ kind_to_str(c.kind()),
+ c.spelling(),
+ type_to_str(c.cur_type().kind())));
+
+ // Recurse.
+ c.visit(|s| ast_dump(&s, depth + 1));
+
+ print_indent(depth, ")");
+
+ CXChildVisit_Continue
+}
+
+/// Try to extract the clang version to a string
+pub fn extract_clang_version() -> String {
+ unsafe { cxstring_into_string(clang_getClangVersion()) }
+}
+
+/// A wrapper for the result of evaluating an expression.
+#[derive(Debug)]
+pub struct EvalResult {
+ x: CXEvalResult,
+}
+
+impl EvalResult {
+ /// Evaluate `cursor` and return the result.
+ pub fn new(cursor: Cursor) -> Option<Self> {
+ if !clang_Cursor_Evaluate::is_loaded() {
+ return None;
+ }
+
+ // Clang has an internal assertion we can trigger if we try to evaluate
+ // a cursor containing a variadic template type reference. Triggering
+ // the assertion aborts the process, and we don't want that. Clang
+ // *also* doesn't expose any API for finding variadic vs non-variadic
+ // template type references, let alone whether a type referenced is a
+ // template type, instead they seem to show up as type references to an
+ // unexposed type. Our solution is to just flat out ban all
+ // `CXType_Unexposed` from evaluation.
+ let mut found_cant_eval = false;
+ cursor.visit(|c| {
+ if c.kind() == CXCursor_TypeRef &&
+ c.cur_type().kind() == CXType_Unexposed {
+ found_cant_eval = true;
+ CXChildVisit_Break
+ } else {
+ CXChildVisit_Recurse
+ }
+ });
+ if found_cant_eval {
+ return None;
+ }
+
+ Some(EvalResult {
+ x: unsafe { clang_Cursor_Evaluate(cursor.x) },
+ })
+ }
+
+ fn kind(&self) -> CXEvalResultKind {
+ unsafe { clang_EvalResult_getKind(self.x) }
+ }
+
+ /// Try to get back the result as a double.
+ pub fn as_double(&self) -> Option<f64> {
+ match self.kind() {
+ CXEval_Float => {
+ Some(unsafe { clang_EvalResult_getAsDouble(self.x) } as f64)
+ }
+ _ => None,
+ }
+ }
+
+ /// Try to get back the result as an integer.
+ pub fn as_int(&self) -> Option<i32> {
+ match self.kind() {
+ CXEval_Int => {
+ Some(unsafe { clang_EvalResult_getAsInt(self.x) } as i32)
+ }
+ _ => None,
+ }
+ }
+
+ /// Evaluates the expression as a literal string, that may or may not be
+ /// valid utf-8.
+ pub fn as_literal_string(&self) -> Option<Vec<u8>> {
+ match self.kind() {
+ CXEval_StrLiteral => {
+ let ret = unsafe {
+ CStr::from_ptr(clang_EvalResult_getAsStr(self.x))
+ };
+ Some(ret.to_bytes().to_vec())
+ }
+ _ => None,
+ }
+ }
+}
+
+impl Drop for EvalResult {
+ fn drop(&mut self) {
+ unsafe { clang_EvalResult_dispose(self.x) };
+ }
+}