Return compile errors

This commit is contained in:
Sergey Pepyakin 2019-01-25 16:46:42 +01:00
parent 2b149f5bdf
commit ed9488629d
3 changed files with 70 additions and 13 deletions

39
derive/src/error.rs Normal file
View File

@ -0,0 +1,39 @@
use proc_macro2::{TokenStream, Span};
use quote::{quote_spanned, ToTokens};
macro_rules! err_span {
($span:expr, $($msg:tt)*) => (
$crate::error::CompileError::new_spanned(&$span, format!($($msg)*))
)
}
pub struct CompileError {
msg: String,
span: Option<Span>,
}
impl CompileError {
pub fn new_spanned(span: &Span, msg: String) -> Self {
CompileError {
span: Some(*span),
msg,
}
}
pub fn new(msg: String) -> Self {
CompileError {
span: None,
msg,
}
}
}
impl ToTokens for CompileError {
fn to_tokens(&self, dst: &mut TokenStream) {
let msg = &self.msg;
let span = self.span.unwrap_or_else(|| Span::call_site());
(quote_spanned! { span=>
compile_error!(#msg);
}).to_tokens(dst);
}
}

View File

@ -44,6 +44,8 @@
extern crate proc_macro;
#[macro_use]
mod error;
mod codegen;
mod parser;
@ -53,8 +55,13 @@ use proc_macro::TokenStream;
pub fn derive_externals(_attr: TokenStream, input: TokenStream) -> TokenStream {
let mut input: proc_macro2::TokenStream = input.into();
let ext_def = parser::parse(input.clone()).unwrap();
codegen::codegen(&ext_def, &mut input);
input.into()
match parser::parse(input.clone()) {
Ok(ext_def) => {
codegen::codegen(&ext_def, &mut input);
input.into()
}
Err(err) => {
(quote::quote! { #err }).into()
}
}
}

View File

@ -1,6 +1,7 @@
use syn::{spanned::Spanned, Ident, ImplItem, ImplItemMethod, ReturnType};
use crate::error::CompileError;
use syn::{spanned::Spanned, FnArg, Ident, ImplItem, ImplItemMethod, ReturnType};
/// A parameter.
/// A parameter. This doesn't used for modeling `&self` or `&mut self` parameters.
#[derive(Clone)]
pub struct Param {
/// A generated identifier used to name temporary variables
@ -15,6 +16,7 @@ pub struct FuncDef {
/// Assigned index of this function.
pub index: u32,
pub name: String,
/// The parameter of this function. This excludes the `&self` or `&mut self`.
pub params: Vec<Param>,
pub return_ty: Option<proc_macro2::Span>,
}
@ -34,8 +36,8 @@ pub struct ImplBlockDef {
}
/// Parse an incoming stream of tokens into externalities definition.
pub fn parse(input: proc_macro2::TokenStream) -> Result<ImplBlockDef, ()> {
let item_impl = syn::parse2::<syn::ItemImpl>(input).map_err(|_| ())?;
pub fn parse(input: proc_macro2::TokenStream) -> Result<ImplBlockDef, CompileError> {
let item_impl = syn::parse2::<syn::ItemImpl>(input).map_err(|_| CompileError::new("failed to parse".to_string()))?;
let mut funcs = vec![];
@ -44,19 +46,28 @@ pub fn parse(input: proc_macro2::TokenStream) -> Result<ImplBlockDef, ()> {
ImplItem::Method(ImplItemMethod { sig, .. }) => {
let index = funcs.len() as u32;
// self TODO: handle this properly
let params = sig
.decl
.inputs
.iter()
.skip(1)
.enumerate()
.map(|(idx, input)| {
.filter_map(|(idx, input)| {
// The first parameter should be either &self or &mut self.
// This makes code generation simpler.
if idx == 0 {
match input {
FnArg::SelfRef(_) => return None,
_ => return Some(
Err(err_span!(input.span(), "only &self and &mut self supported as first argument"))
),
}
}
let param_name = format!("arg{}", idx);
let ident = Ident::new(&param_name, input.span());
Param { ident }
Some(Ok(Param { ident }))
})
.collect::<Vec<_>>();
.collect::<Result<Vec<Param>, CompileError>>()?;
let return_ty = match sig.decl.output {
ReturnType::Default => None,