blob: f5554191a7623f0e6c9df5b7c448680b0c37cb6f [file] [log] [blame]
Brian Silverman4e662aa2022-05-11 23:10:19 -07001// Copyright 2021 Google LLC
2//
3// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
4// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
5// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
6// option. This file may not be copied, modified, or distributed
7// except according to those terms.
8
9use indexmap::set::IndexSet as HashSet;
10
11use autocxx_parser::{
12 directive_names::{EXTERN_RUST_FUN, EXTERN_RUST_TYPE},
13 RustFun, RustPath,
14};
15use itertools::Itertools;
16use proc_macro2::Ident;
Austin Schuh6ea9bfa2023-08-06 19:05:10 -070017use syn::visit_mut::{visit_type_mut, VisitMut};
Brian Silverman4e662aa2022-05-11 23:10:19 -070018use syn::{
Austin Schuh6ea9bfa2023-08-06 19:05:10 -070019 parse_quote, punctuated::Punctuated, AssocConst, AssocType, Attribute, Expr, ExprAssign,
20 ExprAwait, ExprBinary, ExprBlock, ExprBreak, ExprCast, ExprConst, ExprField, ExprGroup,
21 ExprLet, ExprParen, ExprReference, ExprTry, ExprUnary, ImplItem, Item, ItemEnum, ItemStruct,
22 LocalInit, Pat, PatReference, PatSlice, PatTuple, Path, ReturnType, Signature, Stmt, TraitItem,
23 Type, TypeArray, TypeGroup, TypeParamBound, TypeParen, TypePtr, TypeReference, TypeSlice,
Brian Silverman4e662aa2022-05-11 23:10:19 -070024};
25use thiserror::Error;
26
27#[derive(Default)]
28pub(super) struct Discoveries {
29 pub(super) cpp_list: HashSet<String>,
30 pub(super) extern_rust_funs: Vec<RustFun>,
31 pub(super) extern_rust_types: Vec<RustPath>,
32}
33
34#[derive(Error, Debug)]
35pub enum DiscoveryErr {
36 #[error("#[extern_rust_function] was attached to a method in an impl block that was too complex for autocxx. autocxx supports only \"impl X {{...}}\" where X is a single identifier, not a path or more complex type.")]
37 FoundExternRustFunOnTypeWithoutClearReceiver,
Brian Silverman4e662aa2022-05-11 23:10:19 -070038 #[error("#[extern_rust_function] was attached to a method taking a receiver by value.")]
39 NoParameterOnMethod,
40 #[error("#[extern_rust_function] was in an impl block nested wihtin another block. This is only supported in the outermost mod of a file, alongside the include_cpp!.")]
41 FoundExternRustFunWithinMod,
42}
43
44impl Discoveries {
45 pub(super) fn search_item(
46 &mut self,
47 item: &Item,
48 mod_path: Option<RustPath>,
49 ) -> Result<(), DiscoveryErr> {
50 let mut this_mod = PerModDiscoveries {
51 discoveries: self,
52 mod_path,
53 };
54 this_mod.search_item(item)
55 }
56
57 pub(crate) fn found_allowlist(&self) -> bool {
58 !self.cpp_list.is_empty()
59 }
60
61 pub(crate) fn found_rust(&self) -> bool {
62 !self.extern_rust_funs.is_empty() || !self.extern_rust_types.is_empty()
63 }
64
65 pub(crate) fn extend(&mut self, other: Self) {
66 self.cpp_list.extend(other.cpp_list);
67 self.extern_rust_funs.extend(other.extern_rust_funs);
68 self.extern_rust_types.extend(other.extern_rust_types);
69 }
70}
71
72struct PerModDiscoveries<'a> {
73 discoveries: &'a mut Discoveries,
74 mod_path: Option<RustPath>,
75}
76
77impl<'b> PerModDiscoveries<'b> {
78 fn deeper_path(&self, id: &Ident) -> RustPath {
79 match &self.mod_path {
80 None => RustPath::new_from_ident(id.clone()),
81 Some(mod_path) => mod_path.append(id.clone()),
82 }
83 }
84
85 fn search_item(&mut self, item: &Item) -> Result<(), DiscoveryErr> {
86 match item {
87 Item::Fn(fun) => {
88 for stmt in &fun.block.stmts {
89 self.search_stmt(stmt)?
90 }
91 self.search_return_type(&fun.sig.output)?;
92 for i in &fun.sig.inputs {
93 match i {
94 syn::FnArg::Receiver(_) => {}
95 syn::FnArg::Typed(pt) => {
96 self.search_pat(&pt.pat)?;
97 self.search_type(&pt.ty)?;
98 }
99 }
100 }
101 if Self::has_attr(&fun.attrs, EXTERN_RUST_FUN) {
102 self.discoveries.extern_rust_funs.push(RustFun {
103 path: self.deeper_path(&fun.sig.ident),
104 sig: fun.sig.clone(),
Austin Schuh6ea9bfa2023-08-06 19:05:10 -0700105 has_receiver: false,
Brian Silverman4e662aa2022-05-11 23:10:19 -0700106 });
107 }
108 }
109 Item::Impl(imp) => {
110 let receiver = match imp.trait_ {
111 // We do not allow 'extern_rust_fun' on trait impls
112 Some(_) => None,
113 None => match &*imp.self_ty {
114 Type::Path(typ) => {
115 let mut segs = typ.path.segments.iter();
116 let id = segs.next();
117 if let Some(seg) = id {
118 if segs.next().is_some() {
119 None
120 } else {
121 Some(self.deeper_path(&seg.ident))
122 }
123 } else {
124 None
125 }
126 }
127 _ => None,
128 },
129 };
130 for item in &imp.items {
131 self.search_impl_item(item, receiver.as_ref())?
132 }
133 }
134 Item::Mod(md) => {
135 if let Some((_, items)) = &md.content {
136 let mod_path = Some(self.deeper_path(&md.ident));
137 let mut new_mod = PerModDiscoveries {
138 discoveries: self.discoveries,
139 mod_path,
140 };
141 for item in items {
142 new_mod.search_item(item)?
143 }
144 }
145 }
146 Item::Trait(tr) => {
147 for item in &tr.items {
148 self.search_trait_item(item)?
149 }
150 }
151 Item::Struct(ItemStruct { ident, attrs, .. })
152 | Item::Enum(ItemEnum { ident, attrs, .. })
153 if Self::has_attr(attrs, EXTERN_RUST_TYPE) =>
154 {
155 self.discoveries
156 .extern_rust_types
157 .push(self.deeper_path(ident));
158 }
159 _ => {}
160 }
161 Ok(())
162 }
163
164 fn search_path(&mut self, path: &Path) -> Result<(), DiscoveryErr> {
165 let mut seg_iter = path.segments.iter();
166 if let Some(first_seg) = seg_iter.next() {
167 if first_seg.ident == "ffi" {
168 self.discoveries
169 .cpp_list
170 .insert(seg_iter.map(|seg| seg.ident.to_string()).join("::"));
171 }
172 }
173 for seg in path.segments.iter() {
174 self.search_path_arguments(&seg.arguments)?;
175 }
176 Ok(())
177 }
178
179 fn search_trait_item(&mut self, itm: &TraitItem) -> Result<(), DiscoveryErr> {
Austin Schuh6ea9bfa2023-08-06 19:05:10 -0700180 if let TraitItem::Fn(itm) = itm {
Brian Silverman4e662aa2022-05-11 23:10:19 -0700181 if let Some(block) = &itm.default {
182 self.search_stmts(block.stmts.iter())?
183 }
184 }
185 Ok(())
186 }
187
188 fn search_stmts<'a>(
189 &mut self,
190 stmts: impl Iterator<Item = &'a Stmt>,
191 ) -> Result<(), DiscoveryErr> {
192 for stmt in stmts {
193 self.search_stmt(stmt)?
194 }
195 Ok(())
196 }
197
198 fn search_stmt(&mut self, stmt: &Stmt) -> Result<(), DiscoveryErr> {
199 match stmt {
200 Stmt::Local(lcl) => {
Austin Schuh6ea9bfa2023-08-06 19:05:10 -0700201 if let Some(LocalInit { expr, .. }) = &lcl.init {
Brian Silverman4e662aa2022-05-11 23:10:19 -0700202 self.search_expr(expr)?
203 }
204 self.search_pat(&lcl.pat)
205 }
206 Stmt::Item(itm) => self.search_item(itm),
Austin Schuh6ea9bfa2023-08-06 19:05:10 -0700207 Stmt::Expr(exp, _) => self.search_expr(exp),
208 Stmt::Macro(_) => Ok(()),
Brian Silverman4e662aa2022-05-11 23:10:19 -0700209 }
210 }
211
212 fn search_expr(&mut self, expr: &Expr) -> Result<(), DiscoveryErr> {
213 match expr {
214 Expr::Path(exp) => {
215 self.search_path(&exp.path)?;
216 }
Austin Schuh6ea9bfa2023-08-06 19:05:10 -0700217 Expr::Macro(_) | Expr::Infer(_) => {}
Brian Silverman4e662aa2022-05-11 23:10:19 -0700218 Expr::Array(array) => self.search_exprs(array.elems.iter())?,
219 Expr::Assign(ExprAssign { left, right, .. })
Brian Silverman4e662aa2022-05-11 23:10:19 -0700220 | Expr::Binary(ExprBinary { left, right, .. }) => {
221 self.search_expr(left)?;
222 self.search_expr(right)?;
223 }
224 Expr::Async(ass) => self.search_stmts(ass.block.stmts.iter())?,
225 Expr::Await(ExprAwait { base, .. }) | Expr::Field(ExprField { base, .. }) => {
226 self.search_expr(base)?
227 }
Austin Schuh6ea9bfa2023-08-06 19:05:10 -0700228 Expr::Block(ExprBlock { block, .. }) | Expr::Const(ExprConst { block, .. }) => {
229 self.search_stmts(block.stmts.iter())?
230 }
231 Expr::Break(ExprBreak {
Brian Silverman4e662aa2022-05-11 23:10:19 -0700232 expr: Some(expr), ..
233 })
234 | Expr::Cast(ExprCast { expr, .. })
235 | Expr::Group(ExprGroup { expr, .. })
236 | Expr::Paren(ExprParen { expr, .. })
237 | Expr::Reference(ExprReference { expr, .. })
238 | Expr::Try(ExprTry { expr, .. })
Brian Silverman4e662aa2022-05-11 23:10:19 -0700239 | Expr::Unary(ExprUnary { expr, .. }) => self.search_expr(expr)?,
240 Expr::Call(exc) => {
241 self.search_expr(&exc.func)?;
242 self.search_exprs(exc.args.iter())?;
243 }
244 Expr::Closure(cls) => self.search_expr(&cls.body)?,
245 Expr::Continue(_)
246 | Expr::Lit(_)
247 | Expr::Break(ExprBreak { expr: None, .. })
248 | Expr::Verbatim(_) => {}
249 Expr::ForLoop(fl) => {
250 self.search_expr(&fl.expr)?;
251 self.search_stmts(fl.body.stmts.iter())?;
252 }
253 Expr::If(exif) => {
254 self.search_expr(&exif.cond)?;
255 self.search_stmts(exif.then_branch.stmts.iter())?;
256 if let Some((_, else_branch)) = &exif.else_branch {
257 self.search_expr(else_branch)?;
258 }
259 }
260 Expr::Index(exidx) => {
261 self.search_expr(&exidx.expr)?;
262 self.search_expr(&exidx.index)?;
263 }
264 Expr::Let(ExprLet { expr, pat, .. }) => {
265 self.search_expr(expr)?;
266 self.search_pat(pat)?;
267 }
268 Expr::Loop(exloo) => self.search_stmts(exloo.body.stmts.iter())?,
269 Expr::Match(exm) => {
270 self.search_expr(&exm.expr)?;
271 for a in &exm.arms {
272 self.search_expr(&a.body)?;
273 if let Some((_, guard)) = &a.guard {
274 self.search_expr(guard)?;
275 }
276 }
277 }
278 Expr::MethodCall(mtc) => {
279 self.search_expr(&mtc.receiver)?;
280 self.search_exprs(mtc.args.iter())?;
281 }
282 Expr::Range(exr) => {
Austin Schuh6ea9bfa2023-08-06 19:05:10 -0700283 self.search_option_expr(&exr.start)?;
284 self.search_option_expr(&exr.end)?;
Brian Silverman4e662aa2022-05-11 23:10:19 -0700285 }
286 Expr::Repeat(exr) => {
287 self.search_expr(&exr.expr)?;
288 self.search_expr(&exr.len)?;
289 }
290 Expr::Return(exret) => {
291 if let Some(expr) = &exret.expr {
292 self.search_expr(expr)?;
293 }
294 }
295 Expr::Struct(exst) => {
296 for f in &exst.fields {
297 self.search_expr(&f.expr)?;
298 }
299 self.search_option_expr(&exst.rest)?;
300 }
301 Expr::TryBlock(extb) => self.search_stmts(extb.block.stmts.iter())?,
302 Expr::Tuple(ext) => self.search_exprs(ext.elems.iter())?,
303 Expr::Unsafe(exs) => self.search_stmts(exs.block.stmts.iter())?,
304 Expr::While(exw) => {
305 self.search_expr(&exw.cond)?;
306 self.search_stmts(exw.body.stmts.iter())?;
307 }
308 Expr::Yield(exy) => self.search_option_expr(&exy.expr)?,
309 _ => {}
310 }
311 Ok(())
312 }
313
314 fn search_option_expr(&mut self, expr: &Option<Box<Expr>>) -> Result<(), DiscoveryErr> {
315 if let Some(expr) = &expr {
316 self.search_expr(expr)?;
317 }
318 Ok(())
319 }
320
321 fn search_exprs<'a>(
322 &mut self,
323 exprs: impl Iterator<Item = &'a Expr>,
324 ) -> Result<(), DiscoveryErr> {
325 for e in exprs {
326 self.search_expr(e)?;
327 }
328 Ok(())
329 }
330
331 fn search_impl_item(
332 &mut self,
333 impl_item: &ImplItem,
334 receiver: Option<&RustPath>,
335 ) -> Result<(), DiscoveryErr> {
Austin Schuh6ea9bfa2023-08-06 19:05:10 -0700336 if let ImplItem::Fn(itm) = impl_item {
Brian Silverman4e662aa2022-05-11 23:10:19 -0700337 if Self::has_attr(&itm.attrs, EXTERN_RUST_FUN) {
338 if self.mod_path.is_some() {
339 return Err(DiscoveryErr::FoundExternRustFunWithinMod);
340 }
341 if let Some(receiver) = receiver {
342 // We have a method which we want to put into the cxx::bridge's
343 // "extern Rust" block.
344 let sig = add_receiver(&itm.sig, receiver.get_final_ident())?;
345 assert!(receiver.len() == 1);
346 self.discoveries.extern_rust_funs.push(RustFun {
347 path: self.deeper_path(&itm.sig.ident),
348 sig,
Austin Schuh6ea9bfa2023-08-06 19:05:10 -0700349 has_receiver: true,
Brian Silverman4e662aa2022-05-11 23:10:19 -0700350 });
351 self.discoveries.extern_rust_types.push(receiver.clone())
352 } else {
353 return Err(DiscoveryErr::FoundExternRustFunOnTypeWithoutClearReceiver);
354 }
355 }
356 for stmt in &itm.block.stmts {
357 self.search_stmt(stmt)?
358 }
359 }
360 Ok(())
361 }
362
363 fn search_pat(&mut self, pat: &Pat) -> Result<(), DiscoveryErr> {
364 match pat {
Austin Schuh6ea9bfa2023-08-06 19:05:10 -0700365 Pat::Const(const_) => {
366 for stmt in &const_.block.stmts {
367 self.search_stmt(stmt)?
368 }
369 Ok(())
Brian Silverman4e662aa2022-05-11 23:10:19 -0700370 }
Austin Schuh6ea9bfa2023-08-06 19:05:10 -0700371 Pat::Reference(PatReference { pat, .. }) => self.search_pat(pat),
Brian Silverman4e662aa2022-05-11 23:10:19 -0700372 Pat::Ident(_) | Pat::Lit(_) | Pat::Macro(_) | Pat::Range(_) | Pat::Rest(_) => Ok(()),
Austin Schuh6ea9bfa2023-08-06 19:05:10 -0700373 Pat::Paren(paren) => self.search_pat(&paren.pat),
Brian Silverman4e662aa2022-05-11 23:10:19 -0700374 Pat::Or(pator) => {
375 for case in &pator.cases {
376 self.search_pat(case)?;
377 }
378 Ok(())
379 }
380 Pat::Path(pp) => self.search_path(&pp.path),
381 Pat::Slice(PatSlice { elems, .. }) | Pat::Tuple(PatTuple { elems, .. }) => {
382 for case in elems {
383 self.search_pat(case)?;
384 }
385 Ok(())
386 }
387 Pat::Struct(ps) => {
388 self.search_path(&ps.path)?;
389 for f in &ps.fields {
390 self.search_pat(&f.pat)?;
391 }
392 Ok(())
393 }
394 Pat::TupleStruct(tps) => {
395 self.search_path(&tps.path)?;
Austin Schuh6ea9bfa2023-08-06 19:05:10 -0700396 for f in &tps.elems {
Brian Silverman4e662aa2022-05-11 23:10:19 -0700397 self.search_pat(f)?;
398 }
399 Ok(())
400 }
401 Pat::Type(pt) => {
402 self.search_pat(&pt.pat)?;
403 self.search_type(&pt.ty)
404 }
Austin Schuh6ea9bfa2023-08-06 19:05:10 -0700405 Pat::Verbatim(_) | Pat::Wild(_) => Ok(()),
Brian Silverman4e662aa2022-05-11 23:10:19 -0700406 _ => Ok(()),
407 }
408 }
409
410 fn search_type(&mut self, ty: &Type) -> Result<(), DiscoveryErr> {
411 match ty {
412 Type::Array(TypeArray { elem, .. })
413 | Type::Group(TypeGroup { elem, .. })
414 | Type::Paren(TypeParen { elem, .. })
415 | Type::Ptr(TypePtr { elem, .. })
416 | Type::Reference(TypeReference { elem, .. })
417 | Type::Slice(TypeSlice { elem, .. }) => self.search_type(elem)?,
418 Type::BareFn(tf) => {
419 for input in &tf.inputs {
420 self.search_type(&input.ty)?;
421 }
422 self.search_return_type(&tf.output)?;
423 }
424 Type::ImplTrait(tyit) => {
425 for b in &tyit.bounds {
426 if let syn::TypeParamBound::Trait(tyt) = b {
427 self.search_path(&tyt.path)?
428 }
429 }
430 }
431 Type::Infer(_) | Type::Macro(_) | Type::Never(_) => {}
432 Type::Path(typ) => self.search_path(&typ.path)?,
433 Type::TraitObject(tto) => self.search_type_param_bounds(&tto.bounds)?,
434 Type::Tuple(tt) => {
435 for e in &tt.elems {
436 self.search_type(e)?
437 }
438 }
439 _ => {}
440 }
441 Ok(())
442 }
443
444 fn search_type_param_bounds(
445 &mut self,
Austin Schuh6ea9bfa2023-08-06 19:05:10 -0700446 bounds: &Punctuated<TypeParamBound, syn::token::Plus>,
Brian Silverman4e662aa2022-05-11 23:10:19 -0700447 ) -> Result<(), DiscoveryErr> {
448 for b in bounds {
449 if let syn::TypeParamBound::Trait(tpbt) = b {
450 self.search_path(&tpbt.path)?
451 }
452 }
453 Ok(())
454 }
455
456 fn search_return_type(&mut self, output: &ReturnType) -> Result<(), DiscoveryErr> {
457 if let ReturnType::Type(_, ty) = &output {
458 self.search_type(ty)
459 } else {
460 Ok(())
461 }
462 }
463
464 fn search_path_arguments(
465 &mut self,
466 arguments: &syn::PathArguments,
467 ) -> Result<(), DiscoveryErr> {
468 match arguments {
469 syn::PathArguments::None => {}
470 syn::PathArguments::AngleBracketed(paab) => {
471 for arg in &paab.args {
472 match arg {
473 syn::GenericArgument::Lifetime(_) => {}
474 syn::GenericArgument::Type(ty)
Austin Schuh6ea9bfa2023-08-06 19:05:10 -0700475 | syn::GenericArgument::AssocType(AssocType { ty, .. }) => {
Brian Silverman4e662aa2022-05-11 23:10:19 -0700476 self.search_type(ty)?
477 }
478 syn::GenericArgument::Constraint(c) => {
479 self.search_type_param_bounds(&c.bounds)?
480 }
Austin Schuh6ea9bfa2023-08-06 19:05:10 -0700481 syn::GenericArgument::Const(value)
482 | syn::GenericArgument::AssocConst(AssocConst { value, .. }) => {
483 self.search_expr(value)?
484 }
485 _ => {}
Brian Silverman4e662aa2022-05-11 23:10:19 -0700486 }
487 }
488 }
489 syn::PathArguments::Parenthesized(pas) => {
490 self.search_return_type(&pas.output)?;
491 for t in &pas.inputs {
492 self.search_type(t)?;
493 }
494 }
495 }
496 Ok(())
497 }
498
499 fn has_attr(attrs: &[Attribute], attr_name: &str) -> bool {
500 attrs.iter().any(|attr| {
Austin Schuh6ea9bfa2023-08-06 19:05:10 -0700501 attr.path()
Brian Silverman4e662aa2022-05-11 23:10:19 -0700502 .segments
503 .last()
504 .map(|seg| seg.ident == attr_name)
505 .unwrap_or_default()
506 })
507 }
508}
509
Austin Schuh6ea9bfa2023-08-06 19:05:10 -0700510struct SelfSubstituter<'a> {
511 self_ty: &'a Ident,
512}
513
514impl<'a> SelfSubstituter<'a> {
515 pub fn new(self_ty: &'a Ident) -> Self {
516 Self { self_ty }
517 }
518}
519
520impl<'a> VisitMut for SelfSubstituter<'a> {
521 fn visit_type_path_mut(&mut self, i: &mut syn::TypePath) {
522 if i.qself.is_none() && i.path.is_ident("Self") {
523 i.path = Path::from(self.self_ty.clone());
524 }
525 }
526}
527
Brian Silverman4e662aa2022-05-11 23:10:19 -0700528/// Take a method signature that may be `fn a(&self)`
529/// and turn it into `fn a(self: &A)` which is what we will
530/// need to specify to cxx.
531fn add_receiver(sig: &Signature, receiver: &Ident) -> Result<Signature, DiscoveryErr> {
532 let mut sig = sig.clone();
533 match sig.inputs.iter_mut().next() {
534 Some(first_arg) => match first_arg {
Austin Schuh6ea9bfa2023-08-06 19:05:10 -0700535 syn::FnArg::Receiver(rec_arg) => {
536 let mut substituted_type = rec_arg.ty.clone();
537 visit_type_mut(&mut SelfSubstituter::new(receiver), &mut substituted_type);
Brian Silverman4e662aa2022-05-11 23:10:19 -0700538 *first_arg = parse_quote! {
Austin Schuh6ea9bfa2023-08-06 19:05:10 -0700539 qelf: #substituted_type
540 };
541 if let syn::FnArg::Typed(ref mut pat_type) = *first_arg {
542 if let syn::Pat::Ident(ref mut pat_ident) = *pat_type.pat {
543 assert_eq!(pat_ident.ident.to_string(), "qelf");
544 pat_ident.ident = Ident::new("self", pat_ident.ident.span());
545 }
Brian Silverman4e662aa2022-05-11 23:10:19 -0700546 }
547 }
Brian Silverman4e662aa2022-05-11 23:10:19 -0700548 syn::FnArg::Typed(_) => {}
549 },
550 None => return Err(DiscoveryErr::NoParameterOnMethod),
551 }
552 Ok(sig)
553}
554
555#[cfg(test)]
556mod tests {
557 use quote::{quote, ToTokens};
Austin Schuh6ea9bfa2023-08-06 19:05:10 -0700558 use syn::{parse_quote, ImplItemFn};
Brian Silverman4e662aa2022-05-11 23:10:19 -0700559
560 use crate::{ast_discoverer::add_receiver, types::make_ident};
561
562 use super::Discoveries;
563
564 fn assert_cpp_found(discoveries: &Discoveries) {
565 assert!(!discoveries.cpp_list.is_empty());
566 assert!(discoveries.cpp_list.iter().next().unwrap() == "xxx");
567 }
568
569 #[test]
570 fn test_mod_plain_call() {
571 let mut discoveries = Discoveries::default();
572 let itm = parse_quote! {
573 mod foo {
574 fn bar() {
575 ffi::xxx()
576 }
577 }
578 };
579 discoveries.search_item(&itm, None).unwrap();
580 assert_cpp_found(&discoveries);
581 }
582
583 #[test]
584 fn test_plain_call() {
585 let mut discoveries = Discoveries::default();
586 let itm = parse_quote! {
587 fn bar() {
588 ffi::xxx()
589 }
590 };
591 discoveries.search_item(&itm, None).unwrap();
592 assert_cpp_found(&discoveries);
593 }
594
595 #[test]
596 fn test_plain_call_with_semi() {
597 let mut discoveries = Discoveries::default();
598 let itm = parse_quote! {
599 fn bar() {
600 ffi::xxx();
601 }
602 };
603 discoveries.search_item(&itm, None).unwrap();
604 assert_cpp_found(&discoveries);
605 }
606
607 #[test]
608 fn test_in_ns() {
609 let mut discoveries = Discoveries::default();
610 let itm = parse_quote! {
611 fn bar() {
612 ffi::a::b::xxx();
613 }
614 };
615 discoveries.search_item(&itm, None).unwrap();
616 assert!(!discoveries.cpp_list.is_empty());
617 assert!(discoveries.cpp_list.iter().next().unwrap() == "a::b::xxx");
618 }
619
620 #[test]
621 fn test_deep_nested_thingy() {
622 let mut discoveries = Discoveries::default();
623 let itm = parse_quote! {
624 fn bar() {
625 a + 3 * foo(ffi::xxx());
626 }
627 };
628 discoveries.search_item(&itm, None).unwrap();
629 assert_cpp_found(&discoveries);
630 }
631
632 #[test]
633 fn test_ty_in_let() {
634 let mut discoveries = Discoveries::default();
635 let itm = parse_quote! {
636 fn bar() {
637 let foo: ffi::xxx = bar();
638 }
639 };
640 discoveries.search_item(&itm, None).unwrap();
641 assert_cpp_found(&discoveries);
642 }
643
644 #[test]
645 fn test_ty_in_fn() {
646 let mut discoveries = Discoveries::default();
647 let itm = parse_quote! {
648 fn bar(a: &mut ffi::xxx) {
649 }
650 };
651 discoveries.search_item(&itm, None).unwrap();
652 assert_cpp_found(&discoveries);
653 }
654
655 #[test]
656 fn test_ty_in_fn_up() {
657 let mut discoveries = Discoveries::default();
658 let itm = parse_quote! {
659 fn bar(a: cxx::UniquePtr<ffi::xxx>) {
660 }
661 };
662 discoveries.search_item(&itm, None).unwrap();
663 assert_cpp_found(&discoveries);
664 }
665
666 #[test]
667 fn test_extern_rust_fun() {
668 let mut discoveries = Discoveries::default();
669 let itm = parse_quote! {
670 #[autocxx::extern_rust::extern_rust_function]
671 fn bar(a: cxx::UniquePtr<ffi::xxx>) {
672 }
673 };
674 discoveries.search_item(&itm, None).unwrap();
675 assert!(discoveries.extern_rust_funs.get(0).unwrap().sig.ident == "bar");
676 }
677
678 #[test]
679 fn test_extern_rust_method() {
680 let mut discoveries = Discoveries::default();
681 let itm = parse_quote! {
682 impl A {
683 #[autocxx::extern_rust::extern_rust_function]
684 fn bar(&self) {
685 }
686 }
687 };
688 discoveries.search_item(&itm, None).unwrap();
689 assert!(discoveries.extern_rust_funs.get(0).unwrap().sig.ident == "bar");
690 }
691
692 #[test]
693 fn test_extern_rust_ty() {
694 let mut discoveries = Discoveries::default();
695 let itm = parse_quote! {
696 #[autocxx::extern_rust::extern_rust_type]
697 struct Bar {
698
699 }
700 };
701 discoveries.search_item(&itm, None).unwrap();
702 assert!(
703 discoveries
704 .extern_rust_types
705 .get(0)
706 .unwrap()
707 .get_final_ident()
708 == "Bar"
709 );
710 }
711
712 #[test]
713 fn test_add_receiver() {
Austin Schuh6ea9bfa2023-08-06 19:05:10 -0700714 let meth: ImplItemFn = parse_quote! {
Brian Silverman4e662aa2022-05-11 23:10:19 -0700715 fn a(&self) {}
716 };
717 let a = make_ident("A");
718 assert_eq!(
719 add_receiver(&meth.sig, &a)
720 .unwrap()
721 .to_token_stream()
722 .to_string(),
723 quote! { fn a(self: &A) }.to_string()
724 );
725
Austin Schuh6ea9bfa2023-08-06 19:05:10 -0700726 let meth: ImplItemFn = parse_quote! {
Brian Silverman4e662aa2022-05-11 23:10:19 -0700727 fn a(&mut self, b: u32) -> Foo {}
728 };
729 assert_eq!(
730 add_receiver(&meth.sig, &a)
731 .unwrap()
732 .to_token_stream()
733 .to_string(),
734 quote! { fn a(self: &mut A, b: u32) -> Foo }.to_string()
735 );
736
Austin Schuh6ea9bfa2023-08-06 19:05:10 -0700737 let meth: ImplItemFn = parse_quote! {
Brian Silverman4e662aa2022-05-11 23:10:19 -0700738 fn a() {}
739 };
740 assert!(add_receiver(&meth.sig, &a).is_err());
741 }
742}