use std::collections::HashMap;
use super::ast::*;
use super::CLangStandard;
pub struct CXIndex {
pub translation_units: HashMap<String, CXTranslationUnit>,
pub global_options: CXGlobalOptions,
}
#[derive(Debug, Clone)]
pub struct CXGlobalOptions {
pub exclude_declarations_from_pch: bool,
pub display_diagnostics: bool,
}
impl Default for CXGlobalOptions {
fn default() -> Self {
Self {
exclude_declarations_from_pch: false,
display_diagnostics: true,
}
}
}
impl CXIndex {
pub fn create() -> Self {
Self {
translation_units: HashMap::new(),
global_options: CXGlobalOptions::default(),
}
}
pub fn create_with_options(exclude_pch: bool, display_diags: bool) -> Self {
Self {
translation_units: HashMap::new(),
global_options: CXGlobalOptions {
exclude_declarations_from_pch: exclude_pch,
display_diagnostics: display_diags,
},
}
}
pub fn dispose(&mut self) {
self.translation_units.clear();
}
pub fn parse_translation_unit(
&mut self,
source_file: &str,
args: &[String],
unsaved_files: &[CXUnsavedFile],
options: CXTranslationUnitFlags,
) -> Result<CXTranslationUnit, String> {
let source = std::fs::read_to_string(source_file)
.map_err(|e| format!("Cannot read {}: {}", source_file, e))?;
let tu = CXTranslationUnit::parse(source_file, &source, args, unsaved_files, options);
self.translation_units
.insert(source_file.to_string(), tu.clone());
Ok(tu)
}
pub fn parse_translation_unit_from_source(
&mut self,
source_file: &str,
source: &str,
args: &[String],
options: CXTranslationUnitFlags,
) -> CXTranslationUnit {
let tu = CXTranslationUnit::parse(source_file, source, args, &[], options);
self.translation_units
.insert(source_file.to_string(), tu.clone());
tu
}
}
#[derive(Debug, Clone)]
pub struct CXUnsavedFile {
pub filename: String,
pub contents: String,
pub length: usize,
}
impl CXUnsavedFile {
pub fn new(filename: &str, contents: &str) -> Self {
Self {
filename: filename.to_string(),
contents: contents.to_string(),
length: contents.len(),
}
}
}
#[derive(Debug, Clone)]
pub struct CXTranslationUnitFlags {
pub detailed_preprocessing_record: bool,
pub incomplete: bool,
pub precompiled_preamble: bool,
pub cache_completion_results: bool,
pub for_serialization: bool,
pub cxx_chained_pch: bool,
pub skip_function_bodies: bool,
pub include_brief_comments_in_code_completion: bool,
pub create_preamble_on_first_parse: bool,
pub keep_going: bool,
pub single_file_parse: bool,
pub limit_skip_function_bodies_to_preambles: bool,
pub include_attributed_types: bool,
pub visit_implicit_attributes: bool,
pub ignore_non_errors_from_included_files: bool,
}
impl Default for CXTranslationUnitFlags {
fn default() -> Self {
Self {
detailed_preprocessing_record: false,
incomplete: false,
precompiled_preamble: false,
cache_completion_results: false,
for_serialization: false,
cxx_chained_pch: false,
skip_function_bodies: false,
include_brief_comments_in_code_completion: false,
create_preamble_on_first_parse: false,
keep_going: false,
single_file_parse: false,
limit_skip_function_bodies_to_preambles: false,
include_attributed_types: false,
visit_implicit_attributes: false,
ignore_non_errors_from_included_files: false,
}
}
}
impl CXTranslationUnitFlags {
pub fn none() -> Self {
Self::default()
}
}
#[derive(Debug, Clone)]
pub struct CXTranslationUnit {
pub filename: String,
pub ast: TranslationUnit,
pub diagnostics: Vec<CXDiagnostic>,
pub cursors: Vec<CXCursor>,
pub standard: CLangStandard,
}
impl CXTranslationUnit {
pub fn parse(
filename: &str,
source: &str,
_args: &[String],
_unsaved_files: &[CXUnsavedFile],
_options: CXTranslationUnitFlags,
) -> Self {
let standard = CLangStandard::C17;
let tu = TranslationUnit::new(filename);
Self {
filename: filename.to_string(),
ast: tu,
diagnostics: Vec::new(),
cursors: Vec::new(),
standard,
}
}
pub fn cursor(&self) -> CXCursor {
CXCursor {
kind: CXCursorKind::TranslationUnit,
spelling: self.filename.clone(),
location: CXSourceLocation {
file: self.filename.clone(),
line: 1,
column: 1,
offset: 0,
},
extent: CXSourceRange {
start: CXSourceLocation {
file: self.filename.clone(),
line: 1,
column: 1,
offset: 0,
},
end: CXSourceLocation {
file: self.filename.clone(),
line: 1,
column: 1,
offset: 0,
},
},
children: Vec::new(),
semantic_parent: None,
lexical_parent: None,
linkage: CXLinkageKind::External,
is_definition: true,
ty: None,
}
}
pub fn spelling(&self) -> &str {
&self.filename
}
pub fn file(&self, _location: &CXSourceLocation) -> String {
self.filename.clone()
}
pub fn location(&self, _file: &str, _line: u32, _column: u32) -> CXSourceLocation {
CXSourceLocation {
file: self.filename.clone(),
line: _line,
column: _column,
offset: 0,
}
}
pub fn diagnostics(&self) -> &[CXDiagnostic] {
&self.diagnostics
}
pub fn num_diagnostics(&self) -> u32 {
self.diagnostics.len() as u32
}
pub fn dispose(self) {
}
}
#[derive(Debug, Clone)]
pub struct CXCursor {
pub kind: CXCursorKind,
pub spelling: String,
pub location: CXSourceLocation,
pub extent: CXSourceRange,
pub children: Vec<CXCursor>,
pub semantic_parent: Option<Box<CXCursor>>,
pub lexical_parent: Option<Box<CXCursor>>,
pub linkage: CXLinkageKind,
pub is_definition: bool,
pub ty: Option<CXType>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum CXCursorKind {
UnexpectedDecl = 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,
ObjCMessageExpr = 18,
ObjCSelectorRef = 19,
ObjCProtocolRef = 20,
ObjCClassRef = 21,
NullStmt = 22,
CompoundStmt = 23,
CaseStmt = 24,
DefaultStmt = 25,
IfStmt = 26,
SwitchStmt = 27,
WhileStmt = 28,
DoStmt = 29,
ForStmt = 30,
GotoStmt = 31,
LabelStmt = 32,
ReturnStmt = 33,
BreakStmt = 34,
ContinueStmt = 35,
TranslationUnit = 300,
TypedefDecl = 40,
TypeAliasDecl = 41,
UsingDeclaration = 42,
UsingDirective = 43,
Namespace = 44,
NamespaceAlias = 45,
Constructor = 46,
Destructor = 47,
ConversionFunction = 48,
TemplateTypeParameter = 49,
NonTypeTemplateParameter = 50,
TemplateTemplateParameter = 51,
FunctionTemplate = 52,
ClassTemplate = 53,
ClassTemplatePartialSpecialization = 54,
NamespaceRef = 55,
MemberRef = 56,
LabelRef = 57,
OverloadedDeclRef = 58,
VariableRef = 59,
TypeRef = 60,
CXXBaseSpecifier = 61,
TemplateRef = 62,
ConstructorRef = 63,
DestructorRef = 64,
UnresolvedConstructor = 65,
UnresolvedDestructor = 66,
UnresolvedMemberExpr = 67,
CallExpr = 100,
BinaryOperator = 101,
UnaryOperator = 102,
ConditionalOperator = 103,
ArraySubscriptExpr = 104,
MemberRefExpr = 105,
IntegerLiteral = 106,
FloatingLiteral = 107,
CharacterLiteral = 108,
StringLiteral = 109,
ParenExpr = 110,
UnaryExpr = 111,
SizeOfExpr = 112,
OffsetOfExpr = 113,
AlignOfExpr = 114,
CompoundLiteralExpr = 115,
InitListExpr = 116,
GenericSelectionExpr = 117,
AtomicExpr = 118,
StmtExpr = 119,
BlockExpr = 120,
LambdaExpr = 121,
CXXStaticCastExpr = 122,
CXXDynamicCastExpr = 123,
CXXReinterpretCastExpr = 124,
CXXConstCastExpr = 125,
CXXFunctionalCastExpr = 126,
CXXTypeidExpr = 127,
CXXThrowExpr = 128,
CXXNewExpr = 129,
CXXDeleteExpr = 130,
CXXMemberCallExpr = 131,
CXXOperatorCallExpr = 132,
PackExpansionExpr = 133,
SizeOfPackExpr = 134,
FoldExpr = 135,
CoawaitExpr = 136,
CoyieldExpr = 137,
CoreturnExpr = 138,
ModuleImportDecl = 600,
TypeAliasTemplateDecl = 601,
StaticAssert = 602,
FriendDecl = 603,
ConceptDecl = 604,
RequiresExpr = 605,
NotImplemented = 999,
}
impl CXCursorKind {
pub fn is_declaration(&self) -> bool {
matches!(
self,
Self::StructDecl
| Self::UnionDecl
| Self::ClassDecl
| Self::EnumDecl
| Self::FunctionDecl
| Self::VarDecl
| Self::ParmDecl
| Self::TypedefDecl
| Self::Namespace
| Self::Constructor
| Self::Destructor
| Self::FunctionTemplate
| Self::ClassTemplate
| Self::TranslationUnit
)
}
pub fn is_expression(&self) -> bool {
matches!(
self,
Self::CallExpr
| Self::BinaryOperator
| Self::UnaryOperator
| Self::IntegerLiteral
| Self::FloatingLiteral
| Self::StringLiteral
| Self::CharacterLiteral
| Self::ParenExpr
| Self::ArraySubscriptExpr
| Self::MemberRefExpr
| Self::ConditionalOperator
| Self::SizeOfExpr
| Self::OffsetOfExpr
| Self::AlignOfExpr
| Self::CompoundLiteralExpr
| Self::InitListExpr
| Self::LambdaExpr
)
}
pub fn is_statement(&self) -> bool {
matches!(
self,
Self::NullStmt
| Self::CompoundStmt
| Self::IfStmt
| Self::SwitchStmt
| Self::WhileStmt
| Self::DoStmt
| Self::ForStmt
| Self::GotoStmt
| Self::ReturnStmt
| Self::BreakStmt
| Self::ContinueStmt
| Self::LabelStmt
| Self::CaseStmt
| Self::DefaultStmt
)
}
pub fn from_u32(val: u32) -> Self {
match val {
1 => Self::UnexpectedDecl,
2 => Self::StructDecl,
3 => Self::UnionDecl,
4 => Self::ClassDecl,
5 => Self::EnumDecl,
8 => Self::FunctionDecl,
9 => Self::VarDecl,
10 => Self::ParmDecl,
40 => Self::TypedefDecl,
100 => Self::CallExpr,
101 => Self::BinaryOperator,
106 => Self::IntegerLiteral,
109 => Self::StringLiteral,
300 => Self::TranslationUnit,
_ => Self::NotImplemented,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum CXLinkageKind {
Invalid,
NoLinkage,
Internal,
UniqueExternal,
External,
}
impl CXCursor {
pub fn new(kind: CXCursorKind, spelling: &str) -> Self {
Self {
kind,
spelling: spelling.to_string(),
location: CXSourceLocation::default(),
extent: CXSourceRange::default(),
children: Vec::new(),
semantic_parent: None,
lexical_parent: None,
linkage: CXLinkageKind::External,
is_definition: false,
ty: None,
}
}
pub fn visit_children<F>(&self, mut visitor: F)
where
F: FnMut(&CXCursor) -> CXChildVisitResult,
{
for child in &self.children {
let result = visitor(child);
match result {
CXChildVisitResult::Break => break,
CXChildVisitResult::Continue => continue,
CXChildVisitResult::Recurse => {
child.visit_children(|c| visitor(c));
}
}
}
}
pub fn semantic_parent(&self) -> Option<&CXCursor> {
self.semantic_parent.as_deref()
}
pub fn lexical_parent(&self) -> Option<&CXCursor> {
self.lexical_parent.as_deref()
}
pub fn linkage(&self) -> CXLinkageKind {
self.linkage
}
pub fn is_definition(&self) -> bool {
self.is_definition
}
pub fn cursor_type(&self) -> Option<&CXType> {
self.ty.as_ref()
}
}
pub enum CXChildVisitResult {
Break,
Continue,
Recurse,
}
#[derive(Debug, Clone, PartialEq, Default)]
pub struct CXType {
pub kind: CXTypeKind,
pub spelling: String,
pub size: u64,
pub alignment: u64,
pub pointee_type: Option<Box<CXType>>,
pub array_element_type: Option<Box<CXType>>,
pub array_size: i64,
pub result_type: Option<Box<CXType>>,
pub argument_types: Vec<CXType>,
pub is_const: bool,
pub is_volatile: bool,
pub is_restrict: bool,
pub num_template_args: i32,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub enum CXTypeKind {
#[default]
Invalid = 0,
Unexposed = 1,
Void = 2,
Bool = 3,
Char_U = 4,
UChar = 5,
Char16 = 6,
Char32 = 7,
UShort = 8,
UInt = 9,
ULong = 10,
ULongLong = 11,
Int128 = 12,
Char_S = 13,
SChar = 14,
WChar = 15,
Short = 16,
Int = 17,
Long = 18,
LongLong = 19,
Float = 20,
Double = 21,
LongDouble = 22,
Float128 = 30,
Pointer = 100,
BlockPointer = 101,
LValueReference = 102,
RValueReference = 103,
Record = 104,
Enum = 105,
Typedef = 106,
ObjCInterface = 107,
ObjCObjectPointer = 108,
Function = 109,
FunctionProto = 110,
ConstantArray = 111,
Vector = 112,
IncompleteArray = 113,
VariableArray = 114,
DependentSizedArray = 115,
MemberPointer = 116,
Auto = 117,
Elaborated = 118,
Pipe = 119,
Attributed = 120,
Atomic = 121,
Complex = 122,
}
impl CXType {
pub fn new(kind: CXTypeKind, spelling: &str) -> Self {
Self {
kind,
spelling: spelling.to_string(),
size: 0,
alignment: 0,
pointee_type: None,
array_element_type: None,
array_size: -1,
result_type: None,
argument_types: Vec::new(),
is_const: false,
is_volatile: false,
is_restrict: false,
num_template_args: -1,
}
}
pub fn pointee_type(&self) -> Option<&CXType> {
self.pointee_type.as_deref()
}
pub fn array_element_type(&self) -> (Option<&CXType>, i64) {
(self.array_element_type.as_deref(), self.array_size)
}
pub fn result_type(&self) -> Option<&CXType> {
self.result_type.as_deref()
}
pub fn num_arg_types(&self) -> i32 {
self.argument_types.len() as i32
}
pub fn arg_type(&self, index: usize) -> Option<&CXType> {
self.argument_types.get(index)
}
pub fn is_const_qualified(&self) -> bool {
self.is_const
}
pub fn is_volatile_qualified(&self) -> bool {
self.is_volatile
}
pub fn is_pod(&self) -> bool {
matches!(
self.kind,
CXTypeKind::Bool
| CXTypeKind::Char_U
| CXTypeKind::UChar
| CXTypeKind::SChar
| CXTypeKind::Short
| CXTypeKind::Int
| CXTypeKind::Long
| CXTypeKind::LongLong
| CXTypeKind::UShort
| CXTypeKind::UInt
| CXTypeKind::ULong
| CXTypeKind::ULongLong
| CXTypeKind::Float
| CXTypeKind::Double
| CXTypeKind::LongDouble
| CXTypeKind::Pointer
)
}
}
#[derive(Debug, Clone, Default)]
pub struct CXSourceLocation {
pub file: String,
pub line: u32,
pub column: u32,
pub offset: u32,
}
impl CXSourceLocation {
pub fn expansion_location(&self) -> (&str, u32, u32, u32) {
(&self.file, self.line, self.column, self.offset)
}
pub fn is_from_same_file(&self, other: &CXSourceLocation) -> bool {
self.file == other.file
}
}
#[derive(Debug, Clone, Default)]
pub struct CXSourceRange {
pub start: CXSourceLocation,
pub end: CXSourceLocation,
}
impl CXSourceRange {
pub fn range_locations(&self) -> (&CXSourceLocation, &CXSourceLocation) {
(&self.start, &self.end)
}
pub fn is_null(&self) -> bool {
self.start.file == self.end.file
&& self.start.line == self.end.line
&& self.start.column == self.end.column
}
}
#[derive(Debug, Clone)]
pub struct CXCodeCompleteResults {
pub results: Vec<CXCompletionResult>,
pub context: Option<String>,
}
#[derive(Debug, Clone)]
pub struct CXCompletionResult {
pub kind: CXCompletionKind,
pub completion_string: CXCompletionString,
pub priority: u32,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum CXCompletionKind {
Macro,
Keyword,
Typedef,
Function,
Variable,
Field,
EnumConstant,
Struct,
Union,
Class,
Namespace,
Enum,
Template,
Type,
Parameter,
Unknown,
}
#[derive(Debug, Clone)]
pub struct CXCompletionString {
pub chunks: Vec<CXCompletionChunk>,
pub priority: u32,
pub availability: CXAvailabilityKind,
pub brief_comment: Option<String>,
}
#[derive(Debug, Clone)]
pub struct CXCompletionChunk {
pub kind: CXCompletionChunkKind,
pub text: String,
pub annotation: Option<String>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum CXCompletionChunkKind {
Optional,
TypedText,
Text,
Placeholder,
Informative,
CurrentParameter,
LeftParen,
RightParen,
LeftBracket,
RightBracket,
LeftBrace,
RightBrace,
LeftAngle,
RightAngle,
Comma,
ResultType,
Colon,
SemiColon,
Equal,
HorizontalSpace,
VerticalSpace,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum CXAvailabilityKind {
Available,
Deprecated,
NotAvailable,
NotAccessible,
}
impl CXCodeCompleteResults {
pub fn complete_at(
_file: &str,
_line: u32,
_column: u32,
_unsaved_files: &[CXUnsavedFile],
_options: CXTranslationUnitFlags,
) -> Self {
Self {
results: Vec::new(),
context: None,
}
}
pub fn num_results(&self) -> u32 {
self.results.len() as u32
}
pub fn result(&self, index: u32) -> Option<&CXCompletionResult> {
self.results.get(index as usize)
}
pub fn sort_by_priority(&mut self) {
self.results.sort_by(|a, b| b.priority.cmp(&a.priority));
}
pub fn dispose(self) {
}
}
#[derive(Debug, Clone)]
pub struct CXDiagnostic {
pub severity: CXDiagnosticSeverity,
pub spelling: String,
pub location: CXSourceLocation,
pub ranges: Vec<CXSourceRange>,
pub fixits: Vec<CXFixIt>,
pub children: Vec<CXDiagnostic>,
pub category: u32,
pub category_text: String,
pub option: Option<String>,
pub disable_option: Option<String>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum CXDiagnosticSeverity {
Ignored,
Note,
Warning,
Error,
Fatal,
}
impl CXDiagnostic {
pub fn new_error(spelling: &str) -> Self {
Self {
severity: CXDiagnosticSeverity::Error,
spelling: spelling.to_string(),
location: CXSourceLocation::default(),
ranges: Vec::new(),
fixits: Vec::new(),
children: Vec::new(),
category: 0,
category_text: String::new(),
option: None,
disable_option: None,
}
}
pub fn new_warning(spelling: &str) -> Self {
Self {
severity: CXDiagnosticSeverity::Warning,
spelling: spelling.to_string(),
..Self::new_error(spelling)
}
}
pub fn new_note(spelling: &str) -> Self {
Self {
severity: CXDiagnosticSeverity::Note,
spelling: spelling.to_string(),
..Self::new_error(spelling)
}
}
pub fn severity(&self) -> CXDiagnosticSeverity {
self.severity
}
pub fn spelling(&self) -> &str {
&self.spelling
}
pub fn location(&self) -> &CXSourceLocation {
&self.location
}
pub fn num_fixits(&self) -> u32 {
self.fixits.len() as u32
}
pub fn fixit(&self, index: u32) -> Option<&CXFixIt> {
self.fixits.get(index as usize)
}
pub fn children(&self) -> &[CXDiagnostic] {
&self.children
}
pub fn with_disable_option(mut self, option: &str) -> Self {
self.disable_option = Some(option.to_string());
self
}
pub fn format(&self) -> String {
let severity_str = match self.severity {
CXDiagnosticSeverity::Ignored => "ignored",
CXDiagnosticSeverity::Note => "note",
CXDiagnosticSeverity::Warning => "warning",
CXDiagnosticSeverity::Error => "error",
CXDiagnosticSeverity::Fatal => "fatal error",
};
format!(
"{}:{}:{}: {}: {}",
self.location.file,
self.location.line,
self.location.column,
severity_str,
self.spelling
)
}
}
#[derive(Debug, Clone)]
pub struct CXFixIt {
pub range: CXSourceRange,
pub replacement: String,
}
impl CXFixIt {
pub fn new_replacement(range: CXSourceRange, replacement: &str) -> Self {
Self {
range,
replacement: replacement.to_string(),
}
}
pub fn new_removal(range: CXSourceRange) -> Self {
Self {
range,
replacement: String::new(),
}
}
pub fn new_insertion(location: CXSourceLocation, text: &str) -> Self {
Self {
range: CXSourceRange {
start: location.clone(),
end: location,
},
replacement: text.to_string(),
}
}
}
pub fn clang_getCursorKind(cursor: &CXCursor) -> CXCursorKind {
cursor.kind
}
pub fn clang_getCursorSpelling(cursor: &CXCursor) -> String {
cursor.spelling.clone()
}
pub fn clang_getCursorDisplayName(cursor: &CXCursor) -> String {
clang_getCursorSpelling(cursor)
}
pub fn clang_getCursorType(cursor: &CXCursor) -> CXType {
cursor.cursor_type().cloned().unwrap_or_default()
}
pub fn clang_getTypeSpelling(ty: &CXType) -> String {
ty.spelling.clone()
}
pub fn clang_getTypeKindSpelling(kind: CXTypeKind) -> &'static str {
match kind {
CXTypeKind::Invalid => "Invalid",
CXTypeKind::Unexposed => "Unexposed",
CXTypeKind::Void => "Void",
CXTypeKind::Bool => "Bool",
CXTypeKind::Char_U => "Char_U",
CXTypeKind::UChar => "UnsignedChar",
CXTypeKind::Char16 => "Char16",
CXTypeKind::Char32 => "Char32",
CXTypeKind::UShort => "UnsignedShort",
CXTypeKind::UInt => "UnsignedInt",
CXTypeKind::ULong => "UnsignedLong",
CXTypeKind::ULongLong => "UnsignedLongLong",
CXTypeKind::Int128 => "Int128",
CXTypeKind::Char_S => "Char_S",
CXTypeKind::SChar => "SignedChar",
CXTypeKind::WChar => "WChar",
CXTypeKind::Short => "Short",
CXTypeKind::Int => "Int",
CXTypeKind::Long => "Long",
CXTypeKind::LongLong => "LongLong",
CXTypeKind::Float => "Float",
CXTypeKind::Double => "Double",
CXTypeKind::LongDouble => "LongDouble",
CXTypeKind::Float128 => "Float128",
CXTypeKind::Pointer => "Pointer",
CXTypeKind::BlockPointer => "BlockPointer",
CXTypeKind::LValueReference => "LValueReference",
CXTypeKind::RValueReference => "RValueReference",
CXTypeKind::Record => "Record",
CXTypeKind::Enum => "Enum",
CXTypeKind::Typedef => "Typedef",
CXTypeKind::ObjCInterface => "ObjCInterface",
CXTypeKind::ObjCObjectPointer => "ObjCObjectPointer",
CXTypeKind::Function => "FunctionNoProto",
CXTypeKind::FunctionProto => "FunctionProto",
CXTypeKind::ConstantArray => "ConstantArray",
CXTypeKind::Vector => "Vector",
CXTypeKind::IncompleteArray => "IncompleteArray",
CXTypeKind::VariableArray => "VariableArray",
CXTypeKind::DependentSizedArray => "DependentSizedArray",
CXTypeKind::MemberPointer => "MemberPointer",
CXTypeKind::Auto => "Auto",
CXTypeKind::Elaborated => "Elaborated",
CXTypeKind::Pipe => "Pipe",
CXTypeKind::Attributed => "Attributed",
CXTypeKind::Atomic => "Atomic",
CXTypeKind::Complex => "Complex",
}
}
pub fn clang_getNumArgTypes(ty: &CXType) -> i32 {
ty.num_arg_types() as i32
}
pub fn clang_getArgType(ty: &CXType, i: u32) -> Option<CXType> {
ty.arg_type(i as usize).cloned()
}
pub fn clang_getResultType(ty: &CXType) -> CXType {
ty.result_type().cloned().unwrap_or_default()
}
pub fn clang_isConstQualifiedType(ty: &CXType) -> u32 {
ty.is_const_qualified() as u32
}
pub fn clang_isVolatileQualifiedType(ty: &CXType) -> u32 {
ty.is_volatile_qualified() as u32
}
pub fn clang_isRestrictQualifiedType(ty: &CXType) -> u32 {
ty.is_restrict as u32
}
pub fn clang_getPointeeType(ty: &CXType) -> CXType {
ty.pointee_type().cloned().unwrap_or_default()
}
pub fn clang_getArrayElementType(ty: &CXType) -> CXType {
ty.array_element_type().0.cloned().unwrap_or_default()
}
pub fn clang_getArraySize(ty: &CXType) -> i64 {
ty.array_size as i64
}
pub fn clang_getAddressSpace(_ty: &CXType) -> u32 {
0
}
pub fn clang_Type_getSizeOf(ty: &CXType) -> i64 {
ty.size as i64
}
pub fn clang_Type_getAlignOf(ty: &CXType) -> i64 {
ty.alignment as i64
}
pub fn clang_Type_getOffsetOf(ty: &CXType, _field_name: &str) -> i64 {
let _ = ty;
-1
}
pub fn clang_Type_getNumTemplateArguments(ty: &CXType) -> i32 {
ty.num_template_args as i32
}
pub fn clang_getEnumConstantDeclValue(cursor: &CXCursor) -> i64 {
match cursor.kind {
CXCursorKind::EnumConstantDecl => {
cursor.spelling.parse().unwrap_or(0)
}
_ => 0,
}
}
pub fn clang_getEnumConstantDeclUnsignedValue(cursor: &CXCursor) -> u64 {
clang_getEnumConstantDeclValue(cursor) as u64
}
pub fn clang_getFieldDeclBitWidth(cursor: &CXCursor) -> i32 {
match cursor.kind {
CXCursorKind::FieldDecl => {
if let Some(ref ty) = cursor.ty {
if ty.spelling.contains(':') {
let parts: Vec<&str> = ty.spelling.split(':').collect();
if parts.len() > 1 {
return parts[1].trim().parse().unwrap_or(-1);
}
}
}
-1
}
_ => -1,
}
}
pub fn clang_Cursor_getNumArguments(cursor: &CXCursor) -> i32 {
match cursor.kind {
CXCursorKind::CallExpr
| CXCursorKind::CXXMemberCallExpr
| CXCursorKind::CXXOperatorCallExpr
| CXCursorKind::Constructor
| CXCursorKind::Destructor => cursor.children.len() as i32,
_ => -1,
}
}
pub fn clang_Cursor_getArgument(cursor: &CXCursor, i: u32) -> Option<CXCursor> {
if i < cursor.children.len() as u32 {
Some(cursor.children[i as usize].clone())
} else {
None
}
}
pub fn clang_Cursor_isBitField(cursor: &CXCursor) -> u32 {
match cursor.kind {
CXCursorKind::FieldDecl => {
if let Some(ref ty) = cursor.ty {
ty.spelling.contains(':') as u32
} else {
0
}
}
_ => 0,
}
}
pub fn clang_Cursor_isAnonymous(cursor: &CXCursor) -> u32 {
match cursor.kind {
CXCursorKind::StructDecl | CXCursorKind::UnionDecl => cursor.spelling.is_empty() as u32,
_ => 0,
}
}
pub fn clang_Cursor_isAnonymousRecordDecl(cursor: &CXCursor) -> u32 {
clang_Cursor_isAnonymous(cursor)
}
pub fn clang_Cursor_isInlineNamespace(_cursor: &CXCursor) -> u32 {
0
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum CXVisibilityKind {
Invalid,
Hidden,
Protected,
Default,
}
pub fn clang_getCursorVisibility(cursor: &CXCursor) -> CXVisibilityKind {
if matches!(
cursor.kind,
CXCursorKind::UnexpectedDecl | CXCursorKind::NotImplemented
) {
CXVisibilityKind::Invalid
} else {
CXVisibilityKind::Default
}
}
pub fn clang_getCursorLinkage(cursor: &CXCursor) -> CXLinkageKind {
cursor.linkage()
}
pub fn clang_getCursorLanguage(_cursor: &CXCursor) -> u32 {
1 }
pub fn clang_Cursor_getTranslationUnit(cursor: &CXCursor) -> CXTranslationUnit {
CXTranslationUnit {
filename: String::new(),
ast: TranslationUnit {
decls: Vec::new(),
filename: String::new(),
},
diagnostics: Vec::new(),
cursors: vec![cursor.clone()],
standard: CLangStandard::C17,
}
}
pub type CXCursorVisitor = dyn FnMut(&CXCursor, &CXCursor) -> CXChildVisitResult;
pub fn clang_visitChildren(cursor: &CXCursor, visitor: &mut CXCursorVisitor) -> u32 {
if cursor.children.is_empty() {
return 0; }
for child in &cursor.children {
match visitor(child, cursor) {
CXChildVisitResult::Break => return 0,
CXChildVisitResult::Continue => {}
CXChildVisitResult::Recurse => {
}
}
}
1 }
pub fn clang_visitChildren_typed<F>(cursor: &CXCursor, mut visitor: F) -> u32
where
F: FnMut(&CXCursor, &CXCursor) -> CXChildVisitResult + 'static,
{
clang_visitChildren(cursor, &mut visitor)
}
pub fn clang_getTranslationUnitCursor(tu: &CXTranslationUnit) -> CXCursor {
tu.cursor()
}
pub fn clang_getNumCursors(tu: &CXTranslationUnit) -> u32 {
tu.cursors.len() as u32
}
pub fn clang_getCursor(tu: &CXTranslationUnit, i: u32) -> Option<CXCursor> {
tu.cursors.get(i as usize).cloned()
}
pub fn clang_getCursorLocation(cursor: &CXCursor) -> CXSourceLocation {
cursor.location.clone()
}
pub fn clang_getCursorExtent(cursor: &CXCursor) -> CXSourceRange {
CXSourceRange {
start: cursor.location.clone(),
end: cursor.extent.end.clone(),
}
}
pub fn clang_getNullLocation() -> CXSourceLocation {
CXSourceLocation {
file: String::new(),
line: 0,
column: 0,
offset: 0,
}
}
pub fn clang_getNullRange() -> CXSourceRange {
let null_loc = clang_getNullLocation();
CXSourceRange {
start: null_loc.clone(),
end: null_loc,
}
}
pub fn clang_getFileName(location: &CXSourceLocation) -> String {
location.file.clone()
}
pub fn clang_getLineNumber(location: &CXSourceLocation) -> u32 {
location.line
}
pub fn clang_getColumnNumber(location: &CXSourceLocation) -> u32 {
location.column
}
pub fn clang_getFileOffset(location: &CXSourceLocation) -> u32 {
location.offset
}
pub fn clang_equalLocations(loc1: &CXSourceLocation, loc2: &CXSourceLocation) -> u32 {
loc1.is_from_same_file(loc2) as u32
}
pub fn clang_equalRanges(range1: &CXSourceRange, range2: &CXSourceRange) -> u32 {
(clang_equalLocations(&range1.start, &range2.start) != 0
&& clang_equalLocations(&range1.end, &range2.end) != 0) as u32
}
pub fn clang_equalTypes(ty1: &CXType, ty2: &CXType) -> u32 {
(ty1.kind == ty2.kind && ty1.spelling == ty2.spelling) as u32
}
pub fn clang_getCanonicalType(ty: &CXType) -> CXType {
ty.clone()
}
pub fn clang_isPODType(ty: &CXType) -> u32 {
ty.is_pod() as u32
}
pub fn clang_getTypeDeclaration(ty: &CXType) -> CXCursor {
match ty.kind {
CXTypeKind::Record => CXCursor::new(CXCursorKind::StructDecl, &ty.spelling),
CXTypeKind::Enum => CXCursor::new(CXCursorKind::EnumDecl, &ty.spelling),
CXTypeKind::Typedef => CXCursor::new(CXCursorKind::TypedefDecl, &ty.spelling),
CXTypeKind::ObjCInterface => CXCursor::new(CXCursorKind::ObjCInterfaceDecl, &ty.spelling),
_ => CXCursor::new(CXCursorKind::NotImplemented, ""),
}
}
pub fn clang_getNumElements(ty: &CXType) -> i64 {
match ty.kind {
CXTypeKind::ConstantArray | CXTypeKind::Vector => ty.array_size as i64,
CXTypeKind::IncompleteArray => -1,
_ => 1,
}
}
pub fn clang_getElementType(ty: &CXType) -> CXType {
ty.array_element_type().0.cloned().unwrap_or_default()
}
pub fn clang_Type_getNamedType(ty: &CXType) -> CXType {
if ty.kind == CXTypeKind::Elaborated {
let mut canonical = ty.clone();
canonical.kind = CXTypeKind::Record;
canonical
} else {
ty.clone()
}
}
pub fn clang_Type_getClassType(_ty: &CXType) -> CXType {
CXType::new(CXTypeKind::Record, "")
}
#[derive(Debug, Clone)]
pub struct CXToken {
pub kind: u32,
pub spelling: String,
pub location: CXSourceLocation,
pub extent: CXSourceRange,
}
impl CXToken {
pub fn new(kind: u32, spelling: &str, location: CXSourceLocation) -> Self {
Self {
kind,
spelling: spelling.to_string(),
location: location.clone(),
extent: CXSourceRange {
start: location.clone(),
end: location,
},
}
}
}
pub fn clang_getTokenKind(kind: u32) -> &'static str {
match kind {
0 => "punctuation",
1 => "keyword",
2 => "identifier",
3 => "literal",
4 => "comment",
_ => "unknown",
}
}
pub fn clang_getTokenSpelling(_tu: &CXTranslationUnit, token: &CXToken) -> String {
token.spelling.clone()
}
pub fn clang_getTokenLocation(_tu: &CXTranslationUnit, token: &CXToken) -> CXSourceLocation {
token.location.clone()
}
pub fn clang_getTokenExtent(_tu: &CXTranslationUnit, token: &CXToken) -> CXSourceRange {
CXSourceRange {
start: token.location.clone(),
end: token.extent.end.clone(),
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_index_create_and_dispose() {
let mut index = CXIndex::create();
assert!(index.translation_units.is_empty());
index.dispose();
assert!(index.translation_units.is_empty());
}
#[test]
fn test_cursor_kind_is_declaration() {
assert!(CXCursorKind::FunctionDecl.is_declaration());
assert!(CXCursorKind::StructDecl.is_declaration());
assert!(!CXCursorKind::IntegerLiteral.is_declaration());
}
#[test]
fn test_cursor_kind_is_expression() {
assert!(CXCursorKind::CallExpr.is_expression());
assert!(CXCursorKind::StringLiteral.is_expression());
assert!(!CXCursorKind::FunctionDecl.is_expression());
}
#[test]
fn test_cursor_kind_is_statement() {
assert!(CXCursorKind::IfStmt.is_statement());
assert!(CXCursorKind::ReturnStmt.is_statement());
assert!(!CXCursorKind::TranslationUnit.is_statement());
}
#[test]
fn test_cursor_visit_children() {
let parent = CXCursor::new(CXCursorKind::FunctionDecl, "main");
let mut parent_with_children = parent.clone();
parent_with_children.children = vec![
CXCursor::new(CXCursorKind::ReturnStmt, "return"),
CXCursor::new(CXCursorKind::IntegerLiteral, "0"),
];
let mut visited = 0u32;
parent_with_children.visit_children(|_child| {
visited += 1;
CXChildVisitResult::Continue
});
assert_eq!(visited, 2);
}
#[test]
fn test_cx_type_pod_check() {
let int_ty = CXType::new(CXTypeKind::Int, "int");
assert!(int_ty.is_pod());
let ptr_ty = CXType::new(CXTypeKind::Pointer, "int*");
assert!(ptr_ty.is_pod());
let func_ty = CXType::new(CXTypeKind::FunctionProto, "void(int)");
assert!(!func_ty.is_pod());
}
#[test]
fn test_source_location_expansion() {
let loc = CXSourceLocation {
file: "test.c".into(),
line: 42,
column: 10,
offset: 500,
};
let (file, line, col, offset) = loc.expansion_location();
assert_eq!(file, "test.c");
assert_eq!(line, 42);
assert_eq!(col, 10);
assert_eq!(offset, 500);
}
#[test]
fn test_source_range_is_null() {
let loc = CXSourceLocation {
file: "a.c".into(),
line: 1,
column: 1,
offset: 0,
};
let range = CXSourceRange {
start: loc.clone(),
end: loc,
};
assert!(range.is_null());
}
#[test]
fn test_diagnostic_creation() {
let diag = CXDiagnostic::new_error("expected ';'");
assert!(matches!(diag.severity(), CXDiagnosticSeverity::Error));
assert_eq!(diag.spelling(), "expected ';'");
let warn = CXDiagnostic::new_warning("unused variable 'x'");
assert!(matches!(warn.severity(), CXDiagnosticSeverity::Warning));
}
#[test]
fn test_diagnostic_format() {
let mut diag = CXDiagnostic::new_error("type mismatch");
diag.location = CXSourceLocation {
file: "test.c".into(),
line: 10,
column: 5,
offset: 100,
};
let formatted = diag.format();
assert!(formatted.contains("test.c"));
assert!(formatted.contains("10:5"));
assert!(formatted.contains("error"));
}
#[test]
fn test_fixit_creation() {
let loc = CXSourceLocation {
file: "a.c".into(),
line: 1,
column: 5,
offset: 4,
};
let insert = CXFixIt::new_insertion(loc, ";");
assert_eq!(insert.replacement, ";");
}
#[test]
fn test_code_complete_results() {
let results = CXCodeCompleteResults::complete_at(
"test.c",
10,
5,
&[],
CXTranslationUnitFlags::none(),
);
assert_eq!(results.num_results(), 0);
}
#[test]
fn test_translation_unit_parse() {
let tu = CXTranslationUnit::parse(
"test.c",
"int main() { return 0; }",
&[],
&[],
CXTranslationUnitFlags::none(),
);
assert_eq!(tu.spelling(), "test.c");
assert_eq!(tu.num_diagnostics(), 0);
}
#[test]
fn test_unsaved_file_creation() {
let uf = CXUnsavedFile::new("header.h", "#define FOO 1");
assert_eq!(uf.filename, "header.h");
assert_eq!(uf.length, 13);
}
#[test]
fn test_cxcursor_linkage() {
let cursor = CXCursor::new(CXCursorKind::VarDecl, "x");
assert_eq!(cursor.linkage(), CXLinkageKind::External);
}
#[test]
fn test_cxcursor_kind_from_u32() {
assert_eq!(CXCursorKind::from_u32(300), CXCursorKind::TranslationUnit);
assert_eq!(CXCursorKind::from_u32(8), CXCursorKind::FunctionDecl);
assert_eq!(CXCursorKind::from_u32(99999), CXCursorKind::NotImplemented);
}
#[test]
fn test_cursor_visit_with_recurse() {
let leaf1 = CXCursor::new(CXCursorKind::IntegerLiteral, "1");
let leaf2 = CXCursor::new(CXCursorKind::ReturnStmt, "return");
let mut parent = CXCursor::new(CXCursorKind::FunctionDecl, "f");
let child = CXCursor {
children: vec![leaf1, leaf2],
..CXCursor::new(CXCursorKind::CompoundStmt, "body")
};
parent.children = vec![child];
let mut count = 0u32;
parent.visit_children(|_| {
count += 1;
CXChildVisitResult::Recurse
});
assert_eq!(count, 1);
}
#[test]
fn test_cursor_visit_with_break() {
let mut parent = CXCursor::new(CXCursorKind::FunctionDecl, "main");
parent.children = vec![
CXCursor::new(CXCursorKind::VarDecl, "a"),
CXCursor::new(CXCursorKind::VarDecl, "b"),
CXCursor::new(CXCursorKind::VarDecl, "c"),
];
let mut visited = 0u32;
parent.visit_children(|_| {
visited += 1;
if visited == 2 {
CXChildVisitResult::Break
} else {
CXChildVisitResult::Continue
}
});
assert_eq!(visited, 2);
}
#[test]
fn test_cx_type_function_proto() {
let mut func_ty = CXType::new(CXTypeKind::FunctionProto, "int(int, float)");
func_ty.argument_types = vec![
CXType::new(CXTypeKind::Int, "int"),
CXType::new(CXTypeKind::Float, "float"),
];
assert_eq!(func_ty.num_arg_types(), 2);
assert!(func_ty.arg_type(0).is_some());
assert!(func_ty.arg_type(1).is_some());
assert!(func_ty.arg_type(2).is_none());
}
#[test]
fn test_cx_type_pointer_pointee() {
let int_ty = CXType::new(CXTypeKind::Int, "int");
let mut ptr_ty = CXType::new(CXTypeKind::Pointer, "int*");
ptr_ty.pointee_type = Some(Box::new(int_ty));
let pointee = ptr_ty.pointee_type();
assert!(pointee.is_some());
assert_eq!(pointee.unwrap().kind, CXTypeKind::Int);
}
#[test]
fn test_cx_type_array_element() {
let int_ty = CXType::new(CXTypeKind::Int, "int");
let mut arr_ty = CXType::new(CXTypeKind::ConstantArray, "int[10]");
arr_ty.array_element_type = Some(Box::new(int_ty));
arr_ty.array_size = 10;
let (elem, size) = arr_ty.array_element_type();
assert!(elem.is_some());
assert_eq!(size, 10);
}
#[test]
fn test_diagnostic_with_fixits() {
let mut diag = CXDiagnostic::new_error("use of undeclared identifier");
let loc = CXSourceLocation {
file: "test.c".into(),
line: 5,
column: 10,
offset: 45,
};
let fixit = CXFixIt::new_insertion(loc, "int x;");
diag.fixits.push(fixit);
assert_eq!(diag.num_fixits(), 1);
assert!(diag.fixit(0).is_some());
assert!(diag.fixit(1).is_none());
}
#[test]
fn test_diagnostic_children() {
let mut diag = CXDiagnostic::new_error("type mismatch");
let note = CXDiagnostic::new_note("candidate function not viable");
diag.children.push(note);
assert_eq!(diag.children().len(), 1);
}
#[test]
fn test_diagnostic_disable_option() {
let diag =
CXDiagnostic::new_warning("unused variable").with_disable_option("-Wunused-variable");
assert_eq!(diag.disable_option.as_deref(), Some("-Wunused-variable"));
}
#[test]
fn test_code_completion_sort_by_priority() {
let mut results = CXCodeCompleteResults {
results: vec![
CXCompletionResult {
kind: CXCompletionKind::Function,
completion_string: CXCompletionString {
chunks: vec![],
priority: 1,
availability: CXAvailabilityKind::Available,
brief_comment: None,
},
priority: 30,
},
CXCompletionResult {
kind: CXCompletionKind::Variable,
completion_string: CXCompletionString {
chunks: vec![],
priority: 0,
availability: CXAvailabilityKind::Available,
brief_comment: None,
},
priority: 80,
},
],
context: None,
};
results.sort_by_priority();
assert_eq!(results.results[0].priority, 80);
assert_eq!(results.results[1].priority, 30);
}
#[test]
fn test_completion_chunk_kinds() {
let chunk = CXCompletionChunk {
kind: CXCompletionChunkKind::TypedText,
text: "printf".into(),
annotation: Some("int printf(const char*, ...)".into()),
};
assert_eq!(chunk.text, "printf");
assert!(chunk.annotation.is_some());
}
#[test]
fn test_source_range_range_locations() {
let start = CXSourceLocation {
file: "a.c".into(),
line: 1,
column: 1,
offset: 0,
};
let end = CXSourceLocation {
file: "a.c".into(),
line: 1,
column: 10,
offset: 9,
};
let range = CXSourceRange { start, end };
let (s, e) = range.range_locations();
assert_eq!(s.line, 1);
assert_eq!(e.column, 10);
}
#[test]
fn test_unsaved_file_multiple() {
let f1 = CXUnsavedFile::new("a.h", "int foo(void);");
let f2 = CXUnsavedFile::new("b.h", "int bar(void);");
assert_eq!(f1.filename, "a.h");
assert_eq!(f2.filename, "b.h");
assert_ne!(f1.length, f2.length);
}
#[test]
fn test_cxdiagnostic_severity_all() {
let err = CXDiagnostic::new_error("e");
let warn = CXDiagnostic::new_warning("w");
let note = CXDiagnostic::new_note("n");
assert!(matches!(err.severity(), CXDiagnosticSeverity::Error));
assert!(matches!(warn.severity(), CXDiagnosticSeverity::Warning));
assert!(matches!(note.severity(), CXDiagnosticSeverity::Note));
}
#[test]
fn test_clang_getCursorKind() {
let cursor = CXCursor::new(CXCursorKind::FunctionDecl, "main");
assert_eq!(clang_getCursorKind(&cursor), CXCursorKind::FunctionDecl);
let stmt = CXCursor::new(CXCursorKind::ReturnStmt, "return");
assert_eq!(clang_getCursorKind(&stmt), CXCursorKind::ReturnStmt);
}
#[test]
fn test_clang_getCursorSpelling() {
let cursor = CXCursor::new(CXCursorKind::VarDecl, "my_variable");
assert_eq!(clang_getCursorSpelling(&cursor), "my_variable");
}
#[test]
fn test_clang_getCursorDisplayName() {
let cursor = CXCursor::new(CXCursorKind::FunctionDecl, "compute");
assert_eq!(clang_getCursorDisplayName(&cursor), "compute");
}
#[test]
fn test_clang_getTypeSpelling() {
let ty = CXType::new(CXTypeKind::Int, "int");
assert_eq!(clang_getTypeSpelling(&ty), "int");
let ptr_ty = CXType::new(CXTypeKind::Pointer, "int*");
assert_eq!(clang_getTypeSpelling(&ptr_ty), "int*");
}
#[test]
fn test_clang_getTypeKindSpelling_all() {
let kinds = [
(CXTypeKind::Void, "Void"),
(CXTypeKind::Int, "Int"),
(CXTypeKind::Float, "Float"),
(CXTypeKind::Pointer, "Pointer"),
(CXTypeKind::Record, "Record"),
(CXTypeKind::FunctionProto, "FunctionProto"),
(CXTypeKind::ConstantArray, "ConstantArray"),
];
for (kind, expected) in &kinds {
assert_eq!(clang_getTypeKindSpelling(*kind), *expected);
}
}
#[test]
fn test_clang_isConstQualifiedType() {
let ty = CXType::new(CXTypeKind::Int, "int");
assert_eq!(clang_isConstQualifiedType(&ty), 0);
let mut const_ty = CXType::new(CXTypeKind::Int, "const int");
const_ty.is_const = true;
assert_eq!(clang_isConstQualifiedType(&const_ty), 1);
}
#[test]
fn test_clang_isVolatileQualifiedType() {
let ty = CXType::new(CXTypeKind::Int, "int");
assert_eq!(clang_isVolatileQualifiedType(&ty), 0);
let mut vol_ty = CXType::new(CXTypeKind::Int, "volatile int");
vol_ty.is_volatile = true;
assert_eq!(clang_isVolatileQualifiedType(&vol_ty), 1);
}
#[test]
fn test_clang_getPointeeType() {
let int_ty = CXType::new(CXTypeKind::Int, "int");
let mut ptr_ty = CXType::new(CXTypeKind::Pointer, "int*");
ptr_ty.pointee_type = Some(Box::new(int_ty.clone()));
let pointee = clang_getPointeeType(&ptr_ty);
assert_eq!(pointee.kind, CXTypeKind::Int);
}
#[test]
fn test_clang_getArraySize() {
let mut arr = CXType::new(CXTypeKind::ConstantArray, "int[10]");
arr.array_size = 10;
assert_eq!(clang_getArraySize(&arr), 10);
let incomplete = CXType::new(CXTypeKind::IncompleteArray, "int[]");
assert_eq!(clang_getArraySize(&incomplete), 0);
}
#[test]
fn test_clang_Type_getSizeOf() {
let ty = CXType::new(CXTypeKind::Int, "int");
ty.size(); }
#[test]
fn test_clang_getEnumConstantDeclValue() {
let cursor = CXCursor::new(CXCursorKind::EnumConstantDecl, "42");
assert_eq!(clang_getEnumConstantDeclValue(&cursor), 42);
let cursor2 = CXCursor::new(CXCursorKind::EnumConstantDecl, "0");
assert_eq!(clang_getEnumConstantDeclValue(&cursor2), 0);
}
#[test]
fn test_clang_getEnumConstantDeclUnsignedValue() {
let cursor = CXCursor::new(CXCursorKind::EnumConstantDecl, "100");
assert_eq!(clang_getEnumConstantDeclUnsignedValue(&cursor), 100);
}
#[test]
fn test_clang_getFieldDeclBitWidth() {
let mut cursor = CXCursor::new(CXCursorKind::FieldDecl, "x");
cursor.ty = Some(CXType::new(CXTypeKind::Int, "int : 3"));
assert_eq!(clang_getFieldDeclBitWidth(&cursor), 3);
let cursor2 = CXCursor::new(CXCursorKind::FieldDecl, "y");
assert_eq!(clang_getFieldDeclBitWidth(&cursor2), -1);
}
#[test]
fn test_clang_Cursor_getNumArguments() {
let mut cursor = CXCursor::new(CXCursorKind::CallExpr, "printf");
cursor.children = vec![
CXCursor::new(CXCursorKind::StringLiteral, "\"hello\""),
CXCursor::new(CXCursorKind::IntegerLiteral, "42"),
];
assert_eq!(clang_Cursor_getNumArguments(&cursor), 2);
}
#[test]
fn test_clang_Cursor_getArgument() {
let mut cursor = CXCursor::new(CXCursorKind::CallExpr, "foo");
let arg0 = CXCursor::new(CXCursorKind::IntegerLiteral, "1");
let arg1 = CXCursor::new(CXCursorKind::IntegerLiteral, "2");
cursor.children = vec![arg0.clone(), arg1.clone()];
let result = clang_Cursor_getArgument(&cursor, 0);
assert!(result.is_some());
assert_eq!(result.unwrap().spelling, "1");
assert!(clang_Cursor_getArgument(&cursor, 2).is_none());
}
#[test]
fn test_clang_Cursor_isBitField() {
let cursor = CXCursor::new(CXCursorKind::FieldDecl, "x");
assert_eq!(clang_Cursor_isBitField(&cursor), 0);
let mut bf = CXCursor::new(CXCursorKind::FieldDecl, "x");
bf.ty = Some(CXType::new(CXTypeKind::Int, "int : 3"));
assert_eq!(clang_Cursor_isBitField(&bf), 1);
}
#[test]
fn test_clang_Cursor_isAnonymous() {
let cursor = CXCursor::new(CXCursorKind::StructDecl, "");
assert_eq!(clang_Cursor_isAnonymous(&cursor), 1);
let named = CXCursor::new(CXCursorKind::StructDecl, "MyStruct");
assert_eq!(clang_Cursor_isAnonymous(&named), 0);
}
#[test]
fn test_clang_getCursorVisibility() {
let cursor = CXCursor::new(CXCursorKind::FunctionDecl, "f");
assert_eq!(
clang_getCursorVisibility(&cursor),
CXVisibilityKind::Default
);
let invalid = CXCursor::new(CXCursorKind::NotImplemented, "");
assert_eq!(
clang_getCursorVisibility(&invalid),
CXVisibilityKind::Invalid
);
}
#[test]
fn test_clang_visitChildren_typed() {
let mut parent = CXCursor::new(CXCursorKind::FunctionDecl, "main");
parent.children = vec![
CXCursor::new(CXCursorKind::VarDecl, "x"),
CXCursor::new(CXCursorKind::VarDecl, "y"),
];
let mut count = 0u32;
clang_visitChildren_typed(&parent, |_child, _parent| {
count += 1;
CXChildVisitResult::Continue
});
assert_eq!(count, 2);
}
#[test]
fn test_clang_getTranslationUnitCursor() {
let tu =
CXTranslationUnit::parse("test.c", "int x;", &[], &[], CXTranslationUnitFlags::none());
let cursor = clang_getTranslationUnitCursor(&tu);
assert!(cursor.kind == CXCursorKind::TranslationUnit || cursor.spelling == "test.c");
}
#[test]
fn test_clang_getCursorLocation() {
let cursor = CXCursor::new(CXCursorKind::VarDecl, "x");
let loc = clang_getCursorLocation(&cursor);
assert_eq!(loc.line, 1);
assert_eq!(loc.column, 1);
}
#[test]
fn test_clang_getNullLocation() {
let loc = clang_getNullLocation();
assert_eq!(loc.line, 0);
assert_eq!(loc.column, 0);
assert!(loc.file.is_empty());
}
#[test]
fn test_clang_getNullRange() {
let range = clang_getNullRange();
assert!(range.is_null());
}
#[test]
fn test_clang_equalTypes() {
let t1 = CXType::new(CXTypeKind::Int, "int");
let t2 = CXType::new(CXTypeKind::Int, "int");
let t3 = CXType::new(CXTypeKind::Float, "float");
assert_eq!(clang_equalTypes(&t1, &t2), 1);
assert_eq!(clang_equalTypes(&t1, &t3), 0);
}
#[test]
fn test_clang_getCanonicalType() {
let ty = CXType::new(CXTypeKind::Typedef, "my_int");
let canonical = clang_getCanonicalType(&ty);
assert_eq!(canonical.kind, CXTypeKind::Typedef);
}
#[test]
fn test_clang_getTypeDeclaration() {
let struct_ty = CXType::new(CXTypeKind::Record, "MyStruct");
let cursor = clang_getTypeDeclaration(&struct_ty);
assert_eq!(cursor.kind, CXCursorKind::StructDecl);
assert_eq!(cursor.spelling, "MyStruct");
}
#[test]
fn test_clang_getNumElements() {
let mut arr = CXType::new(CXTypeKind::ConstantArray, "int[10]");
arr.array_size = 10;
assert_eq!(clang_getNumElements(&arr), 10);
}
#[test]
fn test_clang_getCursorLanguage() {
let cursor = CXCursor::new(CXCursorKind::FunctionDecl, "f");
assert_eq!(clang_getCursorLanguage(&cursor), 1); }
#[test]
fn test_cx_token_creation() {
let loc = CXSourceLocation {
file: "t.c".into(),
line: 1,
column: 1,
offset: 0,
};
let tok = CXToken::new(2, "identifier", loc);
assert_eq!(tok.kind, 2);
assert_eq!(tok.spelling, "identifier");
}
#[test]
fn test_clang_getTokenKind() {
assert_eq!(clang_getTokenKind(0), "punctuation");
assert_eq!(clang_getTokenKind(1), "keyword");
assert_eq!(clang_getTokenKind(2), "identifier");
assert_eq!(clang_getTokenKind(3), "literal");
assert_eq!(clang_getTokenKind(4), "comment");
assert_eq!(clang_getTokenKind(999), "unknown");
}
#[test]
fn test_clang_getTokenSpelling() {
let tu =
CXTranslationUnit::parse("test.c", "int x;", &[], &[], CXTranslationUnitFlags::none());
let loc = CXSourceLocation {
file: "t.c".into(),
line: 1,
column: 1,
offset: 0,
};
let tok = CXToken::new(2, "int", loc);
assert_eq!(clang_getTokenSpelling(&tu, &tok), "int");
}
#[test]
fn test_clang_getTokenLocation() {
let tu =
CXTranslationUnit::parse("test.c", "int x;", &[], &[], CXTranslationUnitFlags::none());
let loc = CXSourceLocation {
file: "t.c".into(),
line: 5,
column: 3,
offset: 20,
};
let tok = CXToken::new(2, "x", loc);
let result = clang_getTokenLocation(&tu, &tok);
assert_eq!(result.line, 5);
}
#[test]
fn test_clang_Cursor_getTranslationUnit() {
let cursor = CXCursor::new(CXCursorKind::FunctionDecl, "f");
let tu = clang_Cursor_getTranslationUnit(&cursor);
assert_eq!(tu.cursors.len(), 1);
assert_eq!(tu.cursors[0].spelling, "f");
}
#[test]
fn test_clang_isPODType() {
let int_ty = CXType::new(CXTypeKind::Int, "int");
assert_eq!(clang_isPODType(&int_ty), 1);
let func_ty = CXType::new(CXTypeKind::FunctionProto, "void()");
assert_eq!(clang_isPODType(&func_ty), 0);
}
#[test]
fn test_clang_getElementType() {
let int_ty = CXType::new(CXTypeKind::Int, "int");
let mut arr = CXType::new(CXTypeKind::ConstantArray, "int[5]");
arr.array_element_type = Some(Box::new(int_ty.clone()));
arr.array_size = 5;
let elem = clang_getElementType(&arr);
assert_eq!(elem.kind, CXTypeKind::Int);
}
#[test]
fn test_clang_equalLocations() {
let loc1 = CXSourceLocation {
file: "a.c".into(),
line: 1,
column: 1,
offset: 0,
};
let loc2 = CXSourceLocation {
file: "a.c".into(),
line: 1,
column: 1,
offset: 0,
};
let loc3 = CXSourceLocation {
file: "b.c".into(),
line: 1,
column: 1,
offset: 0,
};
assert_eq!(clang_equalLocations(&loc1, &loc2), 1);
assert_eq!(clang_equalLocations(&loc1, &loc3), 0);
}
#[test]
fn test_clang_equalRanges() {
let loc1 = CXSourceLocation {
file: "a.c".into(),
line: 1,
column: 1,
offset: 0,
};
let loc2 = CXSourceLocation {
file: "a.c".into(),
line: 2,
column: 1,
offset: 10,
};
let loc3 = CXSourceLocation {
file: "b.c".into(),
line: 1,
column: 1,
offset: 0,
};
let r1 = CXSourceRange {
start: loc1.clone(),
end: loc2.clone(),
};
let r2 = CXSourceRange {
start: loc1,
end: loc2,
};
let r3 = CXSourceRange {
start: loc3.clone(),
end: loc3,
};
assert_eq!(clang_equalRanges(&r1, &r2), 1);
assert_eq!(clang_equalRanges(&r1, &r3), 0);
}
#[test]
fn test_clang_getFileName() {
let loc = CXSourceLocation {
file: "hello.c".into(),
line: 10,
column: 5,
offset: 100,
};
assert_eq!(clang_getFileName(&loc), "hello.c");
}
#[test]
fn test_clang_getLineNumber() {
let loc = CXSourceLocation {
file: "a.c".into(),
line: 42,
column: 1,
offset: 0,
};
assert_eq!(clang_getLineNumber(&loc), 42);
}
#[test]
fn test_clang_getColumnNumber() {
let loc = CXSourceLocation {
file: "a.c".into(),
line: 1,
column: 15,
offset: 10,
};
assert_eq!(clang_getColumnNumber(&loc), 15);
}
#[test]
fn test_clang_getFileOffset() {
let loc = CXSourceLocation {
file: "a.c".into(),
line: 1,
column: 1,
offset: 500,
};
assert_eq!(clang_getFileOffset(&loc), 500);
}
#[test]
fn test_clang_getCursorExtent() {
let mut cursor = CXCursor::new(CXCursorKind::FunctionDecl, "main");
cursor.location = CXSourceLocation {
file: "a.c".into(),
line: 5,
column: 1,
offset: 100,
};
cursor.extent = CXSourceRange {
start: CXSourceLocation {
file: "a.c".into(),
line: 5,
column: 1,
offset: 100,
},
end: CXSourceLocation {
file: "a.c".into(),
line: 10,
column: 1,
offset: 200,
},
};
let extent = clang_getCursorExtent(&cursor);
assert_eq!(extent.start.line, 5);
assert_eq!(extent.end.line, 10);
}
#[test]
fn test_clang_isRestrictQualifiedType() {
let ty = CXType::new(CXTypeKind::Int, "int");
assert_eq!(clang_isRestrictQualifiedType(&ty), 0);
let mut restrict_ty = CXType::new(CXTypeKind::Pointer, "int* restrict");
restrict_ty.is_restrict = true;
assert_eq!(clang_isRestrictQualifiedType(&restrict_ty), 1);
}
#[test]
fn test_clang_getArrayElementType() {
let int_ty = CXType::new(CXTypeKind::Int, "int");
let mut arr = CXType::new(CXTypeKind::ConstantArray, "int[5]");
arr.array_element_type = Some(Box::new(int_ty.clone()));
arr.array_size = 5;
let elem = clang_getArrayElementType(&arr);
assert_eq!(elem.kind, CXTypeKind::Int);
}
#[test]
fn test_clang_Type_getSizeOf_int() {
let mut ty = CXType::new(CXTypeKind::Int, "int");
ty.size = 4;
assert_eq!(clang_Type_getSizeOf(&ty), 4);
}
#[test]
fn test_clang_Type_getAlignOf_int() {
let mut ty = CXType::new(CXTypeKind::Int, "int");
ty.alignment = 4;
assert_eq!(clang_Type_getAlignOf(&ty), 4);
}
#[test]
fn test_clang_Type_getOffsetOf_unknown() {
let ty = CXType::new(CXTypeKind::Record, "MyStruct");
assert_eq!(clang_Type_getOffsetOf(&ty, "nonexistent"), -1);
}
#[test]
fn test_clang_Cursor_isAnonymousRecordDecl() {
let anon = CXCursor::new(CXCursorKind::StructDecl, "");
assert_eq!(clang_Cursor_isAnonymousRecordDecl(&anon), 1);
let named = CXCursor::new(CXCursorKind::UnionDecl, "MyUnion");
assert_eq!(clang_Cursor_isAnonymousRecordDecl(&named), 0);
}
#[test]
fn test_clang_Cursor_isInlineNamespace() {
let ns = CXCursor::new(CXCursorKind::Namespace, "std");
assert_eq!(clang_Cursor_isInlineNamespace(&ns), 0);
}
#[test]
fn test_clang_Type_getNamedType() {
let elaborated = CXType::new(CXTypeKind::Elaborated, "struct MyStruct");
let named = clang_Type_getNamedType(&elaborated);
assert_eq!(named.kind, CXTypeKind::Record);
}
#[test]
fn test_clang_Type_getClassType() {
let member_ptr = CXType::new(CXTypeKind::MemberPointer, "int MyClass::*");
let class_ty = clang_Type_getClassType(&member_ptr);
assert_eq!(class_ty.kind, CXTypeKind::Record);
}
#[test]
fn test_clang_getNumCursors() {
let tu = CXTranslationUnit {
filename: "test.c".into(),
ast: None,
diagnostics: vec![],
cursors: vec![
CXCursor::new(CXCursorKind::FunctionDecl, "f1"),
CXCursor::new(CXCursorKind::FunctionDecl, "f2"),
CXCursor::new(CXCursorKind::VarDecl, "x"),
],
standard: None,
};
assert_eq!(clang_getNumCursors(&tu), 3);
}
#[test]
fn test_clang_getCursor_from_tu() {
let tu = CXTranslationUnit {
filename: "test.c".into(),
ast: None,
diagnostics: vec![],
cursors: vec![CXCursor::new(CXCursorKind::FunctionDecl, "f")],
standard: None,
};
let cursor = clang_getCursor(&tu, 0);
assert!(cursor.is_some());
assert_eq!(cursor.unwrap().spelling, "f");
assert!(clang_getCursor(&tu, 1).is_none());
}
#[test]
fn test_clang_getTokenExtent() {
let tu =
CXTranslationUnit::parse("test.c", "int x;", &[], &[], CXTranslationUnitFlags::none());
let loc = CXSourceLocation {
file: "test.c".into(),
line: 1,
column: 1,
offset: 0,
};
let mut tok = CXToken::new(2, "int", loc);
tok.extent = CXSourceRange {
start: CXSourceLocation {
file: "test.c".into(),
line: 1,
column: 1,
offset: 0,
},
end: CXSourceLocation {
file: "test.c".into(),
line: 1,
column: 3,
offset: 2,
},
};
let extent = clang_getTokenExtent(&tu, &tok);
assert_eq!(extent.start.column, 1);
}
#[test]
fn test_clang_getAddressSpace() {
let ty = CXType::new(CXTypeKind::Pointer, "int*");
assert_eq!(clang_getAddressSpace(&ty), 0);
}
#[test]
fn test_clang_Type_getNumTemplateArguments() {
let mut ty = CXType::new(CXTypeKind::Unexposed, "vector<int>");
ty.num_template_args = 1;
assert_eq!(clang_Type_getNumTemplateArguments(&ty), 1);
}
}