ion_proc/function/
parameter.rs

1/*
2 * This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
5 */
6
7use proc_macro2::{Span, TokenStream};
8use quote::{format_ident, quote, quote_spanned};
9use syn::punctuated::Punctuated;
10use syn::spanned::Spanned as _;
11use syn::visit_mut::visit_type_mut;
12use syn::{Error, Expr, FnArg, Ident, Pat, PatType, Receiver, Result, Stmt, Token, Type, parse_quote, parse2};
13
14use crate::attribute::ParseAttribute as _;
15use crate::attribute::function::ParameterAttribute;
16use crate::utils::{pat_is_ident, path_ends_with};
17use crate::visitors::{LifetimeRemover, SelfRenamer};
18
19pub(crate) struct Parameter {
20	pub(crate) pat_ty: PatType,
21	convert: Option<Box<Expr>>,
22}
23
24#[derive(Clone)]
25pub(crate) enum ThisKind {
26	Ref { ty: Box<Type> },
27	Object,
28	Owned,
29}
30
31pub(crate) struct ThisParameter {
32	pat_ty: PatType,
33	kind: ThisKind,
34}
35
36pub(crate) struct Parameters {
37	pub(crate) parameters: Vec<Parameter>,
38	pub(crate) this: Option<(ThisParameter, Ident, usize)>,
39	pub(crate) nargs: u16,
40}
41
42impl Parameter {
43	pub(crate) fn from_arg(arg: &FnArg) -> Result<Parameter> {
44		match arg {
45			FnArg::Typed(pat_ty) => {
46				let mut pat_ty = pat_ty.clone();
47				let attribute = ParameterAttribute::from_attributes("ion", &mut pat_ty.attrs, ())?;
48				Ok(Parameter { pat_ty, convert: attribute.convert })
49			}
50			FnArg::Receiver(_) => Err(Error::new(arg.span(), "Expected Typed Function Argument")),
51		}
52	}
53
54	pub(crate) fn get_type_without_lifetimes(&self) -> Box<Type> {
55		let mut ty = self.pat_ty.ty.clone();
56		visit_type_mut(&mut LifetimeRemover, &mut ty);
57		ty
58	}
59
60	pub(crate) fn to_statement(&self, ion: &TokenStream, ident: &Ident) -> Result<Stmt> {
61		let span = self.pat_ty.span();
62		let ty = self.get_type_without_lifetimes();
63
64		let pat_ty: PatType = parse2(quote_spanned!(span => #ident: #ty))?;
65
66		let convert;
67		let convert = if let Some(convert) = &self.convert {
68			convert
69		} else {
70			convert = parse_quote!(());
71			&convert
72		};
73
74		parse2(quote_spanned!(span =>
75			let #pat_ty = #ion::function::FromArgument::from_argument(&mut __accessor, #convert)?;
76		))
77	}
78}
79
80impl ThisParameter {
81	pub(crate) fn from_arg(arg: &FnArg, class_ty: Option<&Type>) -> Result<Option<ThisParameter>> {
82		match arg {
83			FnArg::Typed(pat_ty) => {
84				let span = pat_ty.span();
85				let mut pat_ty = pat_ty.clone();
86				match class_ty {
87					Some(class_ty) if pat_is_ident(&pat_ty.pat, "self") => {
88						visit_type_mut(&mut SelfRenamer { ty: class_ty }, &mut pat_ty.ty);
89						parse_this(pat_ty, true, span).map(Some)
90					}
91					_ => {
92						let attribute = ParameterAttribute::from_attributes("ion", &mut pat_ty.attrs, ())?;
93						if attribute.this {
94							return parse_this(pat_ty, class_ty.is_some(), span).map(Some);
95						}
96						Ok(None)
97					}
98				}
99			}
100			FnArg::Receiver(recv) => {
101				if class_ty.is_none() {
102					return Err(Error::new(arg.span(), "Can only have self on Class Methods"));
103				}
104
105				let class_ty = class_ty.unwrap();
106				if recv.colon_token.is_some() {
107					return Err(Error::new(arg.span(), "Invalid type for self"));
108				}
109
110				let Receiver {
111					attrs, reference, mutability, self_token, ..
112				} = recv;
113				let lifetime = &reference.as_ref().unwrap().1;
114
115				let pat_ty =
116					parse2(quote_spanned!(recv.span() => #(#attrs)* #self_token: &#lifetime #mutability #class_ty))?;
117				parse_this(pat_ty, true, recv.span()).map(Some)
118			}
119		}
120	}
121
122	pub(crate) fn to_statement(&self, ion: &TokenStream, is_class: bool) -> Result<Stmt> {
123		let ThisParameter { pat_ty, kind } = self;
124		let mut pat_ty = pat_ty.clone();
125		pat_ty.attrs.clear();
126
127		if is_class && pat_is_ident(&pat_ty.pat, "self") {
128			pat_ty.pat = parse_quote!(self_);
129		}
130
131		match kind {
132			ThisKind::Ref { ty } => {
133				let mut ty = ty.clone();
134				visit_type_mut(&mut LifetimeRemover, &mut ty);
135
136				if is_class {
137					parse2(quote!(
138						let #pat_ty = <#ty as #ion::ClassDefinition>::get_mut_private(__cx, __this)?;
139					))
140				} else {
141					parse2(quote!(
142						let #pat_ty = <#ty as #ion::conversions::FromValue>::from_value(__cx, __accessor.this(), true, ())?;
143					))
144				}
145			}
146			ThisKind::Object => parse2(quote!(let #pat_ty = __this;)),
147			ThisKind::Owned => Err(Error::new(pat_ty.span(), "This cannot be owned")),
148		}
149	}
150}
151
152impl Parameters {
153	pub(crate) fn parse(parameters: &Punctuated<FnArg, Token![,]>, ty: Option<&Type>) -> Result<Parameters> {
154		let mut nargs: u16 = 0;
155		let mut this: Option<(ThisParameter, Ident, usize)> = None;
156
157		let parameters: Vec<_> = parameters
158			.iter()
159			.enumerate()
160			.filter_map(|(i, arg)| {
161				let this_param = ThisParameter::from_arg(arg, ty);
162				match this_param {
163					Ok(Some(this_param)) => {
164						if let Pat::Ident(pat_ident) = &*this_param.pat_ty.pat {
165							if let Some(this) = &this {
166								return Some(Err(Error::new(
167									this.1.span(),
168									"Unable to have multiple this/self parameters",
169								)));
170							}
171							let ident = pat_ident.ident.clone();
172							this = Some((this_param, ident, i));
173						}
174						return None;
175					}
176					Err(e) => return Some(Err(e)),
177					_ => (),
178				}
179
180				let param = match Parameter::from_arg(arg) {
181					Ok(param) => param,
182					Err(e) => return Some(Err(e)),
183				};
184				if let Type::Path(ty) = &*param.pat_ty.ty
185					&& !path_ends_with(&ty.path, "Opt")
186					&& !path_ends_with(&ty.path, "Rest")
187				{
188					nargs = match nargs.checked_add(1) {
189						Some(nargs) => nargs,
190						None => return Some(Err(Error::new(arg.span(), "Function has too many arguments"))),
191					}
192				}
193				Some(Ok(param))
194			})
195			.collect::<Result<_>>()?;
196
197		Ok(Parameters { parameters, this, nargs })
198	}
199
200	pub(crate) fn to_statements(&self, ion: &TokenStream) -> Result<(Vec<Stmt>, Vec<Ident>)> {
201		let mut statements = Vec::with_capacity(self.parameters.len());
202		let mut idents = Vec::with_capacity(self.parameters.len() + 1);
203		for (i, parameter) in self.parameters.iter().enumerate() {
204			let ident = format_ident!("__ion_var{}", i);
205			statements.push(parameter.to_statement(ion, &ident)?);
206			idents.push(ident);
207		}
208
209		if let Some((_, ident, index)) = &self.this
210			&& ident != "self"
211		{
212			idents.insert(*index, ident.clone());
213		}
214
215		Ok((statements, idents))
216	}
217
218	pub(crate) fn get_this_ident(&self) -> Option<Ident> {
219		self.this.as_ref().map(|x| x.1.clone())
220	}
221
222	pub(crate) fn to_this_statement(&self, ion: &TokenStream, is_class: bool) -> Result<Option<Stmt>> {
223		self.this.as_ref().map(|(this, _, _)| this.to_statement(ion, is_class)).transpose()
224	}
225
226	pub(crate) fn to_args(&self) -> Vec<FnArg> {
227		let mut args = Vec::with_capacity(self.parameters.len() + usize::from(self.this.is_some()));
228
229		args.extend(
230			self.parameters
231				.iter()
232				.map(|parameter| FnArg::Typed(parameter.pat_ty.clone()))
233				.collect::<Vec<_>>(),
234		);
235
236		if let Some((ThisParameter { pat_ty, .. }, _, index)) = &self.this {
237			args.insert(*index, FnArg::Typed(pat_ty.clone()));
238		}
239
240		args
241	}
242}
243
244pub(crate) fn parse_this(pat_ty: PatType, is_class: bool, span: Span) -> Result<ThisParameter> {
245	match &*pat_ty.ty {
246		Type::Reference(reference) => {
247			let elem = reference.clone().elem;
248			match &*elem {
249				Type::Path(ty) if path_ends_with(&ty.path, "Object") => {
250					Ok(ThisParameter { pat_ty, kind: ThisKind::Object })
251				}
252				_ => Ok(ThisParameter { pat_ty, kind: ThisKind::Ref { ty: elem } }),
253			}
254		}
255		_ if !is_class => Ok(ThisParameter { pat_ty, kind: ThisKind::Owned }),
256		_ => Err(Error::new(span, "Invalid type for self")),
257	}
258}