Skip to content

Commit

Permalink
Implement procedural macro diagnostics
Browse files Browse the repository at this point in the history
commit-id:f06e9653
  • Loading branch information
maciektr committed Feb 22, 2024
1 parent b75f088 commit 67107f3
Show file tree
Hide file tree
Showing 6 changed files with 314 additions and 41 deletions.
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

32 changes: 30 additions & 2 deletions plugins/cairo-lang-macro-stable/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,21 +15,49 @@ pub enum StableAuxData {
Some(*mut c_char),
}

/// Diagnostic returned by the procedural macro.
///
/// This struct implements FFI-safe stable ABI.
#[repr(C)]
#[derive(Debug)]
pub struct StableDiagnostic {
pub message: *mut c_char,
pub severity: StableSeverity,
}

/// The severity of a diagnostic.
///
/// This struct implements FFI-safe stable ABI.
#[repr(C)]
#[derive(Debug)]
pub enum StableSeverity {
Error,
Warning,
}

/// Procedural macro result.
///
/// This struct implements FFI-safe stable ABI.
#[repr(C)]
#[derive(Debug)]
pub enum StableProcMacroResult {
/// Plugin has not taken any action.
Leave,
Leave {
diagnostics: *mut StableDiagnostic,
diagnostics_n: usize,
},
/// Plugin generated [`StableTokenStream`] replacement.
Replace {
token_stream: StableTokenStream,
aux_data: StableAuxData,
diagnostics: *mut StableDiagnostic,
diagnostics_n: usize,
},
/// Plugin ordered item removal.
Remove,
Remove {
diagnostics: *mut StableDiagnostic,
diagnostics_n: usize,
},
}

impl StableTokenStream {
Expand Down
1 change: 1 addition & 0 deletions plugins/cairo-lang-macro/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ repository.workspace = true

[dependencies]
anyhow.workspace = true
libc.workspace = true
cairo-lang-macro-attributes = { path = "../cairo-lang-macro-attributes" }
cairo-lang-macro-stable = { path = "../cairo-lang-macro-stable" }
serde.workspace = true
Expand Down
187 changes: 171 additions & 16 deletions plugins/cairo-lang-macro/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,21 +1,25 @@
use libc::{free, malloc};
use serde_json::Value;
use std::ffi::{c_char, CString};
use std::ffi::{c_char, c_void, CString};
use std::fmt::Display;

pub use cairo_lang_macro_attributes::*;
use cairo_lang_macro_stable::{StableAuxData, StableProcMacroResult, StableTokenStream};
use cairo_lang_macro_stable::{
StableAuxData, StableDiagnostic, StableProcMacroResult, StableSeverity, StableTokenStream,
};

#[derive(Debug)]
pub enum ProcMacroResult {
/// Plugin has not taken any action.
Leave,
Leave { diagnostics: Vec<Diagnostic> },
/// Plugin generated [`TokenStream`] replacement.
Replace {
token_stream: TokenStream,
aux_data: Option<AuxData>,
diagnostics: Vec<Diagnostic>,
},
/// Plugin ordered item removal.
Remove,
Remove { diagnostics: Vec<Diagnostic> },
}

#[derive(Debug, Default, Clone)]
Expand Down Expand Up @@ -48,22 +52,70 @@ impl AuxData {
}
}

/// Diagnostic returned by the procedural macro.
#[derive(Debug)]
pub struct Diagnostic {
pub message: String,
pub severity: Severity,
}

/// The severity of a diagnostic.
#[derive(Debug)]
pub enum Severity {
Error,
Warning,
}

impl Diagnostic {
pub fn error(message: impl ToString) -> Self {
Self {
message: message.to_string(),
severity: Severity::Error,
}
}

pub fn warn(message: impl ToString) -> Self {
Self {
message: message.to_string(),
severity: Severity::Warning,
}
}
}

impl ProcMacroResult {
/// Convert to FFI-safe representation.
///
/// # Safety
#[doc(hidden)]
pub fn into_stable(self) -> StableProcMacroResult {
match self {
ProcMacroResult::Leave => StableProcMacroResult::Leave,
ProcMacroResult::Remove => StableProcMacroResult::Remove,
ProcMacroResult::Leave { diagnostics } => {
let (ptr, n) = unsafe { Diagnostic::allocate(diagnostics) };
StableProcMacroResult::Leave {
diagnostics: ptr,
diagnostics_n: n,
}
}
ProcMacroResult::Remove { diagnostics } => {
let (ptr, n) = unsafe { Diagnostic::allocate(diagnostics) };
StableProcMacroResult::Remove {
diagnostics: ptr,
diagnostics_n: n,
}
}
ProcMacroResult::Replace {
token_stream,
aux_data,
} => StableProcMacroResult::Replace {
token_stream: token_stream.into_stable(),
aux_data: AuxData::maybe_into_stable(aux_data),
},
diagnostics,
} => {
let (ptr, n) = unsafe { Diagnostic::allocate(diagnostics) };
StableProcMacroResult::Replace {
token_stream: token_stream.into_stable(),
aux_data: AuxData::maybe_into_stable(aux_data),
diagnostics: ptr,
diagnostics_n: n,
}
}
}
}

Expand All @@ -73,15 +125,33 @@ impl ProcMacroResult {
#[doc(hidden)]
pub unsafe fn from_stable(result: StableProcMacroResult) -> Self {
match result {
StableProcMacroResult::Leave => ProcMacroResult::Leave,
StableProcMacroResult::Remove => ProcMacroResult::Remove,
StableProcMacroResult::Leave {
diagnostics,
diagnostics_n,
} => {
let diagnostics = Diagnostic::deallocate(diagnostics, diagnostics_n);
ProcMacroResult::Leave { diagnostics }
}
StableProcMacroResult::Remove {
diagnostics,
diagnostics_n,
} => {
let diagnostics = Diagnostic::deallocate(diagnostics, diagnostics_n);
ProcMacroResult::Remove { diagnostics }
}
StableProcMacroResult::Replace {
token_stream,
aux_data,
} => ProcMacroResult::Replace {
token_stream: TokenStream::from_stable(token_stream),
aux_data: AuxData::from_stable(aux_data).unwrap(),
},
diagnostics,
diagnostics_n,
} => {
let diagnostics = Diagnostic::deallocate(diagnostics, diagnostics_n);
ProcMacroResult::Replace {
token_stream: TokenStream::from_stable(token_stream),
aux_data: AuxData::from_stable(aux_data).unwrap(),
diagnostics,
}
}
}
}
}
Expand Down Expand Up @@ -138,6 +208,91 @@ impl AuxData {
}
}

impl Diagnostic {
/// Convert to FFI-safe representation.
///
/// # Safety
#[doc(hidden)]
pub fn into_stable(self) -> StableDiagnostic {
let cstr = CString::new(self.message).unwrap();
StableDiagnostic {
message: cstr.into_raw(),
severity: self.severity.into_stable(),
}
}

/// Convert to native Rust representation.
///
/// # Safety
#[doc(hidden)]
pub unsafe fn from_stable(diagnostic: StableDiagnostic) -> Self {
Self {
message: raw_to_string(diagnostic.message),
severity: Severity::from_stable(diagnostic.severity),
}
}

/// Allocate dynamic array with FFI-safe diagnostics.
///
/// # Safety
#[doc(hidden)]
pub unsafe fn allocate(diagnostics: Vec<Self>) -> (*mut StableDiagnostic, usize) {
let stable_diagnostics = diagnostics
.into_iter()
.map(|diagnostic| diagnostic.into_stable())
.collect::<Vec<_>>();
let n = stable_diagnostics.len();
let ptr = malloc(std::mem::size_of::<StableDiagnostic>() * n) as *mut StableDiagnostic;
if ptr.is_null() {
panic!("memory allocation with malloc failed");
}
for (i, diag) in stable_diagnostics.into_iter().enumerate() {
let ptr = ptr.add(i);
std::ptr::write(ptr, diag);
}
(ptr, n)
}

/// Deallocate dynamic array of diagnostics, returning a vector.
///
/// # Safety
pub unsafe fn deallocate(ptr: *mut StableDiagnostic, n: usize) -> Vec<Diagnostic> {
let mut diagnostics: Vec<Diagnostic> = Vec::with_capacity(n);
for i in 0..n {
let ptr = ptr.add(i);
let diag = std::ptr::read(ptr);
let diag = Diagnostic::from_stable(diag);
diagnostics.push(diag);
}
free(ptr as *mut c_void);
diagnostics
}
}

impl Severity {
/// Convert to FFI-safe representation.
///
/// # Safety
#[doc(hidden)]
pub fn into_stable(self) -> StableSeverity {
match self {
Severity::Error => StableSeverity::Error,
Severity::Warning => StableSeverity::Warning,
}
}

/// Convert to native Rust representation.
///
/// # Safety
#[doc(hidden)]
pub unsafe fn from_stable(severity: StableSeverity) -> Self {
match severity {
StableSeverity::Error => Self::Error,
StableSeverity::Warning => Self::Warning,
}
}
}

unsafe fn raw_to_string(raw: *mut c_char) -> String {
if raw.is_null() {
String::default()
Expand Down
Loading

0 comments on commit 67107f3

Please sign in to comment.