use std::fmt;
use std::mem;
use std::cmp::{self, Ordering};
use clang_sys::*;
use utility;
use super::{TranslationUnit};
use super::source::{SourceLocation, SourceRange};
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum FixIt<'tu> {
Deletion(SourceRange<'tu>),
Insertion(SourceLocation<'tu>, String),
Replacement(SourceRange<'tu>, String),
}
#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[repr(C)]
pub enum Severity {
Ignored = 0,
Note = 1,
Warning = 2,
Error = 3,
Fatal = 4,
}
#[derive(Copy, Clone)]
pub struct Diagnostic<'tu> {
ptr: CXDiagnostic,
tu: &'tu TranslationUnit<'tu>,
}
impl<'tu> Diagnostic<'tu> {
#[doc(hidden)]
pub fn from_ptr(ptr: CXDiagnostic, tu: &'tu TranslationUnit<'tu>) -> Diagnostic<'tu> {
assert!(!ptr.is_null());
Diagnostic { ptr, tu }
}
pub fn get_severity(&self) -> Severity {
unsafe { mem::transmute(clang_getDiagnosticSeverity(self.ptr)) }
}
pub fn get_text(&self) -> String {
unsafe { utility::to_string(clang_getDiagnosticSpelling(self.ptr)) }
}
pub fn get_location(&self) -> SourceLocation<'tu> {
unsafe { SourceLocation::from_raw(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_fix_its(&self) -> Vec<FixIt<'tu>> {
unsafe {
(0..clang_getDiagnosticNumFixIts(self.ptr)).map(|i| {
let mut range = mem::MaybeUninit::uninit();
let fixit = clang_getDiagnosticFixIt(self.ptr, i, range.as_mut_ptr());
let string = utility::to_string(fixit);
let range = SourceRange::from_raw(range.assume_init(), 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_children(&self) -> Vec<Diagnostic> {
let ptr = unsafe { clang_getChildDiagnostics(self.ptr) };
iter!(
clang_getNumDiagnosticsInSet(ptr),
clang_getDiagnosticInSet(ptr),
).map(|d| Diagnostic::from_ptr(d, self.tu)).collect()
}
pub fn formatter(&self) -> DiagnosticFormatter<'tu> {
DiagnosticFormatter::new(*self)
}
}
#[doc(hidden)]
impl<'tu> cmp::PartialEq for Diagnostic<'tu> {
fn eq(&self, other: &Diagnostic<'tu>) -> bool {
self.ptr == other.ptr
}
}
impl<'tu> cmp::PartialOrd for Diagnostic<'tu> {
fn partial_cmp(&self, other: &Diagnostic<'tu>) -> Option<Ordering> {
Some(self.get_severity().cmp(&other.get_severity()))
}
}
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, "{}", DiagnosticFormatter::new(*self).format())
}
}
builder! {
builder DiagnosticFormatter: CXDiagnosticDisplayOptions {
diagnostic: Diagnostic<'tu>;
OPTIONS:
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<'tu> DiagnosticFormatter<'tu> {
fn new(diagnostic: Diagnostic<'tu>) -> DiagnosticFormatter<'tu> {
let flags = unsafe { clang_defaultDiagnosticDisplayOptions() };
DiagnosticFormatter { diagnostic, flags }
}
pub fn format(&self) -> String {
unsafe { utility::to_string(clang_formatDiagnostic(self.diagnostic.ptr, self.flags)) }
}
}