use std::iter;
use std::vec;
use std::ptr;
use std::cmp::{min,max};
use clang_sys::*;
use crate::bindgen::clang::*;
use crate::expr::Loc;
use super::Res;
pub(crate) trait MapResult<T> {
fn map_res<U, E, F>(self, f: F) -> Result<Option<U>, E> where F: FnOnce(T) -> Result<U, E>;
}
impl<T> MapResult<T> for Option<T> {
fn map_res<U, E, F>(self, f: F) -> Result<Option<U>, E> where F: FnOnce(T) -> Result<U, E> {
if let Some(val) = self {
match f(val) {
Ok(v) => Ok(Some(v)),
Err(e) => Err(e),
}
} else {
Ok(None)
}
}
}
pub(crate) trait CursorExt where Self: Sized {
fn map<T, F: FnMut(Cursor) -> T>(&self, cb: F) -> iter::Map<vec::IntoIter<Self>, F>;
fn map_children<T, F: FnMut(Cursor) -> T>(&self, cb: F) -> Vec<T>;
fn map_children_err<T,F: FnMut(Cursor) -> Res<T>>(&self, cb: F) -> Res<Vec<T>>;
fn first_expr_child(&self) -> Option<Cursor>;
fn loc(&self) -> Loc;
fn file(&self) -> File;
fn new_cur(x: CXCursor) -> Option<Cursor>;
fn for_body(&self) -> Cursor;
fn var_init(&self) -> Option<Cursor>;
fn for_cond(&self) -> Option<Cursor>;
fn for_inc(&self) -> Option<Cursor>;
fn for_init(&self) -> Option<Cursor>;
fn if_else(&self) -> Option<Cursor>;
fn if_then(&self) -> Cursor;
fn if_cond(&self) -> Cursor;
fn if_condition_variable(&self) -> Option<Cursor>;
fn sub_expr(&self) -> Option<Cursor>;
fn is_anon(&self) -> bool;
fn is_function_macro(&self, tu: &TranslationUnit) -> bool;
fn binary_opcode(&self) -> BinaryOperatorKind;
fn unary_opcode(&self) -> UnaryOperatorKind;
fn get_int_value(&self) -> u64;
fn get_float_value(&self) -> f64;
fn storage(&self) -> CX_StorageClass ;
fn sizeof_arg_type(&self) -> Option<Type>;
}
impl CursorExt for Cursor {
fn map<T, F: FnMut(Cursor) -> T>(&self, cb: F) -> iter::Map<vec::IntoIter<Self>, F> {
self.collect_children().into_iter().map(cb)
}
fn map_children<T, F: FnMut(Cursor) -> T>(&self, mut cb: F) -> Vec<T> {
let mut children = vec![];
self.visit(|c| {
children.push(cb(c));
CXChildVisit_Continue
});
children
}
fn map_children_err<T,F: FnMut(Cursor) -> Res<T>>(&self, mut cb: F) -> Res<Vec<T>> {
let mut children = vec![];
let mut err = None;
self.visit(|c| {
match cb(c) {
Ok(c) => {
children.push(c);
CXChildVisit_Continue
}
Err(e) => {
err = Some(e);
CXChildVisit_Break
}
}
});
if let Some(e) = err {
Err(e)
} else {
Ok(children)
}
}
fn first_expr_child(&self) -> Option<Cursor> {
let mut r = None;
self.visit(|ch| {
match ch.kind() {
CXCursor_TypeRef => CXChildVisit_Continue,
_ => {
r = Some(ch);
CXChildVisit_Break
},
}
});
r
}
fn file(&self) -> File {
let (file, ..) = self.location().location();
file
}
fn loc(&self) -> Loc {
Loc::new(self.extent())
}
fn is_function_macro(&self, tu: &TranslationUnit) -> bool {
if let Some(t) = tu.tokens(self) {
if let Some(t) = t.get(1) {
return t.spelling == "("; }
}
false
}
fn is_anon(&self) -> bool {
unsafe {
clang_Cursor_isAnonymous(self.x) != 0
}
}
fn new_cur(x: CXCursor) -> Option<Cursor> {
let cur = Cursor {x};
if cur.is_valid() {
Some(cur)
} else {
None
}
}
fn for_body(&self) -> Cursor {
unsafe { Cursor {
x: c3_ForStmt_getBody(self.x)
} }
}
fn var_init(&self) -> Option<Cursor> {
unsafe { Self::new_cur(c3_VarDecl_getInit(self.x)) }
}
fn for_cond(&self) -> Option<Cursor> {
unsafe { Self::new_cur(c3_ForStmt_getCond(self.x)) }
}
fn for_inc(&self) -> Option<Cursor> {
unsafe { Self::new_cur(c3_ForStmt_getInc(self.x)) }
}
fn for_init(&self) -> Option<Cursor> {
unsafe { Self::new_cur(c3_ForStmt_getInit(self.x)) }
}
fn if_else(&self) -> Option<Cursor> {
unsafe { Self::new_cur(c3_IfStmt_getElse(self.x)) }
}
fn if_then(&self) -> Cursor {
unsafe { Cursor { x: c3_IfStmt_getThen(self.x) } }
}
fn if_cond(&self) -> Cursor {
unsafe { Cursor { x: c3_IfStmt_getCond(self.x) } }
}
fn if_condition_variable(&self) -> Option<Cursor> {
unsafe { Self::new_cur(c3_IfStmt_getConditionVariable(self.x)) }
}
fn sub_expr(&self) -> Option<Cursor> {
unsafe {
Self::new_cur(c3_Cursor_getSubExpr(self.x))
.or_else(||self.first_expr_child())
}
}
fn binary_opcode(&self) -> BinaryOperatorKind {
unsafe {
c3_Cursor_getBinaryOpcode(self.x)
}
}
fn unary_opcode(&self) -> UnaryOperatorKind {
unsafe {
c3_Cursor_getUnaryOpcode(self.x)
}
}
fn get_int_value(&self) -> u64 {
unsafe { c3_Cursor_getIntValue(self.x) }
}
fn get_float_value(&self) -> f64 {
unsafe { c3_Cursor_getFloatValue(self.x) }
}
fn sizeof_arg_type(&self) -> Option<Type> {
let ty = unsafe { Type { x: c3_UnaryExpr_getArgType(self.x) } };
if ty.is_valid() {
Some(ty)
} else {
None
}
}
fn storage(&self) -> CX_StorageClass {
unsafe {
clang_Cursor_getStorageClass(self.x)
}
}
}
pub trait RangeLocations {
fn locations(self) -> (SourceLocation, SourceLocation);
}
impl RangeLocations for CXSourceRange {
fn locations(self) -> (SourceLocation, SourceLocation) {
unsafe {
(SourceLocation {x:clang_getRangeStart(self)}, SourceLocation{x:clang_getRangeEnd(self)})
}
}
}
impl Loc {
#[must_use] pub fn new(ext: CXSourceRange) -> Self {
fn pos(loc: CXSourceLocation) -> (u32, u32, u32) {
unsafe {
let mut line = 0;
let mut col = 0;
let mut byte_pos = 0;
clang_getFileLocation(loc, ptr::null_mut(), &mut line, &mut col, &mut byte_pos);
(line as u32, col as u32, byte_pos as u32)
}
}
let (start_line, start_col, byte_pos) = pos(unsafe {clang_getRangeStart(ext)});
let (end_line, _, end_pos) = pos(unsafe {clang_getRangeEnd(ext)});
Loc {
byte_pos,
byte_len: end_pos - byte_pos,
start_col: start_col as u16,
start_line,
line_len: (end_line - start_line) as u16,
}
}
#[must_use] pub fn is_builtin(&self) -> bool {
self.start_line == 0
}
}
use std::slice;
use std::os::raw::c_uint;
pub fn comment_tokens(tu: &TranslationUnit) -> Option<Vec<crate::expr::Comment>> {
let range = tu.cursor().extent();
let mut tokens = vec![];
unsafe {
let mut token_ptr = ptr::null_mut();
let mut num_tokens: c_uint = 0;
clang_tokenize(tu.x, range, &mut token_ptr, &mut num_tokens);
if token_ptr.is_null() {
return None;
}
let token_array = slice::from_raw_parts(token_ptr, num_tokens as usize);
let mut last_code_line = 0;
for &token in token_array {
let kind = clang_getTokenKind(token);
let extent = clang_getTokenExtent(tu.x, token);
if kind != CXToken_Comment {
clang_getFileLocation(clang_getRangeEnd(extent), ptr::null_mut(), &mut last_code_line, ptr::null_mut(), ptr::null_mut());
continue;
}
let syntax = cxstring_into_string(clang_getTokenSpelling(tu.x, token));
let loc = Loc::new(extent);
tokens.push(crate::expr::Comment {
syntax,
loc,
after_code: last_code_line == loc.start_line,
});
}
clang_disposeTokens(tu.x, token_ptr, num_tokens);
}
Some(tokens)
}