1use quote::{ToTokens, quote};
93use syn::parse::{Parse, ParseStream};
94use syn::spanned::Spanned;
95use syn::{Expr, ExprGroup, Token};
96
97struct CloneExpr {
99 mutability: Option<Token![mut]>,
100 inner: Expr,
101}
102
103impl Parse for CloneExpr {
104 fn parse(input: ParseStream) -> syn::Result<Self> {
105 let mutability = if input.peek(Token![mut]) {
106 Some(input.parse()?)
107 } else {
108 None
109 };
110 let inner: Expr = input.parse()
111 .map_err(|e| syn::Error::new(e.span(), "expected a valid expression: field access (a.b), tuple index access (a.0), method call (a.method()), or path (var)"))?;
112 Ok(CloneExpr { mutability, inner })
113 }
114}
115
116impl ToTokens for CloneExpr {
117 fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
118 tokens.extend(quote! { let });
119 if let Some(m) = &self.mutability {
120 tokens.extend(quote! { #m });
121 }
122 let inner = &self.inner;
123 extend(inner, tokens);
124 }
125}
126
127fn expr_variant_description(expr: &Expr) -> &'static str {
128 match expr {
129 Expr::Array(_) => "array expression",
130 Expr::Assign(_) => "assignment expression",
131 Expr::Async(_) => "async block",
132 Expr::Await(_) => "await expression",
133 Expr::Binary(_) => "binary expression",
134 Expr::Block(_) => "block expression",
135 Expr::Break(_) => "break expression",
136 Expr::Call(_) => "function call expression",
137 Expr::Cast(_) => "cast expression",
138 Expr::Closure(_) => "closure expression",
139 Expr::Const(_) => "const block",
140 Expr::Continue(_) => "continue expression",
141 Expr::Field(_) => "field access expression",
142 Expr::ForLoop(_) => "for loop expression",
143 Expr::Group(_) => "grouped expression",
144 Expr::If(_) => "if expression",
145 Expr::Index(_) => "index expression",
146 Expr::Infer(_) => "inferred expression",
147 Expr::Let(_) => "let expression",
148 Expr::Lit(_) => "literal expression",
149 Expr::Loop(_) => "loop expression",
150 Expr::Macro(_) => "macro expression",
151 Expr::Match(_) => "match expression",
152 Expr::MethodCall(_) => "method call expression",
153 Expr::Paren(_) => "parenthesized expression",
154 Expr::Path(_) => "path expression",
155 Expr::Range(_) => "range expression",
156 Expr::RawAddr(_) => "raw address expression",
157 Expr::Reference(_) => "reference expression",
158 Expr::Repeat(_) => "array repeat expression",
159 Expr::Return(_) => "return expression",
160 Expr::Struct(_) => "struct literal expression",
161 Expr::Try(_) => "try expression",
162 Expr::TryBlock(_) => "try block",
163 Expr::Tuple(_) => "tuple expression",
164 Expr::Unary(_) => "unary expression",
165 Expr::Unsafe(_) => "unsafe block",
166 Expr::Verbatim(_) => "verbatim expression",
167 Expr::While(_) => "while expression",
168 Expr::Yield(_) => "yield expression",
169 _ => "expression",
170 }
171}
172
173fn extend(expr: &Expr, tokens: &mut proc_macro2::TokenStream) {
174 match expr {
175 Expr::Field(syn::ExprField {
176 base,
177 member: syn::Member::Named(field_name),
178 ..
179 }) => {
180 tokens.extend(quote! {
181 #field_name = #base.#field_name.clone();
182 });
183 }
184 Expr::Field(syn::ExprField {
185 base,
186 member: syn::Member::Unnamed(index),
187 ..
188 }) => {
189 let index_num = index.index;
190 let ident = syn::Ident::new(&format!("field_{}", index_num), index.span());
191 tokens.extend(quote! {
192 #ident = #base.#index.clone();
193 });
194 }
195 Expr::MethodCall(expr_method_call) => {
196 let method = &expr_method_call.method;
197 tokens.extend(quote! {
198 #method = #expr.clone();
199 });
200 }
201 Expr::Path(syn::ExprPath { path, .. }) => {
202 let ident = &path.segments.last().unwrap().ident;
203 tokens.extend(quote! {
204 #ident = #expr.clone();
205 });
206 }
207 Expr::Group(ExprGroup { expr, .. }) => {
208 extend(&expr, tokens);
209 }
210 _ => {
211 panic!(
212 "clone! macro does not support {}. Supported forms: field access (`a.b`), tuple index access (`a.0`), method call (`a.method()`), or path (`var`).",
213 expr_variant_description(expr)
214 );
215 }
216 }
217}
218
219struct CloneExprList {
221 exprs: Vec<CloneExpr>,
222}
223
224impl Parse for CloneExprList {
225 fn parse(input: ParseStream) -> syn::Result<Self> {
226 let mut exprs = Vec::new();
227 while !input.is_empty() {
228 let expr: CloneExpr = input.parse().map_err(|e| {
229 syn::Error::new(e.span(), format!("failed to parse clone expression: {}", e))
230 })?;
231 exprs.push(expr);
232 if input.peek(Token![,]) {
233 let _comma: Token![,] = input.parse()?;
234 } else {
235 break;
236 }
237 }
238 if exprs.is_empty() {
239 return Err(syn::Error::new(
240 input.span(),
241 "clone! macro requires at least one expression",
242 ));
243 }
244 Ok(CloneExprList { exprs })
245 }
246}
247
248impl ToTokens for CloneExprList {
249 fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
250 for expr in &self.exprs {
251 expr.to_tokens(tokens);
252 }
253 }
254}
255
256#[proc_macro]
291pub fn clone(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
292 let expr_list = syn::parse_macro_input!(input as CloneExprList);
293 let mut tokens = proc_macro2::TokenStream::new();
294 expr_list.to_tokens(&mut tokens);
295 proc_macro::TokenStream::from(tokens)
296}