#![warn(missing_copy_implementations, missing_debug_implementations, missing_docs)]
#![cfg_attr(feature="clippy", feature(plugin))]
#![cfg_attr(feature="clippy", plugin(clippy))]
#![cfg_attr(feature="clippy", warn(clippy))]
#[macro_use]
extern crate lazy_static;
extern crate clang_sys;
extern crate libc;
use std::fmt;
use std::hash;
use std::mem;
use std::ptr;
use std::slice;
use std::cmp::{self, Ordering};
use std::collections::{HashMap};
use std::marker::{PhantomData};
use std::path::{Path, PathBuf};
use std::rc::{Rc};
use std::sync::atomic::{self, AtomicBool};
use clang_sys as ffi;
use libc::{c_int, c_uint, c_ulong, time_t};
macro_rules! iter {
($num:ident($($num_argument:expr), *), $get:ident($($get_argument:expr), *),) => ({
let count = unsafe { ffi::$num($($num_argument), *) };
(0..count).map(|i| unsafe { ffi::$get($($get_argument), *, i) })
});
($num:ident($($num_argument:expr), *), $($get:ident($($get_argument:expr), *)), *,) => ({
let count = unsafe { ffi::$num($($num_argument), *) };
(0..count).map(|i| unsafe { ($(ffi::$get($($get_argument), *, i)), *) })
});
}
macro_rules! iter_option {
($num:ident($($num_argument:expr), *), $get:ident($($get_argument:expr), *),) => ({
let count = unsafe { ffi::$num($($num_argument), *) };
if count >= 0 {
Some((0..count).map(|i| unsafe { ffi::$get($($get_argument), *, i as c_uint) }))
} else {
None
}
});
}
macro_rules! options {
($(#[$attribute:meta])* options $name:ident: $underlying:ident {
$($(#[$fattribute:meta])* pub $option:ident: $flag:ident), +,
}) => (
$(#[$attribute])*
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
pub struct $name {
$($(#[$fattribute])* pub $option: bool), +,
}
impl From<ffi::$underlying> for $name {
fn from(flags: ffi::$underlying) -> $name {
$name { $($option: flags.contains(ffi::$flag)), + }
}
}
impl Into<ffi::$underlying> for $name {
fn into(self) -> ffi::$underlying {
let mut flags = ffi::$underlying::empty();
$(if self.$option { flags.insert(ffi::$flag); })+
flags
}
}
);
}
pub trait Nullable<T> {
fn map<U, F: FnOnce(T) -> U>(self, f: F) -> Option<U>;
}
macro_rules! nullable {
($name:ident) => (
impl Nullable<ffi::$name> for ffi::$name {
fn map<U, F: FnOnce(ffi::$name) -> U>(self, f: F) -> Option<U> {
if !self.0.is_null() {
Some(f(self))
} else {
None
}
}
}
);
}
nullable!(CXCompilationDatabase);
nullable!(CXCompileCommand);
nullable!(CXCompileCommands);
nullable!(CXCompletionString);
nullable!(CXCursorSet);
nullable!(CXDiagnostic);
nullable!(CXDiagnosticSet);
nullable!(CXFile);
nullable!(CXIdxClientASTFile);
nullable!(CXIdxClientContainer);
nullable!(CXIdxClientEntity);
nullable!(CXIdxClientFile);
nullable!(CXIndex);
nullable!(CXIndexAction);
nullable!(CXModule);
nullable!(CXModuleMapDescriptor);
nullable!(CXRemapping);
nullable!(CXTranslationUnit);
nullable!(CXVirtualFileOverlay);
impl Nullable<ffi::CXCursor> for ffi::CXCursor {
fn map<U, F: FnOnce(ffi::CXCursor) -> U>(self, f: F) -> Option<U> {
unsafe {
let null = ffi::clang_equalCursors(self, ffi::clang_getNullCursor()) != 0;
if !null && ffi::clang_isInvalid(self.kind) == 0 {
Some(f(self))
} else {
None
}
}
}
}
impl Nullable<ffi::CXSourceLocation> for ffi::CXSourceLocation {
fn map<U, F: FnOnce(ffi::CXSourceLocation) -> U>(self, f: F) -> Option<U> {
unsafe {
if ffi::clang_equalLocations(self, ffi::clang_getNullLocation()) == 0 {
Some(f(self))
} else {
None
}
}
}
}
impl Nullable<ffi::CXSourceRange> for ffi::CXSourceRange {
fn map<U, F: FnOnce(ffi::CXSourceRange) -> U>(self, f: F) -> Option<U> {
unsafe {
if ffi::clang_Range_isNull(self) == 0 {
Some(f(self))
} else {
None
}
}
}
}
impl Nullable<ffi::CXString> for ffi::CXString {
fn map<U, F: FnOnce(ffi::CXString) -> U>(self, f: F) -> Option<U> {
if !self.data.is_null() {
Some(f(self))
} else {
None
}
}
}
impl Nullable<ffi::CXType> for ffi::CXType {
fn map<U, F: FnOnce(ffi::CXType) -> U>(self, f: F) -> Option<U> {
if self.kind != ffi::CXTypeKind::Invalid {
Some(f(self))
} else {
None
}
}
}
impl Nullable<ffi::CXVersion> for ffi::CXVersion {
fn map<U, F: FnOnce(ffi::CXVersion) -> U>(self, f: F) -> Option<U> {
if self.Major != -1 && self.Minor != -1 && self.Subminor != -1 {
Some(f(self))
} else {
None
}
}
}
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
#[repr(C)]
pub enum Accessibility {
Private = 3,
Protected = 2,
Public = 1,
}
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
pub enum AlignofError {
Dependent,
Incomplete,
}
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
#[repr(C)]
pub enum Availability {
Available = 0,
Deprecated = 1,
Inaccessible = 3,
Unavailable = 2,
}
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
#[repr(C)]
pub enum CallingConvention {
Unexposed = 200,
Cdecl = 1,
Fastcall = 3,
Pascal = 5,
Stdcall = 2,
Thiscall = 4,
Vectorcall = 12,
Aapcs = 6,
AapcsVfp = 7,
IntelOcl = 9,
SysV64 = 11,
Win64 = 10,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum CompletionChunk<'r> {
Optional(CompletionString<'r>),
CurrentParameter(String),
Informative(String),
Placeholder(String),
ResultType(String),
Text(String),
TypedText(String),
Colon(String),
Comma(String),
Equals(String),
Semicolon(String),
LeftAngleBracket(String),
RightAngleBracket(String),
LeftBrace(String),
RightBrace(String),
LeftParenthesis(String),
RightParenthesis(String),
LeftSquareBracket(String),
RightSquareBracket(String),
HorizontalSpace(String),
VerticalSpace(String),
}
impl<'r> CompletionChunk<'r> {
pub fn get_text(&self) -> String {
match *self {
CompletionChunk::Optional(_) => {
panic!("this completion chunk is a `CompletionChunk::Optional`")
},
CompletionChunk::CurrentParameter(ref text) |
CompletionChunk::Informative(ref text) |
CompletionChunk::Placeholder(ref text) |
CompletionChunk::ResultType(ref text) |
CompletionChunk::TypedText(ref text) |
CompletionChunk::Text(ref text) |
CompletionChunk::Colon(ref text) |
CompletionChunk::Comma(ref text) |
CompletionChunk::Equals(ref text) |
CompletionChunk::Semicolon(ref text) |
CompletionChunk::LeftAngleBracket(ref text) |
CompletionChunk::RightAngleBracket(ref text) |
CompletionChunk::LeftBrace(ref text) |
CompletionChunk::RightBrace(ref text) |
CompletionChunk::LeftParenthesis(ref text) |
CompletionChunk::RightParenthesis(ref text) |
CompletionChunk::LeftSquareBracket(ref text) |
CompletionChunk::RightSquareBracket(ref text) |
CompletionChunk::HorizontalSpace(ref text) |
CompletionChunk::VerticalSpace(ref text) => text.clone(),
}
}
}
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
#[repr(C)]
pub enum EntityKind {
UnexposedDecl = 1,
StructDecl = 2,
UnionDecl = 3,
ClassDecl = 4,
EnumDecl = 5,
FieldDecl = 6,
EnumConstantDecl = 7,
FunctionDecl = 8,
VarDecl = 9,
ParmDecl = 10,
ObjCInterfaceDecl = 11,
ObjCCategoryDecl = 12,
ObjCProtocolDecl = 13,
ObjCPropertyDecl = 14,
ObjCIvarDecl = 15,
ObjCInstanceMethodDecl = 16,
ObjCClassMethodDecl = 17,
ObjCImplementationDecl = 18,
ObjCCategoryImplDecl = 19,
TypedefDecl = 20,
Method = 21,
Namespace = 22,
LinkageSpec = 23,
Constructor = 24,
Destructor = 25,
ConversionFunction = 26,
TemplateTypeParameter = 27,
NonTypeTemplateParameter = 28,
TemplateTemplateParameter = 29,
FunctionTemplate = 30,
ClassTemplate = 31,
ClassTemplatePartialSpecialization = 32,
NamespaceAlias = 33,
UsingDirective = 34,
UsingDeclaration = 35,
TypeAliasDecl = 36,
ObjCSynthesizeDecl = 37,
ObjCDynamicDecl = 38,
AccessSpecifier = 39,
ObjCSuperClassRef = 40,
ObjCProtocolRef = 41,
ObjCClassRef = 42,
TypeRef = 43,
BaseSpecifier = 44,
TemplateRef = 45,
NamespaceRef = 46,
MemberRef = 47,
LabelRef = 48,
OverloadedDeclRef = 49,
VariableRef = 50,
UnexposedExpr = 100,
DeclRefExpr = 101,
MemberRefExpr = 102,
CallExpr = 103,
ObjCMessageExpr = 104,
BlockExpr = 105,
IntegerLiteral = 106,
FloatingLiteral = 107,
ImaginaryLiteral = 108,
StringLiteral = 109,
CharacterLiteral = 110,
ParenExpr = 111,
UnaryOperator = 112,
ArraySubscriptExpr = 113,
BinaryOperator = 114,
CompoundAssignOperator = 115,
ConditionalOperator = 116,
CStyleCastExpr = 117,
CompoundLiteralExpr = 118,
InitListExpr = 119,
AddrLabelExpr = 120,
StmtExpr = 121,
GenericSelectionExpr = 122,
GNUNullExpr = 123,
StaticCastExpr = 124,
DynamicCastExpr = 125,
ReinterpretCastExpr = 126,
ConstCastExpr = 127,
FunctionalCastExpr = 128,
TypeidExpr = 129,
BoolLiteralExpr = 130,
NullPtrLiteralExpr = 131,
ThisExpr = 132,
ThrowExpr = 133,
NewExpr = 134,
DeleteExpr = 135,
UnaryExpr = 136,
ObjCStringLiteral = 137,
ObjCEncodeExpr = 138,
ObjCSelectorExpr = 139,
ObjCProtocolExpr = 140,
ObjCBridgedCastExpr = 141,
PackExpansionExpr = 142,
SizeOfPackExpr = 143,
LambdaExpr = 144,
ObjCBoolLiteralExpr = 145,
ObjCSelfExpr = 146,
UnexposedStmt = 200,
LabelStmt = 201,
CompoundStmt = 202,
CaseStmt = 203,
DefaultStmt = 204,
IfStmt = 205,
SwitchStmt = 206,
WhileStmt = 207,
DoStmt = 208,
ForStmt = 209,
GotoStmt = 210,
IndirectGotoStmt = 211,
ContinueStmt = 212,
BreakStmt = 213,
ReturnStmt = 214,
AsmStmt = 215,
ObjCAtTryStmt = 216,
ObjCAtCatchStmt = 217,
ObjCAtFinallyStmt = 218,
ObjCAtThrowStmt = 219,
ObjCAtSynchronizedStmt = 220,
ObjCAutoreleasePoolStmt = 221,
ObjCForCollectionStmt = 222,
CatchStmt = 223,
TryStmt = 224,
ForRangeStmt = 225,
SehTryStmt = 226,
SehExceptStmt = 227,
SehFinallyStmt = 228,
SehLeaveStmt = 247,
MsAsmStmt = 229,
NullStmt = 230,
DeclStmt = 231,
OmpParallelDirective = 232,
OmpSimdDirective = 233,
OmpForDirective = 234,
OmpSectionsDirective = 235,
OmpSectionDirective = 236,
OmpSingleDirective = 237,
OmpParallelForDirective = 238,
OmpParallelSectionsDirective = 239,
OmpTaskDirective = 240,
OmpMasterDirective = 241,
OmpCriticalDirective = 242,
OmpTaskyieldDirective = 243,
OmpBarrierDirective = 244,
OmpTaskwaitDirective = 245,
OmpFlushDirective = 246,
OmpOrderedDirective = 248,
OmpAtomicDirective = 249,
OmpForSimdDirective = 250,
OmpParallelForSimdDirective = 251,
OmpTargetDirective = 252,
OmpTeamsDirective = 253,
OmpTaskgroupDirective = 254,
OmpCancellationPointDirective = 255,
OmpCancelDirective = 256,
TranslationUnit = 300,
UnexposedAttr = 400,
IbActionAttr = 401,
IbOutletAttr = 402,
IbOutletCollectionAttr = 403,
FinalAttr = 404,
OverrideAttr = 405,
AnnotateAttr = 406,
AsmLabelAttr = 407,
PackedAttr = 408,
PureAttr = 409,
ConstAttr = 410,
NoDuplicateAttr = 411,
CudaConstantAttr = 412,
CudaDeviceAttr = 413,
CudaGlobalAttr = 414,
CudaHostAttr = 415,
CudaSharedAttr = 416,
PreprocessingDirective = 500,
MacroDefinition = 501,
MacroExpansion = 502,
InclusionDirective = 503,
ModuleImportDecl = 600,
OverloadCandidate = 700,
}
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
#[repr(C)]
pub enum EntityVisitResult {
Break = 0,
Continue = 1,
Recurse = 2,
}
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub enum FixIt<'tu> {
Deletion(SourceRange<'tu>),
Insertion(SourceLocation<'tu>, String),
Replacement(SourceRange<'tu>, String),
}
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
#[repr(C)]
pub enum Language {
C = 1,
Cpp = 3,
ObjectiveC = 2,
}
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
#[repr(C)]
pub enum Linkage {
Automatic = 1,
Internal = 2,
External = 4,
UniqueExternal = 3,
}
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
#[repr(C)]
pub enum MemoryUsage {
Ast = 1,
AstSideTables = 6,
ExternalAstSourceMalloc = 9,
ExternalAstSourceMMap = 10,
GlobalCodeCompletionResults = 4,
Identifiers = 2,
PreprocessingRecord = 12,
Preprocessor = 11,
PreprocessorHeaderSearch = 14,
Selectors = 3,
SourceManagerContentCache = 5,
SourceManagerDataStructures = 13,
SourceManagerMalloc = 7,
SourceManagerMMap = 8,
}
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
pub enum OffsetofError {
Dependent,
Incomplete,
Name,
Parent,
}
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
#[repr(C)]
pub enum RefQualifier {
LValue = 1,
RValue = 2,
}
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
pub enum SaveError {
Errors,
Unknown,
}
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
#[repr(C)]
pub enum Severity {
Ignored = 0,
Note = 1,
Warning = 2,
Error = 3,
Fatal = 4,
}
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
pub enum SizeofError {
Dependent,
Incomplete,
VariableSize,
}
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
pub enum SourceError {
AstDeserialization,
Crash,
Unknown,
}
#[cfg(any(feature="clang_3_6", feature="clang_3_7"))]
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
#[repr(C)]
pub enum StorageClass {
None = 1,
Auto = 6,
Register = 7,
Static = 3,
Extern = 2,
PrivateExtern = 4,
OpenClWorkGroupLocal = 5,
}
#[cfg(any(feature="clang_3_6", feature="clang_3_7"))]
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum TemplateArgument<'tu> {
Declaration,
Expression,
Null,
Nullptr,
Pack,
Template,
TemplateExpansion,
Integral(i64, u64),
Type(Type<'tu>),
}
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
#[repr(C)]
pub enum TokenKind {
Comment = 4,
Identifier = 2,
Keyword = 1,
Literal = 3,
Punctuation = 0,
}
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
#[repr(C)]
pub enum TypeKind {
Unexposed = 1,
Void = 2,
Bool = 3,
CharS = 13,
CharU = 4,
SChar = 14,
UChar = 5,
WChar = 15,
Char16 = 6,
Char32 = 7,
Short = 16,
UShort = 8,
Int = 17,
UInt = 9,
Long = 18,
ULong = 10,
LongLong = 19,
ULongLong = 11,
Int128 = 20,
UInt128 = 12,
Float = 21,
Double = 22,
LongDouble = 23,
Nullptr = 24,
Complex = 100,
Dependent = 26,
Overload = 25,
ObjCId = 27,
ObjCClass = 28,
ObjCSel = 29,
ObjCInterface = 108,
ObjCObjectPointer = 109,
Pointer = 101,
BlockPointer = 102,
MemberPointer = 117,
LValueReference = 103,
RValueReference = 104,
Enum = 106,
Record = 105,
Typedef = 107,
FunctionPrototype = 111,
FunctionNoPrototype = 110,
ConstantArray = 112,
DependentSizedArray = 116,
IncompleteArray = 114,
VariableArray = 115,
Vector = 113,
}
lazy_static! { static ref AVAILABLE: AtomicBool = AtomicBool::new(true); }
pub struct Clang;
impl Clang {
pub fn new() -> Result<Clang, ()> {
if AVAILABLE.swap(false, atomic::Ordering::Relaxed) {
Ok(Clang)
} else {
Err(())
}
}
pub fn get_version() -> String {
unsafe { to_string(ffi::clang_getClangVersion()) }
}
}
impl Drop for Clang {
fn drop(&mut self) {
AVAILABLE.store(true, atomic::Ordering::Relaxed);
}
}
impl fmt::Debug for Clang {
fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
formatter.debug_struct("Clang").finish()
}
}
pub struct CompilationDatabase<'c> {
ptr: ffi::CXCompilationDatabase,
_marker: PhantomData<&'c Clang>,
}
impl<'c> CompilationDatabase<'c> {
pub fn from_directory<D: AsRef<Path>>(
_: &'c Clang, directory: D
) -> Result<CompilationDatabase<'c>, ()> {
unsafe {
let mut code = mem::uninitialized();
let ptr = ffi::clang_CompilationDatabase_fromDirectory(
from_path(directory).as_ptr(), &mut code
);
if code == ffi::CXCompilationDatabase_Error::NoError {
Ok(CompilationDatabase { ptr: ptr, _marker: PhantomData })
} else {
Err(())
}
}
}
fn get_commands_(&self, ptr: ffi::CXCompileCommands) -> Vec<CompileCommand> {
if !ptr.0.is_null() {
let commands = CompileCommands::from_ptr(ptr);
iter!(
clang_CompileCommands_getSize(commands.ptr),
clang_CompileCommands_getCommand(commands.ptr),
).map(|c| CompileCommand::from_ptr(c, commands.clone())).collect()
} else {
vec![]
}
}
pub fn get_all_commands(&self) -> Vec<CompileCommand> {
unsafe {
let ptr = ffi::clang_CompilationDatabase_getAllCompileCommands(self.ptr);
self.get_commands_(ptr)
}
}
pub fn get_commands<F: AsRef<Path>>(&self, file: F) -> Vec<CompileCommand> {
unsafe {
let ptr = ffi::clang_CompilationDatabase_getCompileCommands(
self.ptr, from_path(file).as_ptr()
);
self.get_commands_(ptr)
}
}
}
impl<'c> Drop for CompilationDatabase<'c> {
fn drop(&mut self) {
unsafe { ffi::clang_CompilationDatabase_dispose(self.ptr); }
}
}
impl<'c> fmt::Debug for CompilationDatabase<'c> {
fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
formatter.debug_struct("CompilationDatabase")
.field("commands", &self.get_all_commands())
.finish()
}
}
#[derive(Clone)]
pub struct CompileCommand<'d> {
ptr: ffi::CXCompileCommand,
parent: Rc<CompileCommands>,
_marker: PhantomData<&'d CompilationDatabase<'d>>,
}
impl<'d> CompileCommand<'d> {
fn from_ptr(ptr: ffi::CXCompileCommand, parent: Rc<CompileCommands>) -> CompileCommand<'d> {
CompileCommand { ptr: ptr, parent: parent, _marker: PhantomData }
}
pub fn get_arguments(&self) -> Vec<String> {
iter!(
clang_CompileCommand_getNumArgs(self.ptr),
clang_CompileCommand_getArg(self.ptr),
).map(to_string).collect()
}
pub fn get_mapped_source_files(&self) -> Vec<(PathBuf, String)> {
iter!(
clang_CompileCommand_getNumMappedSources(self.ptr),
clang_CompileCommand_getMappedSourcePath(self.ptr),
clang_CompileCommand_getMappedSourceContent(self.ptr),
).map(|(p, c)| (to_string(p).into(), to_string(c))).collect()
}
pub fn get_working_directory(&self) -> PathBuf {
unsafe { to_string(ffi::clang_CompileCommand_getDirectory(self.ptr)).into() }
}
}
impl<'c> fmt::Debug for CompileCommand<'c> {
fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
formatter.debug_struct("CompileCommand")
.field("working_directory", &self.get_working_directory())
.field("arguments", &self.get_arguments())
.finish()
}
}
#[derive(Clone)]
struct CompileCommands {
ptr: ffi::CXCompileCommands,
}
impl CompileCommands {
fn from_ptr(ptr: ffi::CXCompileCommands) -> Rc<CompileCommands> {
Rc::new(CompileCommands { ptr: ptr })
}
}
impl Drop for CompileCommands {
fn drop(&mut self) {
unsafe { ffi::clang_CompileCommands_dispose(self.ptr); }
}
}
options! {
options CompletionContext: CXCompletionContext {
pub all_types: CXCompletionContext_AnyType,
pub all_values: CXCompletionContext_AnyValue,
pub class_type_values: CXCompletionContext_CXXClassTypeValue,
pub dot_members: CXCompletionContext_DotMemberAccess,
pub arrow_members: CXCompletionContext_ArrowMemberAccess,
pub enum_tags: CXCompletionContext_EnumTag,
pub union_tags: CXCompletionContext_UnionTag,
pub struct_tags: CXCompletionContext_StructTag,
pub class_names: CXCompletionContext_ClassTag,
pub namespaces: CXCompletionContext_Namespace,
pub nested_name_specifiers: CXCompletionContext_NestedNameSpecifier,
pub macro_names: CXCompletionContext_MacroName,
pub natural_language: CXCompletionContext_NaturalLanguage,
pub objc_object_values: CXCompletionContext_ObjCObjectValue,
pub objc_selector_values: CXCompletionContext_ObjCSelectorValue,
pub objc_property_members: CXCompletionContext_ObjCPropertyAccess,
pub objc_interfaces: CXCompletionContext_ObjCInterface,
pub objc_protocols: CXCompletionContext_ObjCProtocol,
pub objc_categories: CXCompletionContext_ObjCCategory,
pub objc_instance_messages: CXCompletionContext_ObjCInstanceMessage,
pub objc_class_messages: CXCompletionContext_ObjCClassMessage,
pub objc_selector_names: CXCompletionContext_ObjCSelectorName,
}
}
options! {
options CompletionOptions: CXCodeComplete_Flags {
pub macros: CXCodeComplete_IncludeMacros,
pub code_patterns: CXCodeComplete_IncludeCodePatterns,
pub briefs: CXCodeComplete_IncludeBriefComments,
}
}
impl Default for CompletionOptions {
fn default() -> CompletionOptions {
unsafe { CompletionOptions::from(ffi::clang_defaultCodeCompleteOptions()) }
}
}
#[derive(Copy, Clone)]
pub struct CompletionResult<'r> {
raw: ffi::CXCompletionResult,
_marker: PhantomData<&'r CompletionResults>
}
impl<'r> CompletionResult<'r> {
fn from_raw(raw: ffi::CXCompletionResult) -> CompletionResult<'r> {
CompletionResult { raw: raw, _marker: PhantomData }
}
pub fn get_kind(&self) -> EntityKind {
unsafe { mem::transmute(self.raw.CursorKind) }
}
pub fn get_string(&self) -> CompletionString<'r> {
CompletionString::from_raw(self.raw.CompletionString)
}
}
impl<'r> cmp::Eq for CompletionResult<'r> { }
impl<'r> cmp::Ord for CompletionResult<'r> {
fn cmp(&self, other: &CompletionResult<'r>) -> Ordering {
self.get_string().cmp(&other.get_string())
}
}
impl<'r> cmp::PartialEq for CompletionResult<'r> {
fn eq(&self, other: &CompletionResult<'r>) -> bool {
self.get_kind() == other.get_kind() && self.get_string() == other.get_string()
}
}
impl<'r> cmp::PartialOrd for CompletionResult<'r> {
fn partial_cmp(&self, other: &CompletionResult<'r>) -> Option<Ordering> {
self.get_string().partial_cmp(&other.get_string())
}
}
impl<'r> fmt::Debug for CompletionResult<'r> {
fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
formatter.debug_struct("CompletionResult")
.field("kind", &self.get_kind())
.field("string", &self.get_string())
.finish()
}
}
pub struct CompletionResults {
ptr: *mut ffi::CXCodeCompleteResults,
}
impl CompletionResults {
fn from_ptr(ptr: *mut ffi::CXCodeCompleteResults) -> CompletionResults {
CompletionResults { ptr: ptr }
}
pub fn get_container_kind(&self) -> Option<(EntityKind, bool)> {
unsafe {
let mut incomplete = mem::uninitialized();
match ffi::clang_codeCompleteGetContainerKind(self.ptr, &mut incomplete) {
ffi::CXCursorKind::InvalidCode => None,
other => Some((mem::transmute(other), incomplete != 0)),
}
}
}
pub fn get_context(&self) -> Option<CompletionContext> {
unsafe {
let bits = ffi::clang_codeCompleteGetContexts(self.ptr) as c_uint;
if bits != 0 && bits != 4194303 {
Some(CompletionContext::from(ffi::CXCompletionContext::from_bits_truncate(bits)))
} else {
None
}
}
}
pub fn get_diagnostics<'tu>(&self, tu: &'tu TranslationUnit<'tu>) -> Vec<Diagnostic<'tu>> {
iter!(
clang_codeCompleteGetNumDiagnostics(self.ptr),
clang_codeCompleteGetDiagnostic(self.ptr),
).map(|d| Diagnostic::from_ptr(d, tu)).collect()
}
pub fn get_objc_selector(&self) -> Option<String> {
unsafe { to_string_option(ffi::clang_codeCompleteGetObjCSelector(self.ptr)) }
}
pub fn get_results(&self) -> Vec<CompletionResult> {
unsafe {
let raws = slice::from_raw_parts((*self.ptr).Results, (*self.ptr).NumResults as usize);
raws.iter().map(|r| CompletionResult::from_raw(*r)).collect()
}
}
pub fn get_usr(&self) -> Option<Usr> {
unsafe { to_string_option(ffi::clang_codeCompleteGetContainerUSR(self.ptr)).map(Usr) }
}
}
impl Drop for CompletionResults {
fn drop(&mut self) {
unsafe { ffi::clang_disposeCodeCompleteResults(self.ptr); }
}
}
impl fmt::Debug for CompletionResults {
fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
formatter.debug_struct("CompletionResults")
.field("results", &self.get_results())
.finish()
}
}
#[derive(Copy, Clone)]
pub struct CompletionString<'r> {
raw: ffi::CXCompletionString,
_marker: PhantomData<&'r CompletionResults>
}
impl<'r> CompletionString<'r> {
fn from_raw(raw: ffi::CXCompletionString) -> CompletionString<'r> {
CompletionString { raw: raw, _marker: PhantomData }
}
pub fn get_annotations(&self) -> Vec<String> {
iter!(
clang_getCompletionNumAnnotations(self.raw),
clang_getCompletionAnnotation(self.raw),
).map(to_string).collect()
}
pub fn get_availability(&self) -> Availability {
unsafe { mem::transmute(ffi::clang_getCompletionAvailability(self.raw)) }
}
pub fn get_chunks(&self) -> Vec<CompletionChunk> {
iter!(
clang_getNumCompletionChunks(self.raw),
clang_getCompletionChunkKind(self.raw),
).enumerate().map(|(i, k)| {
macro_rules! text {
($variant:ident) => ({
let text = unsafe { ffi::clang_getCompletionChunkText(self.raw, i as c_uint) };
CompletionChunk::$variant(to_string(text))
});
}
match k {
ffi::CXCompletionChunkKind::Optional => {
let raw = unsafe {
ffi::clang_getCompletionChunkCompletionString(self.raw, i as c_uint)
};
CompletionChunk::Optional(CompletionString::from_raw(raw))
},
ffi::CXCompletionChunkKind::CurrentParameter => text!(CurrentParameter),
ffi::CXCompletionChunkKind::TypedText => text!(TypedText),
ffi::CXCompletionChunkKind::Text => text!(Text),
ffi::CXCompletionChunkKind::Placeholder => text!(Placeholder),
ffi::CXCompletionChunkKind::Informative => text!(Informative),
ffi::CXCompletionChunkKind::ResultType => text!(ResultType),
ffi::CXCompletionChunkKind::Colon => text!(Colon),
ffi::CXCompletionChunkKind::Comma => text!(Comma),
ffi::CXCompletionChunkKind::Equal => text!(Equals),
ffi::CXCompletionChunkKind::SemiColon => text!(Semicolon),
ffi::CXCompletionChunkKind::LeftAngle => text!(LeftAngleBracket),
ffi::CXCompletionChunkKind::RightAngle => text!(RightAngleBracket),
ffi::CXCompletionChunkKind::LeftBrace => text!(LeftBrace),
ffi::CXCompletionChunkKind::RightBrace => text!(RightBrace),
ffi::CXCompletionChunkKind::LeftParen => text!(LeftParenthesis),
ffi::CXCompletionChunkKind::RightParen => text!(RightParenthesis),
ffi::CXCompletionChunkKind::LeftBracket => text!(LeftSquareBracket),
ffi::CXCompletionChunkKind::RightBracket => text!(RightSquareBracket),
ffi::CXCompletionChunkKind::HorizontalSpace => text!(HorizontalSpace),
ffi::CXCompletionChunkKind::VerticalSpace => text!(VerticalSpace),
}
}).collect()
}
pub fn get_comment_brief(&self) -> Option<String> {
unsafe { to_string_option(ffi::clang_getCompletionBriefComment(self.raw)) }
}
pub fn get_parent_name(&self) -> Option<String> {
unsafe { to_string_option(ffi::clang_getCompletionParent(self.raw, ptr::null_mut())) }
}
pub fn get_priority(&self) -> usize {
unsafe { ffi::clang_getCompletionPriority(self.raw) as usize }
}
pub fn get_typed_text(&self) -> Option<String> {
for chunk in self.get_chunks() {
if let CompletionChunk::TypedText(text) = chunk {
return Some(text);
}
}
None
}
}
impl<'r> cmp::Eq for CompletionString<'r> { }
impl<'r> cmp::Ord for CompletionString<'r> {
fn cmp(&self, other: &CompletionString<'r>) -> Ordering {
match self.get_priority().cmp(&other.get_priority()) {
Ordering::Equal => self.get_typed_text().cmp(&other.get_typed_text()),
other => other,
}
}
}
impl<'r> cmp::PartialEq for CompletionString<'r> {
fn eq(&self, other: &CompletionString<'r>) -> bool {
self.get_chunks() == other.get_chunks()
}
}
impl<'r> cmp::PartialOrd for CompletionString<'r> {
fn partial_cmp(&self, other: &CompletionString<'r>) -> Option<Ordering> {
Some(self.cmp(other))
}
}
impl<'r> fmt::Debug for CompletionString<'r> {
fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
formatter.debug_struct("CompletionString")
.field("chunks", &self.get_chunks())
.finish()
}
}
#[derive(Copy, Clone)]
pub struct Diagnostic<'tu> {
ptr: ffi::CXDiagnostic,
tu: &'tu TranslationUnit<'tu>,
}
impl<'tu> Diagnostic<'tu> {
fn from_ptr(ptr: ffi::CXDiagnostic, tu: &'tu TranslationUnit<'tu>) -> Diagnostic<'tu> {
Diagnostic { ptr: ptr, tu: tu }
}
pub fn format(&self, options: FormatOptions) -> String {
unsafe { to_string(ffi::clang_formatDiagnostic(self.ptr, options.into())) }
}
pub fn get_children(&self) -> Vec<Diagnostic> {
let raw = unsafe { ffi::clang_getChildDiagnostics(self.ptr) };
iter!(
clang_getNumDiagnosticsInSet(raw),
clang_getDiagnosticInSet(raw),
).map(|d| Diagnostic::from_ptr(d, self.tu)).collect()
}
pub fn get_fix_its(&self) -> Vec<FixIt<'tu>> {
unsafe {
(0..ffi::clang_getDiagnosticNumFixIts(self.ptr)).map(|i| {
let mut range = mem::uninitialized();
let string = to_string(ffi::clang_getDiagnosticFixIt(self.ptr, i, &mut range));
let range = SourceRange::from_raw(range, self.tu);
if string.is_empty() {
FixIt::Deletion(range)
} else if range.get_start() == range.get_end() {
FixIt::Insertion(range.get_start(), string)
} else {
FixIt::Replacement(range, string)
}
}).collect()
}
}
pub fn get_location(&self) -> SourceLocation<'tu> {
unsafe { SourceLocation::from_raw(ffi::clang_getDiagnosticLocation(self.ptr), self.tu) }
}
pub fn get_ranges(&self) -> Vec<SourceRange<'tu>> {
iter!(
clang_getDiagnosticNumRanges(self.ptr),
clang_getDiagnosticRange(self.ptr),
).map(|r| {
SourceRange::from_raw(r, self.tu)
}).collect()
}
pub fn get_severity(&self) -> Severity {
unsafe { mem::transmute(ffi::clang_getDiagnosticSeverity(self.ptr)) }
}
pub fn get_text(&self) -> String {
unsafe { to_string(ffi::clang_getDiagnosticSpelling(self.ptr)) }
}
}
impl<'tu> fmt::Debug for Diagnostic<'tu> {
fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
formatter.debug_struct("Diagnostic")
.field("location", &self.get_location())
.field("severity", &self.get_severity())
.field("text", &self.get_text())
.finish()
}
}
impl<'tu> fmt::Display for Diagnostic<'tu> {
fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
write!(formatter, "{}", self.format(FormatOptions::default()))
}
}
#[derive(Copy, Clone)]
pub struct Entity<'tu> {
raw: ffi::CXCursor,
tu: &'tu TranslationUnit<'tu>,
}
impl<'tu> Entity<'tu> {
fn from_raw(raw: ffi::CXCursor, tu: &'tu TranslationUnit<'tu>) -> Entity<'tu> {
Entity { raw: raw, tu: tu }
}
pub fn get_accessibility(&self) -> Option<Accessibility> {
unsafe {
match ffi::clang_getCXXAccessSpecifier(self.raw) {
ffi::CX_CXXAccessSpecifier::CXXInvalidAccessSpecifier => None,
other => Some(mem::transmute(other)),
}
}
}
pub fn get_arguments(&self) -> Option<Vec<Entity<'tu>>> {
iter_option!(
clang_Cursor_getNumArguments(self.raw),
clang_Cursor_getArgument(self.raw),
).map(|i| i.map(|a| Entity::from_raw(a, self.tu)).collect())
}
pub fn get_availability(&self) -> Availability {
unsafe { mem::transmute(ffi::clang_getCursorAvailability(self.raw)) }
}
pub fn get_bit_field_width(&self) -> Option<usize> {
unsafe {
let width = ffi::clang_getFieldDeclBitWidth(self.raw);
if width >= 0 {
Some(width as usize)
} else {
None
}
}
}
pub fn get_canonical_entity(&self) -> Entity<'tu> {
unsafe { Entity::from_raw(ffi::clang_getCanonicalCursor(self.raw), self.tu) }
}
pub fn get_comment(&self) -> Option<String> {
unsafe { to_string_option(ffi::clang_Cursor_getRawCommentText(self.raw)) }
}
pub fn get_comment_brief(&self) -> Option<String> {
unsafe { to_string_option(ffi::clang_Cursor_getBriefCommentText(self.raw)) }
}
pub fn get_comment_range(&self) -> Option<SourceRange<'tu>> {
let range = unsafe { ffi::clang_Cursor_getCommentRange(self.raw) };
range.map(|r| SourceRange::from_raw(r, self.tu))
}
pub fn get_completion_string(&self) -> Option<CompletionString> {
unsafe { ffi::clang_getCursorCompletionString(self.raw).map(CompletionString::from_raw) }
}
pub fn get_children(&self) -> Vec<Entity<'tu>> {
let mut children = vec![];
self.visit_children(|c, _| {
children.push(c);
EntityVisitResult::Continue
});
children
}
pub fn get_definition(&self) -> Option<Entity<'tu>> {
unsafe { ffi::clang_getCursorDefinition(self.raw).map(|p| Entity::from_raw(p, self.tu)) }
}
pub fn get_display_name(&self) -> Option<String> {
unsafe { to_string_option(ffi::clang_getCursorDisplayName(self.raw)) }
}
pub fn get_enum_constant_value(&self) -> Option<(i64, u64)> {
unsafe {
if self.get_kind() == EntityKind::EnumConstantDecl {
let signed = ffi::clang_getEnumConstantDeclValue(self.raw);
let unsigned = ffi::clang_getEnumConstantDeclUnsignedValue(self.raw);
Some((signed, unsigned))
} else {
None
}
}
}
pub fn get_enum_underlying_type(&self) -> Option<Type<'tu>> {
unsafe { ffi::clang_getEnumDeclIntegerType(self.raw).map(|t| Type::from_raw(t, self.tu)) }
}
pub fn get_file(&self) -> Option<File<'tu>> {
unsafe { ffi::clang_getIncludedFile(self.raw).map(|f| File::from_ptr(f, self.tu)) }
}
pub fn get_kind(&self) -> EntityKind {
unsafe { mem::transmute(ffi::clang_getCursorKind(self.raw)) }
}
pub fn get_language(&self) -> Option<Language> {
unsafe {
match ffi::clang_getCursorLanguage(self.raw) {
ffi::CXLanguageKind::Invalid => None,
other => Some(mem::transmute(other)),
}
}
}
pub fn get_lexical_parent(&self) -> Option<Entity<'tu>> {
let parent = unsafe { ffi::clang_getCursorLexicalParent(self.raw) };
parent.map(|p| Entity::from_raw(p, self.tu))
}
pub fn get_linkage(&self) -> Option<Linkage> {
unsafe {
match ffi::clang_getCursorLinkage(self.raw) {
ffi::CXLinkageKind::Invalid => None,
other => Some(mem::transmute(other)),
}
}
}
pub fn get_location(&self) -> Option<SourceLocation<'tu>> {
unsafe {
let location = ffi::clang_getCursorLocation(self.raw);
location.map(|l| SourceLocation::from_raw(l, self.tu))
}
}
#[cfg(any(feature="clang_3_6", feature="clang_3_7"))]
pub fn get_mangled_name(&self) -> Option<String> {
unsafe { to_string_option(ffi::clang_Cursor_getMangling(self.raw)) }
}
pub fn get_module(&self) -> Option<Module<'tu>> {
unsafe { ffi::clang_Cursor_getModule(self.raw).map(|m| Module::from_ptr(m, self.tu)) }
}
pub fn get_name(&self) -> Option<String> {
unsafe { to_string_option(ffi::clang_getCursorSpelling(self.raw)) }
}
pub fn get_name_ranges(&self) -> Vec<SourceRange<'tu>> {
use std::ptr;
unsafe {
(0..).map(|i| ffi::clang_Cursor_getSpellingNameRange(self.raw, i, 0)).take_while(|r| {
if ffi::clang_Range_isNull(*r) != 0 {
false
} else {
let mut file = mem::uninitialized();
ffi::clang_getSpellingLocation(
ffi::clang_getRangeStart(*r),
&mut file,
ptr::null_mut(),
ptr::null_mut(),
ptr::null_mut(),
);
!file.0.is_null()
}
}).map(|r| SourceRange::from_raw(r, self.tu)).collect()
}
}
pub fn get_objc_attributes(&self) -> Option<ObjCAttributes> {
unsafe {
let attributes = ffi::clang_Cursor_getObjCPropertyAttributes(self.raw, 0);
if attributes.bits() != 0 {
Some(ObjCAttributes::from(attributes))
} else {
None
}
}
}
pub fn get_objc_ib_outlet_collection_type(&self) -> Option<Type<'tu>> {
unsafe {
ffi::clang_getIBOutletCollectionType(self.raw).map(|t| Type::from_raw(t, self.tu))
}
}
pub fn get_objc_receiver_type(&self) -> Option<Type<'tu>> {
unsafe { ffi::clang_Cursor_getReceiverType(self.raw).map(|t| Type::from_raw(t, self.tu)) }
}
pub fn get_objc_selector_index(&self) -> Option<usize> {
unsafe {
let index = ffi::clang_Cursor_getObjCSelectorIndex(self.raw);
if index >= 0 {
Some(index as usize)
} else {
None
}
}
}
pub fn get_objc_type_encoding(&self) -> Option<String> {
unsafe { to_string_option(ffi::clang_getDeclObjCTypeEncoding(self.raw)) }
}
pub fn get_objc_qualifiers(&self) -> Option<ObjCQualifiers> {
unsafe {
let qualifiers = ffi::clang_Cursor_getObjCDeclQualifiers(self.raw);
if qualifiers.bits() != 0 {
Some(ObjCQualifiers::from(qualifiers))
} else {
None
}
}
}
pub fn get_overloaded_declarations(&self) -> Option<Vec<Entity<'tu>>> {
let declarations = iter!(
clang_getNumOverloadedDecls(self.raw),
clang_getOverloadedDecl(self.raw),
).map(|e| Entity::from_raw(e, self.tu)).collect::<Vec<_>>();
if !declarations.is_empty() {
Some(declarations)
} else {
None
}
}
pub fn get_overridden_methods(&self) -> Option<Vec<Entity<'tu>>> {
unsafe {
let (mut raw, mut count) = mem::uninitialized();
ffi::clang_getOverriddenCursors(self.raw, &mut raw, &mut count);
if !raw.is_null() {
let raws = slice::from_raw_parts(raw, count as usize);
let methods = raws.iter().map(|e| Entity::from_raw(*e, self.tu)).collect();
ffi::clang_disposeOverriddenCursors(raw);
Some(methods)
} else {
None
}
}
}
pub fn get_platform_availability(&self) -> Option<Vec<PlatformAvailability>> {
if !self.is_declaration() {
return None;
}
unsafe {
let mut buffer: [ffi::CXPlatformAvailability; 32] = mem::uninitialized();
let count = ffi::clang_getCursorPlatformAvailability(
self.raw,
ptr::null_mut(),
ptr::null_mut(),
ptr::null_mut(),
ptr::null_mut(),
(&mut buffer).as_mut_ptr(),
buffer.len() as c_int,
);
Some((0..count as usize).map(|i| PlatformAvailability::from_raw(buffer[i])).collect())
}
}
pub fn get_range(&self) -> Option<SourceRange<'tu>> {
unsafe {
let range = ffi::clang_getCursorExtent(self.raw);
range.map(|r| SourceRange::from_raw(r, self.tu))
}
}
pub fn get_reference(&self) -> Option<Entity<'tu>> {
unsafe { ffi::clang_getCursorReferenced(self.raw).map(|p| Entity::from_raw(p, self.tu)) }
}
pub fn get_semantic_parent(&self) -> Option<Entity<'tu>> {
let parent = unsafe { ffi::clang_getCursorSemanticParent(self.raw) };
parent.map(|p| Entity::from_raw(p, self.tu))
}
#[cfg(any(feature="clang_3_6", feature="clang_3_7"))]
pub fn get_storage_class(&self) -> Option<StorageClass> {
unsafe {
match ffi::clang_Cursor_getStorageClass(self.raw) {
ffi::CX_StorageClass::Invalid => None,
other => Some(mem::transmute(other)),
}
}
}
pub fn get_template(&self) -> Option<Entity<'tu>> {
let parent = unsafe { ffi::clang_getSpecializedCursorTemplate(self.raw) };
parent.map(|p| Entity::from_raw(p, self.tu))
}
#[cfg(any(feature="clang_3_6", feature="clang_3_7"))]
pub fn get_template_arguments(&self) -> Option<Vec<TemplateArgument<'tu>>> {
let get_type = &ffi::clang_Cursor_getTemplateArgumentType;
let get_signed = &ffi::clang_Cursor_getTemplateArgumentValue;
let get_unsigned = &ffi::clang_Cursor_getTemplateArgumentUnsignedValue;
iter_option!(
clang_Cursor_getNumTemplateArguments(self.raw),
clang_Cursor_getTemplateArgumentKind(self.raw),
).map(|i| {
i.enumerate().map(|(i, t)| {
match t {
ffi::CXTemplateArgumentKind::Null => TemplateArgument::Null,
ffi::CXTemplateArgumentKind::Type => {
let type_ = unsafe { get_type(self.raw, i as c_uint) };
TemplateArgument::Type(Type::from_raw(type_, self.tu))
},
ffi::CXTemplateArgumentKind::Declaration => TemplateArgument::Declaration,
ffi::CXTemplateArgumentKind::NullPtr => TemplateArgument::Nullptr,
ffi::CXTemplateArgumentKind::Integral => {
let signed = unsafe { get_signed(self.raw, i as c_uint) };
let unsigned = unsafe { get_unsigned(self.raw, i as c_uint) };
TemplateArgument::Integral(signed as i64, unsigned as u64)
},
ffi::CXTemplateArgumentKind::Template => TemplateArgument::Template,
ffi::CXTemplateArgumentKind::TemplateExpansion => TemplateArgument::TemplateExpansion,
ffi::CXTemplateArgumentKind::Expression => TemplateArgument::Expression,
ffi::CXTemplateArgumentKind::Pack => TemplateArgument::Pack,
_ => unreachable!(),
}
}).collect()
})
}
pub fn get_template_kind(&self) -> Option<EntityKind> {
unsafe {
match ffi::clang_getTemplateCursorKind(self.raw) {
ffi::CXCursorKind::NoDeclFound => None,
other => Some(mem::transmute(other)),
}
}
}
pub fn get_translation_unit(&self) -> &'tu TranslationUnit<'tu> {
self.tu
}
pub fn get_type(&self) -> Option<Type<'tu>> {
unsafe { ffi::clang_getCursorType(self.raw).map(|t| Type::from_raw(t, self.tu)) }
}
pub fn get_typedef_underlying_type(&self) -> Option<Type<'tu>> {
unsafe {
let type_ = ffi::clang_getTypedefDeclUnderlyingType(self.raw);
type_.map(|t| Type::from_raw(t, self.tu))
}
}
pub fn get_usr(&self) -> Option<Usr> {
unsafe { to_string_option(ffi::clang_getCursorUSR(self.raw)).map(Usr) }
}
#[cfg(feature="clang_3_7")]
pub fn is_anonymous(&self) -> bool {
unsafe { ffi::clang_Cursor_isAnonymous(self.raw) != 0 }
}
pub fn is_bit_field(&self) -> bool {
unsafe { ffi::clang_Cursor_isBitField(self.raw) != 0 }
}
pub fn is_const_method(&self) -> bool {
unsafe { ffi::clang_CXXMethod_isConst(self.raw) != 0 }
}
pub fn is_definition(&self) -> bool {
unsafe { ffi::clang_isCursorDefinition(self.raw) != 0 }
}
pub fn is_dynamic_call(&self) -> bool {
unsafe { ffi::clang_Cursor_isDynamicCall(self.raw) != 0 }
}
pub fn is_objc_optional(&self) -> bool {
unsafe { ffi::clang_Cursor_isObjCOptional(self.raw) != 0 }
}
pub fn is_pure_virtual_method(&self) -> bool {
unsafe { ffi::clang_CXXMethod_isPureVirtual(self.raw) != 0 }
}
pub fn is_static_method(&self) -> bool {
unsafe { ffi::clang_CXXMethod_isStatic(self.raw) != 0 }
}
pub fn is_variadic(&self) -> bool {
unsafe { ffi::clang_Cursor_isVariadic(self.raw) != 0 }
}
pub fn is_virtual_base(&self) -> bool {
unsafe { ffi::clang_isVirtualBase(self.raw) != 0 }
}
pub fn is_virtual_method(&self) -> bool {
unsafe { ffi::clang_CXXMethod_isVirtual(self.raw) != 0 }
}
pub fn visit_children<F: FnMut(Entity<'tu>, Entity<'tu>) -> EntityVisitResult>(
&self, f: F
) -> bool {
trait EntityCallback<'tu> {
fn call(&mut self, entity: Entity<'tu>, parent: Entity<'tu>) -> EntityVisitResult;
}
impl<'tu, F: FnMut(Entity<'tu>, Entity<'tu>) -> EntityVisitResult> EntityCallback<'tu> for F {
fn call(&mut self, entity: Entity<'tu>, parent: Entity<'tu>) -> EntityVisitResult {
self(entity, parent)
}
}
extern fn visit(
cursor: ffi::CXCursor, parent: ffi::CXCursor, data: ffi::CXClientData
) -> ffi::CXChildVisitResult {
unsafe {
let &mut (tu, ref mut callback):
&mut (&TranslationUnit, Box<EntityCallback>) =
mem::transmute(data);
let entity = Entity::from_raw(cursor, tu);
let parent = Entity::from_raw(parent, tu);
mem::transmute(callback.call(entity, parent))
}
}
let mut data = (self.tu, Box::new(f) as Box<EntityCallback>);
unsafe { ffi::clang_visitChildren(self.raw, visit, mem::transmute(&mut data)) != 0 }
}
pub fn is_attribute(&self) -> bool {
unsafe { ffi::clang_isAttribute(self.raw.kind) != 0 }
}
pub fn is_declaration(&self) -> bool {
unsafe { ffi::clang_isDeclaration(self.raw.kind) != 0 }
}
pub fn is_expression(&self) -> bool {
unsafe { ffi::clang_isExpression(self.raw.kind) != 0 }
}
pub fn is_preprocessing(&self) -> bool {
unsafe { ffi::clang_isPreprocessing(self.raw.kind) != 0 }
}
pub fn is_reference(&self) -> bool {
unsafe { ffi::clang_isReference(self.raw.kind) != 0 }
}
pub fn is_statement(&self) -> bool {
unsafe { ffi::clang_isStatement(self.raw.kind) != 0 }
}
pub fn is_unexposed(&self) -> bool {
unsafe { ffi::clang_isUnexposed(self.raw.kind) != 0 }
}
}
impl<'tu> cmp::Eq for Entity<'tu> { }
impl<'tu> cmp::PartialEq for Entity<'tu> {
fn eq(&self, other: &Entity<'tu>) -> bool {
unsafe { ffi::clang_equalCursors(self.raw, other.raw) != 0 }
}
}
impl<'tu> fmt::Debug for Entity<'tu> {
fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
formatter.debug_struct("Entity")
.field("location", &self.get_location())
.field("kind", &self.get_kind())
.field("display_name", &self.get_display_name())
.finish()
}
}
impl<'tu> hash::Hash for Entity<'tu> {
fn hash<H: hash::Hasher>(&self, hasher: &mut H) {
unsafe {
let integer = ffi::clang_hashCursor(self.raw);
let slice = slice::from_raw_parts(mem::transmute(&integer), mem::size_of_val(&integer));
hasher.write(slice);
}
}
}
#[derive(Copy, Clone)]
pub struct File<'tu> {
ptr: ffi::CXFile,
tu: &'tu TranslationUnit<'tu>,
}
impl<'tu> File<'tu> {
fn from_ptr(ptr: ffi::CXFile, tu: &'tu TranslationUnit<'tu>) -> File<'tu> {
File { ptr: ptr, tu: tu }
}
pub fn get_id(&self) -> (u64, u64, u64) {
unsafe {
let mut id = mem::uninitialized();
ffi::clang_getFileUniqueID(self.ptr, &mut id);
(id.data[0] as u64, id.data[1] as u64, id.data[2] as u64)
}
}
pub fn get_includes(&self) -> Vec<Entity<'tu>> {
let mut includes = vec![];
self.visit_includes(|e, _| {
includes.push(e);
true
});
includes
}
pub fn get_location(&self, line: u32, column: u32) -> SourceLocation<'tu> {
if line == 0 || column == 0 {
panic!("`line` or `column` is `0`");
}
let (line, column) = (line, column) as (c_uint, c_uint);
let location = unsafe { ffi::clang_getLocation(self.tu.ptr, self.ptr, line, column) };
SourceLocation::from_raw(location, self.tu)
}
pub fn get_module(&self) -> Option<Module<'tu>> {
let module = unsafe { ffi::clang_getModuleForFile(self.tu.ptr, self.ptr) };
module.map(|m| Module::from_ptr(m, self.tu))
}
pub fn get_offset_location(&self, offset: u32) -> SourceLocation<'tu> {
let offset = offset as c_uint;
let location = unsafe { ffi::clang_getLocationForOffset(self.tu.ptr, self.ptr, offset) };
SourceLocation::from_raw(location, self.tu)
}
pub fn get_path(&self) -> PathBuf {
let path = unsafe { ffi::clang_getFileName(self.ptr) };
Path::new(&to_string(path)).into()
}
pub fn get_references(&self, entity: Entity<'tu>) -> Vec<Entity<'tu>> {
let mut references = vec![];
self.visit_references(entity, |e, _| {
references.push(e);
true
});
references
}
pub fn get_skipped_ranges(&self) -> Vec<SourceRange<'tu>> {
unsafe {
let raw = ffi::clang_getSkippedRanges(self.tu.ptr, self.ptr);
let raws = slice::from_raw_parts((*raw).ranges, (*raw).count as usize);
let ranges = raws.iter().map(|r| SourceRange::from_raw(*r, self.tu)).collect();
ffi::clang_disposeSourceRangeList(raw);
ranges
}
}
pub fn get_time(&self) -> time_t {
unsafe { ffi::clang_getFileTime(self.ptr) }
}
pub fn is_include_guarded(&self) -> bool {
unsafe { ffi::clang_isFileMultipleIncludeGuarded(self.tu.ptr, self.ptr) != 0 }
}
pub fn visit_includes<F: FnMut(Entity<'tu>, SourceRange<'tu>) -> bool>(&self, f: F) -> bool {
visit(self.tu, f, |v| unsafe { ffi::clang_findIncludesInFile(self.tu.ptr, self.ptr, v) })
}
pub fn visit_references<F: FnMut(Entity<'tu>, SourceRange<'tu>) -> bool>(
&self, entity: Entity<'tu>, f: F
) -> bool {
visit(self.tu, f, |v| unsafe { ffi::clang_findReferencesInFile(entity.raw, self.ptr, v) })
}
}
impl<'tu> cmp::Eq for File<'tu> { }
impl<'tu> cmp::PartialEq for File<'tu> {
fn eq(&self, other: &File<'tu>) -> bool {
self.get_id() == other.get_id()
}
}
impl<'tu> fmt::Debug for File<'tu> {
fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
formatter.debug_struct("File").field("path", &self.get_path()).finish()
}
}
impl<'tu> hash::Hash for File<'tu> {
fn hash<H: hash::Hasher>(&self, hasher: &mut H) {
self.get_id().hash(hasher);
}
}
options! {
options FormatOptions: CXDiagnosticDisplayOptions {
pub source_location: CXDiagnostic_DisplaySourceLocation,
pub column: CXDiagnostic_DisplayColumn,
pub source_ranges: CXDiagnostic_DisplaySourceRanges,
pub option: CXDiagnostic_DisplayOption,
pub category_id: CXDiagnostic_DisplayCategoryId,
pub category_name: CXDiagnostic_DisplayCategoryName,
}
}
impl Default for FormatOptions {
fn default() -> FormatOptions {
unsafe { FormatOptions::from(ffi::clang_defaultDiagnosticDisplayOptions()) }
}
}
pub struct Index<'c> {
ptr: ffi::CXIndex,
_marker: PhantomData<&'c Clang>,
}
impl<'c> Index<'c> {
pub fn new(_: &'c Clang, exclude: bool, diagnostics: bool) -> Index<'c> {
let ptr = unsafe { ffi::clang_createIndex(exclude as c_int, diagnostics as c_int) };
Index { ptr: ptr, _marker: PhantomData }
}
pub fn get_thread_options(&self) -> ThreadOptions {
unsafe { ThreadOptions::from(ffi::clang_CXIndex_getGlobalOptions(self.ptr)) }
}
pub fn set_thread_options(&mut self, options: ThreadOptions) {
unsafe { ffi::clang_CXIndex_setGlobalOptions(self.ptr, options.into()); }
}
}
impl<'c> Drop for Index<'c> {
fn drop(&mut self) {
unsafe { ffi::clang_disposeIndex(self.ptr); }
}
}
impl<'c> fmt::Debug for Index<'c> {
fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
formatter.debug_struct("Index").finish()
}
}
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
pub struct Location<'tu> {
pub file: File<'tu>,
pub line: u32,
pub column: u32,
pub offset: u32,
}
#[derive(Copy, Clone)]
pub struct Module<'tu> {
ptr: ffi::CXModule,
tu: &'tu TranslationUnit<'tu>,
}
impl<'tu> Module<'tu> {
fn from_ptr(ptr: ffi::CXModule, tu: &'tu TranslationUnit<'tu>) -> Module<'tu> {
Module { ptr: ptr, tu: tu }
}
pub fn get_file(&self) -> File<'tu> {
let ptr = unsafe { ffi::clang_Module_getASTFile(self.ptr) };
File::from_ptr(ptr, self.tu)
}
pub fn get_full_name(&self) -> String {
let name = unsafe { ffi::clang_Module_getFullName(self.ptr) };
to_string(name)
}
pub fn get_name(&self) -> String {
let name = unsafe { ffi::clang_Module_getName(self.ptr) };
to_string(name)
}
pub fn get_parent(&self) -> Option<Module<'tu>> {
let parent = unsafe { ffi::clang_Module_getParent(self.ptr) };
parent.map(|p| Module::from_ptr(p, self.tu))
}
pub fn get_top_level_headers(&self) -> Vec<File<'tu>> {
iter!(
clang_Module_getNumTopLevelHeaders(self.tu.ptr, self.ptr),
clang_Module_getTopLevelHeader(self.tu.ptr, self.ptr),
).map(|h| File::from_ptr(h, self.tu)).collect()
}
pub fn is_system(&self) -> bool {
unsafe { ffi::clang_Module_isSystem(self.ptr) != 0 }
}
}
impl<'tu> cmp::Eq for Module<'tu> { }
impl<'tu> cmp::PartialEq for Module<'tu> {
fn eq(&self, other: &Module<'tu>) -> bool {
self.get_file() == other.get_file() && self.get_full_name() == other.get_full_name()
}
}
impl<'tu> fmt::Debug for Module<'tu> {
fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
formatter.debug_struct("Module")
.field("file", &self.get_file())
.field("full_name", &self.get_full_name())
.finish()
}
}
options! {
options ObjCAttributes: CXObjCPropertyAttrKind {
pub readonly: CXObjCPropertyAttr_readonly,
pub getter: CXObjCPropertyAttr_getter,
pub assign: CXObjCPropertyAttr_assign,
pub readwrite: CXObjCPropertyAttr_readwrite,
pub retain: CXObjCPropertyAttr_retain,
pub copy: CXObjCPropertyAttr_copy,
pub nonatomic: CXObjCPropertyAttr_nonatomic,
pub setter: CXObjCPropertyAttr_setter,
pub atomic: CXObjCPropertyAttr_atomic,
pub weak: CXObjCPropertyAttr_weak,
pub strong: CXObjCPropertyAttr_strong,
pub unsafe_retained: CXObjCPropertyAttr_unsafe_unretained,
}
}
options! {
options ObjCQualifiers: CXObjCDeclQualifierKind {
pub in_: CXObjCDeclQualifier_In,
pub inout: CXObjCDeclQualifier_Inout,
pub out: CXObjCDeclQualifier_Out,
pub bycopy: CXObjCDeclQualifier_Bycopy,
pub byref: CXObjCDeclQualifier_Byref,
pub oneway: CXObjCDeclQualifier_Oneway,
}
}
options! {
#[derive(Default)]
options ParseOptions: CXTranslationUnit_Flags {
pub cached_completion_results: CXTranslationUnit_CacheCompletionResults,
pub detailed_preprocessing_record: CXTranslationUnit_DetailedPreprocessingRecord,
pub briefs_in_completion_results: CXTranslationUnit_IncludeBriefCommentsInCodeCompletion,
pub incomplete: CXTranslationUnit_Incomplete,
pub skipped_function_bodies: CXTranslationUnit_SkipFunctionBodies,
}
}
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub struct PlatformAvailability {
pub platform: String,
pub unavailable: bool,
pub introduced: Option<Version>,
pub deprecated: Option<Version>,
pub obsoleted: Option<Version>,
pub message: Option<String>,
}
impl PlatformAvailability {
fn from_raw(mut raw: ffi::CXPlatformAvailability) -> PlatformAvailability {
let availability = PlatformAvailability {
platform: to_string(raw.Platform),
unavailable: raw.Unavailable != 0,
introduced: raw.Introduced.map(Version::from_raw),
deprecated: raw.Deprecated.map(Version::from_raw),
obsoleted: raw.Obsoleted.map(Version::from_raw),
message: to_string_option(raw.Message),
};
unsafe { ffi::clang_disposeCXPlatformAvailability(&mut raw); }
availability
}
}
macro_rules! location {
($function:ident, $location:expr, $tu:expr) => ({
let (mut file, mut line, mut column, mut offset) = mem::uninitialized();
ffi::$function($location, &mut file, &mut line, &mut column, &mut offset);
Location {
file: File::from_ptr(file, $tu),
line: line as u32,
column: column as u32,
offset: offset as u32,
}
});
}
#[derive(Copy, Clone)]
pub struct SourceLocation<'tu> {
raw: ffi::CXSourceLocation,
tu: &'tu TranslationUnit<'tu>,
}
impl<'tu> SourceLocation<'tu> {
fn from_raw(raw: ffi::CXSourceLocation, tu: &'tu TranslationUnit<'tu>) -> SourceLocation<'tu> {
SourceLocation { raw: raw, tu: tu }
}
pub fn get_entity(&self) -> Option<Entity<'tu>> {
unsafe { ffi::clang_getCursor(self.tu.ptr, self.raw).map(|c| Entity::from_raw(c, self.tu)) }
}
pub fn get_expansion_location(&self) -> Location<'tu> {
unsafe { location!(clang_getExpansionLocation, self.raw, self.tu) }
}
pub fn get_file_location(&self) -> Location<'tu> {
unsafe { location!(clang_getFileLocation, self.raw, self.tu) }
}
pub fn get_presumed_location(&self) -> (String, u32, u32) {
unsafe {
let (mut file, mut line, mut column) = mem::uninitialized();
ffi::clang_getPresumedLocation(self.raw, &mut file, &mut line, &mut column);
(to_string(file), line as u32, column as u32)
}
}
pub fn get_spelling_location(&self) -> Location<'tu> {
unsafe { location!(clang_getSpellingLocation, self.raw, self.tu) }
}
pub fn is_in_main_file(&self) -> bool {
unsafe { ffi::clang_Location_isFromMainFile(self.raw) != 0 }
}
pub fn is_in_system_header(&self) -> bool {
unsafe { ffi::clang_Location_isInSystemHeader(self.raw) != 0 }
}
}
impl<'tu> cmp::Eq for SourceLocation<'tu> { }
impl<'tu> cmp::PartialEq for SourceLocation<'tu> {
fn eq(&self, other: &SourceLocation<'tu>) -> bool {
unsafe { ffi::clang_equalLocations(self.raw, other.raw) != 0 }
}
}
impl<'tu> fmt::Debug for SourceLocation<'tu> {
fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
let location = self.get_spelling_location();
formatter.debug_struct("SourceLocation")
.field("file", &location.file)
.field("line", &location.line)
.field("column", &location.column)
.field("offset", &location.offset)
.finish()
}
}
impl<'tu> hash::Hash for SourceLocation<'tu> {
fn hash<H: hash::Hasher>(&self, hasher: &mut H) {
self.get_spelling_location().hash(hasher)
}
}
#[derive(Copy, Clone)]
pub struct SourceRange<'tu> {
raw: ffi::CXSourceRange,
tu: &'tu TranslationUnit<'tu>,
}
impl<'tu> SourceRange<'tu> {
fn from_raw(raw: ffi::CXSourceRange, tu: &'tu TranslationUnit<'tu>) -> SourceRange<'tu> {
SourceRange { raw: raw, tu: tu }
}
pub fn new(start: SourceLocation<'tu>, end: SourceLocation<'tu>) -> SourceRange<'tu> {
let raw = unsafe { ffi::clang_getRange(start.raw, end.raw) };
SourceRange::from_raw(raw, start.tu)
}
pub fn get_end(&self) -> SourceLocation<'tu> {
let end = unsafe { ffi::clang_getRangeEnd(self.raw) };
SourceLocation::from_raw(end, self.tu)
}
pub fn get_start(&self) -> SourceLocation<'tu> {
let start = unsafe { ffi::clang_getRangeStart(self.raw) };
SourceLocation::from_raw(start, self.tu)
}
pub fn tokenize(&self) -> Vec<Token<'tu>> {
unsafe {
let (mut raw, mut count) = mem::uninitialized();
ffi::clang_tokenize(self.tu.ptr, self.raw, &mut raw, &mut count);
let raws = slice::from_raw_parts(raw, count as usize);
let tokens = raws.iter().map(|t| Token::from_raw(*t, self.tu)).collect();
ffi::clang_disposeTokens(self.tu.ptr, raw, count);
tokens
}
}
}
impl<'tu> cmp::Eq for SourceRange<'tu> { }
impl<'tu> cmp::PartialEq for SourceRange<'tu> {
fn eq(&self, other: &SourceRange<'tu>) -> bool {
unsafe { ffi::clang_equalRanges(self.raw, other.raw) != 0 }
}
}
impl<'tu> fmt::Debug for SourceRange<'tu> {
fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
formatter.debug_struct("SourceRange")
.field("start", &self.get_start())
.field("end", &self.get_end())
.finish()
}
}
impl<'tu> hash::Hash for SourceRange<'tu> {
fn hash<H: hash::Hasher>(&self, hasher: &mut H) {
self.get_start().hash(hasher);
self.get_end().hash(hasher);
}
}
options! {
#[derive(Default)]
options ThreadOptions: CXGlobalOptFlags {
pub editing: CXGlobalOpt_ThreadBackgroundPriorityForEditing,
pub indexing: CXGlobalOpt_ThreadBackgroundPriorityForIndexing,
}
}
#[derive(Copy, Clone)]
pub struct Token<'tu> {
raw: ffi::CXToken,
tu: &'tu TranslationUnit<'tu>,
}
impl<'tu> Token<'tu> {
fn from_raw(raw: ffi::CXToken, tu: &'tu TranslationUnit<'tu>) -> Token<'tu> {
Token{ raw: raw, tu: tu }
}
pub fn get_kind(&self) -> TokenKind {
unsafe { mem::transmute(ffi::clang_getTokenKind(self.raw)) }
}
pub fn get_location(&self) -> SourceLocation<'tu> {
unsafe {
let raw = ffi::clang_getTokenLocation(self.tu.ptr, self.raw);
SourceLocation::from_raw(raw, self.tu)
}
}
pub fn get_range(&self) -> SourceRange<'tu> {
unsafe { SourceRange::from_raw(ffi::clang_getTokenExtent(self.tu.ptr, self.raw), self.tu) }
}
pub fn get_spelling(&self) -> String {
unsafe { to_string(ffi::clang_getTokenSpelling(self.tu.ptr, self.raw)) }
}
}
impl<'tu> fmt::Debug for Token<'tu> {
fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
formatter.debug_struct("Token")
.field("range", &self.get_range())
.field("kind", &self.get_kind())
.field("spelling", &self.get_spelling())
.finish()
}
}
pub struct TranslationUnit<'i> {
ptr: ffi::CXTranslationUnit,
_marker: PhantomData<&'i Index<'i>>,
}
impl<'i> TranslationUnit<'i> {
fn from_ptr(ptr: ffi::CXTranslationUnit) -> TranslationUnit<'i> {
TranslationUnit{ ptr: ptr, _marker: PhantomData }
}
pub fn from_ast<F: AsRef<Path>>(
index: &'i Index, file: F
) -> Result<TranslationUnit<'i>, ()> {
let ptr = unsafe { ffi::clang_createTranslationUnit(index.ptr, from_path(file).as_ptr()) };
ptr.map(TranslationUnit::from_ptr).ok_or(())
}
pub fn from_source<F: AsRef<Path>>(
index: &'i Index,
file: F,
arguments: &[&str],
unsaved: &[Unsaved],
options: ParseOptions,
) -> Result<TranslationUnit<'i>, SourceError> {
let arguments = arguments.iter().map(from_string).collect::<Vec<_>>();
let arguments = arguments.iter().map(|a| a.as_ptr()).collect::<Vec<_>>();
let unsaved = unsaved.iter().map(|u| u.as_raw()).collect::<Vec<_>>();
unsafe {
let mut ptr = mem::uninitialized();
let code = ffi::clang_parseTranslationUnit2(
index.ptr,
from_path(file).as_ptr(),
arguments.as_ptr(),
arguments.len() as c_int,
mem::transmute(unsaved.as_ptr()),
unsaved.len() as c_uint,
options.into(),
&mut ptr,
);
match code {
ffi::CXErrorCode::Success => Ok(TranslationUnit::from_ptr(ptr)),
ffi::CXErrorCode::ASTReadError => Err(SourceError::AstDeserialization),
ffi::CXErrorCode::Crashed => Err(SourceError::Crash),
ffi::CXErrorCode::Failure => Err(SourceError::Unknown),
_ => unreachable!(),
}
}
}
pub fn annotate(&'i self, tokens: &[Token<'i>]) -> Vec<Option<Entity<'i>>> {
unsafe {
let mut raws = vec![mem::uninitialized(); tokens.len()];
ffi::clang_annotateTokens(
self.ptr, mem::transmute(tokens.as_ptr()), tokens.len() as c_uint, raws.as_mut_ptr()
);
raws.iter().map(|e| e.map(|e| Entity::from_raw(e, self))).collect()
}
}
pub fn complete<F: AsRef<Path>>(
&self,
file: F,
line: u32,
column: u32,
unsaved: &[Unsaved],
options: CompletionOptions,
) -> CompletionResults {
unsafe {
let ptr = ffi::clang_codeCompleteAt(
self.ptr,
from_path(file).as_ptr(),
line as c_uint,
column as c_uint,
mem::transmute(unsaved.as_ptr()),
unsaved.len() as c_uint,
options.into(),
);
CompletionResults::from_ptr(ptr)
}
}
pub fn get_diagnostics(&'i self) -> Vec<Diagnostic<'i>> {
iter!(clang_getNumDiagnostics(self.ptr), clang_getDiagnostic(self.ptr),).map(|d| {
Diagnostic::from_ptr(d, self)
}).collect()
}
pub fn get_entity(&'i self) -> Entity<'i> {
unsafe { Entity::from_raw(ffi::clang_getTranslationUnitCursor(self.ptr), self) }
}
pub fn get_file<F: AsRef<Path>>(&'i self, file: F) -> Option<File<'i>> {
let file = unsafe { ffi::clang_getFile(self.ptr, from_path(file).as_ptr()) };
file.map(|f| File::from_ptr(f, self))
}
pub fn get_memory_usage(&self) -> HashMap<MemoryUsage, usize> {
unsafe {
let raw = ffi::clang_getCXTUResourceUsage(self.ptr);
let raws = slice::from_raw_parts(raw.entries, raw.numEntries as usize);
let usage = raws.iter().map(|u| (mem::transmute(u.kind), u.amount as usize)).collect();
ffi::clang_disposeCXTUResourceUsage(raw);
usage
}
}
pub fn save<F: AsRef<Path>>(&self, file: F) -> Result<(), SaveError> {
let code = unsafe {
ffi::clang_saveTranslationUnit(
self.ptr, from_path(file).as_ptr(), ffi::CXSaveTranslationUnit_None
)
};
match code {
ffi::CXSaveError::None => Ok(()),
ffi::CXSaveError::InvalidTU => Err(SaveError::Errors),
ffi::CXSaveError::Unknown => Err(SaveError::Unknown),
_ => unreachable!(),
}
}
pub fn reparse(self, unsaved: &[Unsaved]) -> Result<TranslationUnit<'i>, SourceError> {
let unsaved = unsaved.iter().map(|u| u.as_raw()).collect::<Vec<_>>();
unsafe {
let code = ffi::clang_reparseTranslationUnit(
self.ptr,
unsaved.len() as c_uint,
mem::transmute(unsaved.as_ptr()),
ffi::CXReparse_None,
);
match code {
ffi::CXErrorCode::Success => Ok(self),
ffi::CXErrorCode::ASTReadError => Err(SourceError::AstDeserialization),
ffi::CXErrorCode::Crashed => Err(SourceError::Crash),
ffi::CXErrorCode::Failure => Err(SourceError::Unknown),
_ => unreachable!(),
}
}
}
}
impl<'i> Drop for TranslationUnit<'i> {
fn drop(&mut self) {
unsafe { ffi::clang_disposeTranslationUnit(self.ptr); }
}
}
impl<'i> fmt::Debug for TranslationUnit<'i> {
fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
let spelling = unsafe { ffi::clang_getTranslationUnitSpelling(self.ptr) };
formatter.debug_struct("TranslationUnit").field("spelling", &to_string(spelling)).finish()
}
}
#[derive(Copy, Clone)]
pub struct Type<'tu> {
raw: ffi::CXType,
tu: &'tu TranslationUnit<'tu>,
}
impl<'tu> Type<'tu> {
fn from_raw(raw: ffi::CXType, tu: &'tu TranslationUnit<'tu>) -> Type<'tu> {
Type { raw: raw, tu: tu }
}
pub fn get_alignof(&self) -> Result<usize, AlignofError> {
unsafe {
match ffi::clang_Type_getAlignOf(self.raw) {
-3 => Err(AlignofError::Dependent),
-2 => Err(AlignofError::Incomplete),
other => Ok(other as usize),
}
}
}
pub fn get_argument_types(&self) -> Option<Vec<Type<'tu>>> {
iter_option!(
clang_getNumArgTypes(self.raw),
clang_getArgType(self.raw),
).map(|i| i.map(|t| Type::from_raw(t, self.tu)).collect())
}
pub fn get_calling_convention(&self) -> Option<CallingConvention> {
unsafe {
match ffi::clang_getFunctionTypeCallingConv(self.raw) {
ffi::CXCallingConv::Invalid => None,
other => Some(mem::transmute(other)),
}
}
}
pub fn get_canonical_type(&self) -> Type<'tu> {
unsafe { Type::from_raw(ffi::clang_getCanonicalType(self.raw), self.tu) }
}
pub fn get_class_type(&self) -> Option<Type<'tu>> {
unsafe { ffi::clang_Type_getClassType(self.raw).map(|t| Type::from_raw(t, self.tu)) }
}
pub fn get_declaration(&self) -> Option<Entity<'tu>> {
unsafe { ffi::clang_getTypeDeclaration(self.raw).map(|e| Entity::from_raw(e, self.tu)) }
}
pub fn get_display_name(&self) -> String {
unsafe { to_string(ffi::clang_getTypeSpelling(self.raw)) }
}
pub fn get_element_type(&self) -> Option<Type<'tu>> {
unsafe { ffi::clang_getElementType(self.raw).map(|t| Type::from_raw(t, self.tu)) }
}
#[cfg(feature="clang_3_7")]
pub fn get_fields(&self) -> Option<Vec<Entity<'tu>>> {
if self.get_kind() == TypeKind::Record {
let mut fields = vec![];
self.visit_fields(|e| {
fields.push(e);
true
});
Some(fields)
} else {
None
}
}
pub fn get_offsetof<F: AsRef<str>>(&self, field: F) -> Result<usize, OffsetofError> {
unsafe {
match ffi::clang_Type_getOffsetOf(self.raw, from_string(field).as_ptr()) {
-1 => Err(OffsetofError::Parent),
-2 => Err(OffsetofError::Incomplete),
-3 => Err(OffsetofError::Dependent),
-5 => Err(OffsetofError::Name),
other => Ok(other as usize),
}
}
}
pub fn get_kind(&self) -> TypeKind {
unsafe { mem::transmute(self.raw.kind) }
}
pub fn get_pointee_type(&self) -> Option<Type<'tu>> {
unsafe { ffi::clang_getPointeeType(self.raw).map(|t| Type::from_raw(t, self.tu)) }
}
pub fn get_ref_qualifier(&self) -> Option<RefQualifier> {
unsafe {
match ffi::clang_Type_getCXXRefQualifier(self.raw) {
ffi::CXRefQualifierKind::None => None,
other => Some(mem::transmute(other)),
}
}
}
pub fn get_result_type(&self) -> Option<Type<'tu>> {
unsafe { ffi::clang_getResultType(self.raw).map(|t| Type::from_raw(t, self.tu)) }
}
pub fn get_size(&self) -> Option<usize> {
let size = unsafe { ffi::clang_getNumElements(self.raw) };
if size >= 0 {
Some(size as usize)
} else {
None
}
}
pub fn get_sizeof(&self) -> Result<usize, SizeofError> {
unsafe {
match ffi::clang_Type_getSizeOf(self.raw) {
-2 => Err(SizeofError::Incomplete),
-3 => Err(SizeofError::Dependent),
-4 => Err(SizeofError::VariableSize),
other => Ok(other as usize),
}
}
}
pub fn get_template_argument_types(&self) -> Option<Vec<Option<Type<'tu>>>> {
iter_option!(
clang_Type_getNumTemplateArguments(self.raw),
clang_Type_getTemplateArgumentAsType(self.raw),
).map(|i| i.map(|t| t.map(|t| Type::from_raw(t, self.tu))).collect())
}
pub fn is_const_qualified(&self) -> bool {
unsafe { ffi::clang_isConstQualifiedType(self.raw) != 0 }
}
pub fn is_pod(&self) -> bool {
unsafe { ffi::clang_isPODType(self.raw) != 0 }
}
pub fn is_restrict_qualified(&self) -> bool {
unsafe { ffi::clang_isRestrictQualifiedType(self.raw) != 0 }
}
pub fn is_variadic(&self) -> bool {
unsafe { ffi::clang_isFunctionTypeVariadic(self.raw) != 0 }
}
pub fn is_volatile_qualified(&self) -> bool {
unsafe { ffi::clang_isVolatileQualifiedType(self.raw) != 0 }
}
#[cfg(feature="clang_3_7")]
pub fn visit_fields<F: FnMut(Entity<'tu>) -> bool>(&self, f: F) -> Option<bool> {
if self.get_kind() != TypeKind::Record {
return None;
}
trait Callback<'tu> {
fn call(&mut self, field: Entity<'tu>) -> bool;
}
impl<'tu, F: FnMut(Entity<'tu>) -> bool> Callback<'tu> for F {
fn call(&mut self, field: Entity<'tu>) -> bool {
self(field)
}
}
extern fn visit(cursor: ffi::CXCursor, data: ffi::CXClientData) -> ffi::CXVisitorResult {
unsafe {
let &mut (tu, ref mut callback):
&mut (&TranslationUnit, Box<Callback>) =
mem::transmute(data);
if callback.call(Entity::from_raw(cursor, tu)) {
ffi::CXVisitorResult::Continue
} else {
ffi::CXVisitorResult::Break
}
}
}
let mut data = (self.tu, Box::new(f) as Box<Callback>);
unsafe {
let data = mem::transmute(&mut data);
Some(ffi::clang_Type_visitFields(self.raw, visit, data) == ffi::CXVisitorResult::Break)
}
}
}
impl<'tu> cmp::Eq for Type<'tu> { }
impl<'tu> cmp::PartialEq for Type<'tu> {
fn eq(&self, other: &Type<'tu>) -> bool {
unsafe { ffi::clang_equalTypes(self.raw, other.raw) != 0 }
}
}
impl<'tu> fmt::Debug for Type<'tu> {
fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
formatter.debug_struct("Type")
.field("kind", &self.get_kind())
.field("display_name", &self.get_display_name())
.finish()
}
}
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub struct Unsaved {
path: std::ffi::CString,
contents: std::ffi::CString,
}
impl Unsaved {
pub fn new<P: AsRef<Path>, C: AsRef<str>>(path: P, contents: C) -> Unsaved {
Unsaved { path: from_path(path), contents: from_string(contents) }
}
fn as_raw(&self) -> ffi::CXUnsavedFile {
ffi::CXUnsavedFile {
Filename: self.path.as_ptr(),
Contents: self.contents.as_ptr(),
Length: self.contents.as_bytes().len() as c_ulong,
}
}
}
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct Usr(pub String);
impl Usr {
pub fn from_objc_category<C1: AsRef<str>, C2: AsRef<str>>(class: C1, category: C2) -> Usr {
let raw = unsafe {
ffi::clang_constructUSR_ObjCCategory(
from_string(class).as_ptr(), from_string(category).as_ptr()
)
};
Usr(to_string(raw))
}
pub fn from_objc_class<C: AsRef<str>>(class: C) -> Usr {
unsafe { Usr(to_string(ffi::clang_constructUSR_ObjCClass(from_string(class).as_ptr()))) }
}
pub fn from_objc_ivar<N: AsRef<str>>(class: &Usr, name: N) -> Usr {
with_string(&class.0, |s| {
let raw = unsafe { ffi::clang_constructUSR_ObjCIvar(from_string(name).as_ptr(), s) };
Usr(to_string(raw))
})
}
pub fn from_objc_method<N: AsRef<str>>(class: &Usr, name: N, instance: bool) -> Usr {
with_string(&class.0, |s| {
let raw = unsafe {
ffi::clang_constructUSR_ObjCMethod(
from_string(name).as_ptr(), instance as c_uint, s
)
};
Usr(to_string(raw))
})
}
pub fn from_objc_property<N: AsRef<str>>(class: &Usr, name: N) -> Usr {
with_string(&class.0, |s| {
let raw = unsafe {
ffi::clang_constructUSR_ObjCProperty(from_string(name).as_ptr(), s)
};
Usr(to_string(raw))
})
}
pub fn from_objc_protocol<P: AsRef<str>>(protocol: P) -> Usr {
unsafe {
Usr(to_string(ffi::clang_constructUSR_ObjCProtocol(from_string(protocol).as_ptr())))
}
}
}
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
pub struct Version {
pub x: i32,
pub y: i32,
pub z: i32,
}
impl Version {
fn from_raw(raw: ffi::CXVersion) -> Version {
Version { x: raw.Major as i32, y: raw.Minor as i32, z: raw.Subminor as i32 }
}
}
fn from_path<P: AsRef<Path>>(path: P) -> std::ffi::CString {
from_string(path.as_ref().as_os_str().to_str().expect("invalid C string"))
}
fn from_string<S: AsRef<str>>(string: S) -> std::ffi::CString {
std::ffi::CString::new(string.as_ref()).expect("invalid C string")
}
fn to_string(clang: ffi::CXString) -> String {
unsafe {
let c = std::ffi::CStr::from_ptr(ffi::clang_getCString(clang));
let rust = c.to_str().expect("invalid Rust string").into();
ffi::clang_disposeString(clang);
rust
}
}
fn to_string_option(clang: ffi::CXString) -> Option<String> {
clang.map(to_string).and_then(|s| {
if !s.is_empty() {
Some(s)
} else {
None
}
})
}
fn visit<'tu, F, G>(tu: &'tu TranslationUnit<'tu>, f: F, g: G) -> bool
where F: FnMut(Entity<'tu>, SourceRange<'tu>) -> bool,
G: Fn(ffi::CXCursorAndRangeVisitor) -> ffi::CXResult
{
trait Callback<'tu> {
fn call(&mut self, entity: Entity<'tu>, range: SourceRange<'tu>) -> bool;
}
impl<'tu, F: FnMut(Entity<'tu>, SourceRange<'tu>) -> bool> Callback<'tu> for F {
fn call(&mut self, entity: Entity<'tu>, range: SourceRange<'tu>) -> bool {
self(entity, range)
}
}
extern fn visit(
data: ffi::CXClientData, cursor: ffi::CXCursor, range: ffi::CXSourceRange
) -> ffi::CXVisitorResult {
unsafe {
let &mut (tu, ref mut callback):
&mut (&TranslationUnit, Box<Callback>) =
mem::transmute(data);
if callback.call(Entity::from_raw(cursor, tu), SourceRange::from_raw(range, tu)) {
ffi::CXVisitorResult::Continue
} else {
ffi::CXVisitorResult::Break
}
}
}
let mut data = (tu, Box::new(f) as Box<Callback>);
let visitor = ffi::CXCursorAndRangeVisitor {
context: unsafe { mem::transmute(&mut data) }, visit: visit
};
g(visitor) == ffi::CXResult::VisitBreak
}
fn with_string<S: AsRef<str>, T, F: FnOnce(ffi::CXString) -> T>(string: S, f: F) -> T {
let string = from_string(string);
let cxstring = unsafe {
ffi::CXString { data: mem::transmute(string.as_ptr()), private_flags: 0 }
};
f(cxstring)
}