objrs_macros/
lib.rs

1// The contents of this file is licensed by its authors and copyright holders under the Apache
2// License (Version 2.0), MIT license, or Mozilla Public License (Version 2.0), at your option. The
3// contents of this file may not be copied, modified, or distributed except according to those
4// terms. See the COPYRIGHT file at the top-level directory of this distribution for copies of these
5// licenses and more information.
6
7// TODO: Use span's resolved_at/located_at to use def_site() span with line/column information of user code.
8
9#![feature(proc_macro_diagnostic, proc_macro_span, proc_macro_def_site)]
10#![recursion_limit = "512"]
11
12extern crate core;
13extern crate proc_macro;
14extern crate proc_macro2;
15#[macro_use]
16extern crate quote;
17extern crate syn;
18
19use class::{parse_class, ClassAttr};
20use class_impl::{parse_impl, ImplAttr};
21use proc_macro::Diagnostic;
22use proc_macro2::{Span, TokenStream};
23use protocol::{parse_protocol, ProtocolAttr};
24use quote::quote;
25use syn::parse2;
26use syn::parse::{ParseStream, Parse};
27
28mod class;
29mod class_impl;
30mod gensym;
31mod ivar;
32mod property;
33mod protocol;
34mod selector;
35mod test;
36mod util;
37
38enum ObjrsAttr {
39  Impl(ImplAttr),
40  Class(ClassAttr),
41  Protocol(ProtocolAttr),
42  Selector(Span),
43  Ivar(Span),
44}
45
46impl Parse for ObjrsAttr {
47  fn parse(input: ParseStream) -> syn::parse::Result<Self> {
48    use syn::token::Impl;
49    use util::{ivar, selector, class, protocol};
50
51    let lookahead = input.lookahead1();
52    if lookahead.peek(Impl) {
53      return input.parse().map(ObjrsAttr::Impl);
54    } else if lookahead.peek(class) {
55      return input.parse().map(ObjrsAttr::Class);
56    } else if lookahead.peek(protocol) {
57      return input.parse().map(ObjrsAttr::Protocol);
58    } else if lookahead.peek(selector) {
59      return Ok(ObjrsAttr::Selector(input.cursor().span()));
60    } else if lookahead.peek(ivar) {
61      return Ok(ObjrsAttr::Ivar(input.cursor().span()));
62    }
63    return Err(lookahead.error());
64  }
65}
66
67fn to_diagnostic(err: syn::parse::Error) -> Diagnostic {
68  return err.span().unstable().error(err.to_string());
69}
70
71fn shim(args: TokenStream, input: TokenStream) -> Result<TokenStream, Diagnostic> {
72  match parse2(args).map_err(to_diagnostic)? {
73    ObjrsAttr::Impl(attr) => return parse_impl(attr, input),
74    ObjrsAttr::Class(attr) => return parse_class(attr, input),
75    ObjrsAttr::Protocol(attr) => return parse_protocol(attr, input),
76    ObjrsAttr::Selector(span) => {
77      return Err(
78        span
79          .unstable()
80          .error("attribute must be enclosed in an impl block with a #[objrs(impl)] attribute"),
81      )
82    }
83    ObjrsAttr::Ivar(span) => {
84      return Err(
85        span
86          .unstable()
87          .error("attribute must be enclosed in a struct item with a #[objrs(class)] attribute"),
88      )
89    }
90  }
91}
92
93#[proc_macro_attribute]
94pub fn objrs(
95  args: proc_macro::TokenStream,
96  input: proc_macro::TokenStream,
97) -> proc_macro::TokenStream {
98  match shim(args.into(), input.into()) {
99    Ok(stream) => return stream.into(),
100    Err(diagnostic) => {
101      diagnostic.emit();
102      return proc_macro::TokenStream::new();
103    }
104  }
105}
106
107#[proc_macro]
108pub fn selector(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
109  match selector::parse_selector_ref(input.into()) {
110    Ok(stream) => return stream.into(),
111    Err(diagnostic) => {
112      diagnostic.emit();
113      // Return an empty SEL. This should help other diagnostic messages.
114      return quote!{{
115        extern crate objrs as __objrs_root;
116        __objrs_root::runtime::SEL(0 as *const _)
117      }}.into();
118    }
119  }
120}
121
122// #[proc_macro]
123// pub fn objrs_external(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
124//   let iter = input.into_iter().map(|mut token_tree| {
125//     match token_tree {
126//       TokenTree::Op(op) if op.op() == ';' && op.spacing() == Spacing::Alone => {
127//         token_tree = TokenTree::Group(Group::new(Delimiter::Brace, TokenStream::new()))
128//       }
129//       _ => (),
130//     }
131
132//     return token_tree;
133//   });
134//   return iter.collect();
135// }
136
137// #[proc_macro_attribute]
138// pub fn hack(_args: proc_macro::TokenStream, input: proc_macro::TokenStream) -> proc_macro::TokenStream {
139//   use quote::quote;
140//   let pub_foo = proc_macro2::Ident::new("Foo", proc_macro2::Span::call_site());
141//   let priv_foo: proc_macro::TokenTree = proc_macro::Ident::new("Foo", proc_macro::Span::def_site()).into();//proc_macro2::Ident::new("Foo", proc_macro2::Span::def_site());
142//   let priv_foo: proc_macro::TokenStream = priv_foo.into();
143//   let priv_foo: proc_macro2::TokenStream = priv_foo.into();
144//   let foo = quote!{
145//     pub struct #priv_foo{
146//       x: u32,
147//     }
148//     impl core::ops::Deref for #pub_foo {
149//       type Target = #priv_foo;
150
151//       #[inline(always)]
152//       fn deref(&self) -> &Self::Target {
153//         return unsafe { core::mem::transmute(self) };
154//       }
155//     }
156//   };
157//   let input: TokenStream = input.into();
158//   return quote!{
159//     #input
160//     #foo
161//   }.into();
162// }
163
164// #[proc_macro]
165// pub fn nothing(_: proc_macro::TokenStream) -> proc_macro::TokenStream {
166//   return proc_macro::TokenStream::new();
167// }
168
169// #[proc_macro]
170// pub fn debug(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
171//   panic!("{:?}", input);
172// }