From ed9488629dd14e33a04ba88c455ffd5abdecf5cd Mon Sep 17 00:00:00 2001 From: Sergey Pepyakin Date: Fri, 25 Jan 2019 16:46:42 +0100 Subject: [PATCH] Return compile errors --- derive/src/error.rs | 39 +++++++++++++++++++++++++++++++++++++++ derive/src/lib.rs | 15 +++++++++++---- derive/src/parser.rs | 29 ++++++++++++++++++++--------- 3 files changed, 70 insertions(+), 13 deletions(-) create mode 100644 derive/src/error.rs diff --git a/derive/src/error.rs b/derive/src/error.rs new file mode 100644 index 0000000..3895f51 --- /dev/null +++ b/derive/src/error.rs @@ -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, +} + +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); + } +} diff --git a/derive/src/lib.rs b/derive/src/lib.rs index dad9af4..d9b4aff 100644 --- a/derive/src/lib.rs +++ b/derive/src/lib.rs @@ -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() + } + } } diff --git a/derive/src/parser.rs b/derive/src/parser.rs index e8149f3..8e42ccb 100644 --- a/derive/src/parser.rs +++ b/derive/src/parser.rs @@ -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, pub return_ty: Option, } @@ -34,8 +36,8 @@ pub struct ImplBlockDef { } /// Parse an incoming stream of tokens into externalities definition. -pub fn parse(input: proc_macro2::TokenStream) -> Result { - let item_impl = syn::parse2::(input).map_err(|_| ())?; +pub fn parse(input: proc_macro2::TokenStream) -> Result { + let item_impl = syn::parse2::(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 { 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(¶m_name, input.span()); - Param { ident } + Some(Ok(Param { ident })) }) - .collect::>(); + .collect::, CompileError>>()?; let return_ty = match sig.decl.output { ReturnType::Default => None,