1#![feature(proc_macro_diagnostic)]
2
3extern crate proc_macro;
4
5mod spanned;
6
7use crate::spanned::SpannedUnstable;
8use proc_macro::{Diagnostic, Level, TokenStream};
9use quote::quote;
10use syn::{
11 parse_macro_input, parse_quote, punctuated::Punctuated, token::Comma, AttributeArgs, FnArg,
12 FnDecl, ImplItem, Item, ItemImpl, MethodSig, ReturnType, Type,
13};
14
15type Result<T> = std::result::Result<T, Diagnostic>;
16
17#[proc_macro_attribute]
18pub fn autoresolvable(attr: TokenStream, item: TokenStream) -> TokenStream {
19 let item = parse_macro_input!(item as Item);
20 let _attr = parse_macro_input!(attr as AttributeArgs);
21
22 let result = generate_autoresolvable_impl(&item);
23
24 let emitted_tokens = match result {
25 Ok(token_stream) => token_stream,
26 Err(diagnostic) => {
27 diagnostic.emit();
28 quote! {
29 #item
30 }
31 }
32 };
33
34 emitted_tokens.into()
35}
36
37fn generate_autoresolvable_impl(item: &Item) -> Result<proc_macro2::TokenStream> {
38 let item = parse_item_impl(item)?;
39
40 validate_item_impl(item)?;
41
42 let self_ty = &item.self_ty;
43
44 let constructors = parse_constructors(&item);
45
46 validate_constructors(item, &constructors)?;
47
48 let constructor = determine_constructor_to_autoresolve(&constructors);
49
50 let constructor_argument_types = parse_constructor_argument_types(constructor)?;
51
52 let resolutions = generate_type_resolutions(&constructor_argument_types);
53
54 let (impl_generics, _type_generics, where_clause) = item.generics.split_for_impl();
55 let ident = &constructor.ident;
56
57 Ok(quote! {
58 #item
59
60 impl #impl_generics wonderbox::internal::AutoResolvable for #self_ty #where_clause {
61 fn try_resolve(container: &wonderbox::Container) -> Option<Self> {
62 Some(Self::#ident(#resolutions))
63 }
64 }
65 })
66}
67
68fn parse_item_impl(item: &Item) -> Result<&ItemImpl> {
69 match item {
70 Item::Impl(item_impl) => Ok(item_impl),
71 _ => {
72 let error_message = format!("{} needs to be placed over an impl block", ATTRIBUTE_NAME);
73 Err(Diagnostic::spanned(
74 item.span_unstable(),
75 Level::Error,
76 error_message,
77 ))
78 }
79 }
80}
81
82fn validate_item_impl(item_impl: &ItemImpl) -> Result<()> {
83 if item_impl.trait_.is_none() {
84 Ok(())
85 } else {
86 let error_message = format!(
87 "{} must be placed over a direct impl, not a trait impl",
88 ATTRIBUTE_NAME
89 );
90 Err(Diagnostic::spanned(
91 item_impl.span_unstable(),
92 Level::Error,
93 error_message,
94 ))
95 }
96}
97
98fn validate_constructors(item_impl: &ItemImpl, constructors: &[&MethodSig]) -> Result<()> {
99 if constructors.len() == 1 {
100 Ok(())
101 } else {
102 let error_message = format!("Expected one constructor, found {}", constructors.len());
103 Err(Diagnostic::spanned(
104 item_impl.span_unstable(),
105 Level::Error,
106 error_message,
107 ))
108 }
109}
110
111fn determine_constructor_to_autoresolve<'a, 'b>(
112 constructors: &'a [&'b MethodSig],
113) -> &'b MethodSig {
114 constructors.first().unwrap()
115}
116
117fn parse_constructors(item_impl: &ItemImpl) -> Vec<&MethodSig> {
118 item_impl
119 .items
120 .iter()
121 .filter_map(parse_method_signature)
122 .filter(|declaration| returns_self(&declaration.decl, &item_impl.self_ty))
123 .filter(|inputs| has_no_self_parameter(&inputs.decl))
124 .collect()
125}
126
127fn parse_method_signature(impl_item: &ImplItem) -> Option<&MethodSig> {
128 match impl_item {
129 ImplItem::Method(method) => Some(&method.sig),
130 _ => None,
131 }
132}
133
134fn returns_self(function: &FnDecl, explicit_self_type: &Type) -> bool {
135 match &function.output {
136 ReturnType::Default => false,
137 ReturnType::Type(_, return_type) => {
138 **return_type == generate_self_type() || **return_type == *explicit_self_type
139 }
140 }
141}
142
143fn has_no_self_parameter(function: &FnDecl) -> bool {
144 let first_input = function.inputs.first();
145 match first_input {
146 Some(first_arg) => match first_arg.value() {
147 FnArg::SelfRef(_) | FnArg::SelfValue(_) => false,
148 _ => true,
149 },
150 None => true,
151 }
152}
153
154fn parse_constructor_argument_types(constructor: &MethodSig) -> Result<Vec<&Type>> {
155 constructor
156 .decl
157 .inputs
158 .iter()
159 .map(|arg| match arg {
160 FnArg::SelfRef(_) | FnArg::SelfValue(_) => unreachable!(),
161 FnArg::Captured(arg) => Ok(&arg.ty),
162 _ => Err(Diagnostic::spanned(
163 arg.span_unstable(),
164 Level::Error,
165 "Only normal, non self type parameters are supported",
166 )),
167 })
168 .collect()
169}
170
171fn generate_type_resolutions(types: &[&Type]) -> Punctuated<proc_macro2::TokenStream, Comma> {
172 types
173 .iter()
174 .map(|type_| {
175 quote! {
176 container.try_resolve::<#type_>()?
177 }
178 })
179 .collect()
180}
181
182fn generate_self_type() -> Type {
183 parse_quote! {
184 Self
185 }
186}
187
188const ATTRIBUTE_NAME: &str = "#[autoresolvable]";