1 use crate::syntax::atom::Atom::{self, *};
2 use crate::syntax::report::Errors;
3 use crate::syntax::visit::{self, Visit};
4 use crate::syntax::{
5     error, ident, trivial, Api, Array, Enum, ExternFn, ExternType, Impl, Lang, Lifetimes,
6     NamedType, Ptr, Receiver, Ref, Signature, SliceRef, Struct, Trait, Ty1, Type, TypeAlias, Types,
7 };
8 use proc_macro2::{Delimiter, Group, Ident, TokenStream};
9 use quote::{quote, ToTokens};
10 use std::fmt::Display;
11 use syn::{GenericParam, Generics, Lifetime};
12 
13 pub(crate) struct Check<'a> {
14     apis: &'a [Api],
15     types: &'a Types<'a>,
16     errors: &'a mut Errors,
17     generator: Generator,
18 }
19 
20 pub(crate) enum Generator {
21     // cxx-build crate, cxxbridge cli, cxx-gen.
22     #[allow(dead_code)]
23     Build,
24     // cxxbridge-macro. This is relevant in that the macro output is going to
25     // get fed straight to rustc, so for errors that rustc already contains
26     // logic to catch (probably with a better diagnostic than what the proc
27     // macro API is able to produce), we avoid duplicating them in our own
28     // diagnostics.
29     #[allow(dead_code)]
30     Macro,
31 }
32 
typecheck(cx: &mut Errors, apis: &[Api], types: &Types, generator: Generator)33 pub(crate) fn typecheck(cx: &mut Errors, apis: &[Api], types: &Types, generator: Generator) {
34     do_typecheck(&mut Check {
35         apis,
36         types,
37         errors: cx,
38         generator,
39     });
40 }
41 
do_typecheck(cx: &mut Check)42 fn do_typecheck(cx: &mut Check) {
43     ident::check_all(cx, cx.apis);
44 
45     for ty in cx.types {
46         match ty {
47             Type::Ident(ident) => check_type_ident(cx, ident),
48             Type::RustBox(ptr) => check_type_box(cx, ptr),
49             Type::RustVec(ty) => check_type_rust_vec(cx, ty),
50             Type::UniquePtr(ptr) => check_type_unique_ptr(cx, ptr),
51             Type::SharedPtr(ptr) => check_type_shared_ptr(cx, ptr),
52             Type::WeakPtr(ptr) => check_type_weak_ptr(cx, ptr),
53             Type::CxxVector(ptr) => check_type_cxx_vector(cx, ptr),
54             Type::Ref(ty) => check_type_ref(cx, ty),
55             Type::Ptr(ty) => check_type_ptr(cx, ty),
56             Type::Array(array) => check_type_array(cx, array),
57             Type::Fn(ty) => check_type_fn(cx, ty),
58             Type::SliceRef(ty) => check_type_slice_ref(cx, ty),
59             Type::Str(_) | Type::Void(_) => {}
60         }
61     }
62 
63     for api in cx.apis {
64         match api {
65             Api::Include(_) => {}
66             Api::Struct(strct) => check_api_struct(cx, strct),
67             Api::Enum(enm) => check_api_enum(cx, enm),
68             Api::CxxType(ety) | Api::RustType(ety) => check_api_type(cx, ety),
69             Api::CxxFunction(efn) | Api::RustFunction(efn) => check_api_fn(cx, efn),
70             Api::TypeAlias(alias) => check_api_type_alias(cx, alias),
71             Api::Impl(imp) => check_api_impl(cx, imp),
72         }
73     }
74 }
75 
76 impl Check<'_> {
error(&mut self, sp: impl ToTokens, msg: impl Display)77     pub(crate) fn error(&mut self, sp: impl ToTokens, msg: impl Display) {
78         self.errors.error(sp, msg);
79     }
80 }
81 
check_type_ident(cx: &mut Check, name: &NamedType)82 fn check_type_ident(cx: &mut Check, name: &NamedType) {
83     let ident = &name.rust;
84     if Atom::from(ident).is_none()
85         && !cx.types.structs.contains_key(ident)
86         && !cx.types.enums.contains_key(ident)
87         && !cx.types.cxx.contains(ident)
88         && !cx.types.rust.contains(ident)
89     {
90         let msg = format!("unsupported type: {}", ident);
91         cx.error(ident, msg);
92     }
93 }
94 
check_type_box(cx: &mut Check, ptr: &Ty1)95 fn check_type_box(cx: &mut Check, ptr: &Ty1) {
96     if let Type::Ident(ident) = &ptr.inner {
97         if cx.types.cxx.contains(&ident.rust)
98             && !cx.types.aliases.contains_key(&ident.rust)
99             && !cx.types.structs.contains_key(&ident.rust)
100             && !cx.types.enums.contains_key(&ident.rust)
101         {
102             cx.error(ptr, error::BOX_CXX_TYPE.msg);
103         }
104 
105         if Atom::from(&ident.rust).is_none() {
106             return;
107         }
108     }
109 
110     cx.error(ptr, "unsupported target type of Box");
111 }
112 
check_type_rust_vec(cx: &mut Check, ty: &Ty1)113 fn check_type_rust_vec(cx: &mut Check, ty: &Ty1) {
114     match &ty.inner {
115         Type::Ident(ident) => {
116             if cx.types.cxx.contains(&ident.rust)
117                 && !cx.types.aliases.contains_key(&ident.rust)
118                 && !cx.types.structs.contains_key(&ident.rust)
119                 && !cx.types.enums.contains_key(&ident.rust)
120             {
121                 cx.error(ty, "Rust Vec containing C++ type is not supported yet");
122                 return;
123             }
124 
125             match Atom::from(&ident.rust) {
126                 None
127                 | Some(
128                     Bool | Char | U8 | U16 | U32 | U64 | Usize | I8 | I16 | I32 | I64 | Isize | F32
129                     | F64 | RustString,
130                 ) => return,
131                 Some(CxxString) => {}
132             }
133         }
134         Type::Str(_) => return,
135         _ => {}
136     }
137 
138     cx.error(ty, "unsupported element type of Vec");
139 }
140 
check_type_unique_ptr(cx: &mut Check, ptr: &Ty1)141 fn check_type_unique_ptr(cx: &mut Check, ptr: &Ty1) {
142     if let Type::Ident(ident) = &ptr.inner {
143         if cx.types.rust.contains(&ident.rust) {
144             cx.error(ptr, "unique_ptr of a Rust type is not supported yet");
145             return;
146         }
147 
148         match Atom::from(&ident.rust) {
149             None | Some(CxxString) => return,
150             _ => {}
151         }
152     } else if let Type::CxxVector(_) = &ptr.inner {
153         return;
154     }
155 
156     cx.error(ptr, "unsupported unique_ptr target type");
157 }
158 
check_type_shared_ptr(cx: &mut Check, ptr: &Ty1)159 fn check_type_shared_ptr(cx: &mut Check, ptr: &Ty1) {
160     if let Type::Ident(ident) = &ptr.inner {
161         if cx.types.rust.contains(&ident.rust) {
162             cx.error(ptr, "shared_ptr of a Rust type is not supported yet");
163             return;
164         }
165 
166         match Atom::from(&ident.rust) {
167             None
168             | Some(
169                 Bool | U8 | U16 | U32 | U64 | Usize | I8 | I16 | I32 | I64 | Isize | F32 | F64
170                 | CxxString,
171             ) => return,
172             Some(Char | RustString) => {}
173         }
174     } else if let Type::CxxVector(_) = &ptr.inner {
175         cx.error(ptr, "std::shared_ptr<std::vector> is not supported yet");
176         return;
177     }
178 
179     cx.error(ptr, "unsupported shared_ptr target type");
180 }
181 
check_type_weak_ptr(cx: &mut Check, ptr: &Ty1)182 fn check_type_weak_ptr(cx: &mut Check, ptr: &Ty1) {
183     if let Type::Ident(ident) = &ptr.inner {
184         if cx.types.rust.contains(&ident.rust) {
185             cx.error(ptr, "weak_ptr of a Rust type is not supported yet");
186             return;
187         }
188 
189         match Atom::from(&ident.rust) {
190             None
191             | Some(
192                 Bool | U8 | U16 | U32 | U64 | Usize | I8 | I16 | I32 | I64 | Isize | F32 | F64
193                 | CxxString,
194             ) => return,
195             Some(Char | RustString) => {}
196         }
197     } else if let Type::CxxVector(_) = &ptr.inner {
198         cx.error(ptr, "std::weak_ptr<std::vector> is not supported yet");
199         return;
200     }
201 
202     cx.error(ptr, "unsupported weak_ptr target type");
203 }
204 
check_type_cxx_vector(cx: &mut Check, ptr: &Ty1)205 fn check_type_cxx_vector(cx: &mut Check, ptr: &Ty1) {
206     if let Type::Ident(ident) = &ptr.inner {
207         if cx.types.rust.contains(&ident.rust) {
208             cx.error(
209                 ptr,
210                 "C++ vector containing a Rust type is not supported yet",
211             );
212             return;
213         }
214 
215         match Atom::from(&ident.rust) {
216             None
217             | Some(
218                 U8 | U16 | U32 | U64 | Usize | I8 | I16 | I32 | I64 | Isize | F32 | F64 | CxxString,
219             ) => return,
220             Some(Char) => { /* todo */ }
221             Some(Bool | RustString) => {}
222         }
223     }
224 
225     cx.error(ptr, "unsupported vector element type");
226 }
227 
check_type_ref(cx: &mut Check, ty: &Ref)228 fn check_type_ref(cx: &mut Check, ty: &Ref) {
229     if ty.mutable && !ty.pinned {
230         if let Some(requires_pin) = match &ty.inner {
231             Type::Ident(ident) if ident.rust == CxxString || is_opaque_cxx(cx, &ident.rust) => {
232                 Some(ident.rust.to_string())
233             }
234             Type::CxxVector(_) => Some("CxxVector<...>".to_owned()),
235             _ => None,
236         } {
237             cx.error(
238                 ty,
239                 format!(
240                     "mutable reference to C++ type requires a pin -- use Pin<&mut {}>",
241                     requires_pin,
242                 ),
243             );
244         }
245     }
246 
247     match ty.inner {
248         Type::Fn(_) | Type::Void(_) => {}
249         Type::Ref(_) => {
250             cx.error(ty, "C++ does not allow references to references");
251             return;
252         }
253         _ => return,
254     }
255 
256     cx.error(ty, "unsupported reference type");
257 }
258 
check_type_ptr(cx: &mut Check, ty: &Ptr)259 fn check_type_ptr(cx: &mut Check, ty: &Ptr) {
260     match ty.inner {
261         Type::Fn(_) | Type::Void(_) => {}
262         Type::Ref(_) => {
263             cx.error(ty, "C++ does not allow pointer to reference as a type");
264             return;
265         }
266         _ => return,
267     }
268 
269     cx.error(ty, "unsupported pointer type");
270 }
271 
check_type_slice_ref(cx: &mut Check, ty: &SliceRef)272 fn check_type_slice_ref(cx: &mut Check, ty: &SliceRef) {
273     let supported = !is_unsized(cx, &ty.inner)
274         || match &ty.inner {
275             Type::Ident(ident) => {
276                 cx.types.rust.contains(&ident.rust) || cx.types.aliases.contains_key(&ident.rust)
277             }
278             _ => false,
279         };
280 
281     if !supported {
282         let mutable = if ty.mutable { "mut " } else { "" };
283         let mut msg = format!("unsupported &{}[T] element type", mutable);
284         if let Type::Ident(ident) = &ty.inner {
285             if is_opaque_cxx(cx, &ident.rust) {
286                 msg += ": opaque C++ type is not supported yet";
287             }
288         }
289         cx.error(ty, msg);
290     }
291 }
292 
check_type_array(cx: &mut Check, ty: &Array)293 fn check_type_array(cx: &mut Check, ty: &Array) {
294     let supported = !is_unsized(cx, &ty.inner);
295 
296     if !supported {
297         cx.error(ty, "unsupported array element type");
298     }
299 }
300 
check_type_fn(cx: &mut Check, ty: &Signature)301 fn check_type_fn(cx: &mut Check, ty: &Signature) {
302     if ty.throws {
303         cx.error(ty, "function pointer returning Result is not supported yet");
304     }
305 
306     for arg in &ty.args {
307         if let Type::Ptr(_) = arg.ty {
308             if ty.unsafety.is_none() {
309                 cx.error(
310                     arg,
311                     "pointer argument requires that the function pointer be marked unsafe",
312                 );
313             }
314         }
315     }
316 }
317 
check_api_struct(cx: &mut Check, strct: &Struct)318 fn check_api_struct(cx: &mut Check, strct: &Struct) {
319     let name = &strct.name;
320     check_reserved_name(cx, &name.rust);
321     check_lifetimes(cx, &strct.generics);
322 
323     if strct.fields.is_empty() {
324         let span = span_for_struct_error(strct);
325         cx.error(span, "structs without any fields are not supported");
326     }
327 
328     if cx.types.cxx.contains(&name.rust) {
329         if let Some(ety) = cx.types.untrusted.get(&name.rust) {
330             let msg = "extern shared struct must be declared in an `unsafe extern` block";
331             cx.error(ety, msg);
332         }
333     }
334 
335     for derive in &strct.derives {
336         if derive.what == Trait::ExternType {
337             let msg = format!("derive({}) on shared struct is not supported", derive);
338             cx.error(derive, msg);
339         }
340     }
341 
342     for field in &strct.fields {
343         if let Type::Fn(_) = field.ty {
344             cx.error(
345                 field,
346                 "function pointers in a struct field are not implemented yet",
347             );
348         } else if is_unsized(cx, &field.ty) {
349             let desc = describe(cx, &field.ty);
350             let msg = format!("using {} by value is not supported", desc);
351             cx.error(field, msg);
352         }
353     }
354 }
355 
check_api_enum(cx: &mut Check, enm: &Enum)356 fn check_api_enum(cx: &mut Check, enm: &Enum) {
357     check_reserved_name(cx, &enm.name.rust);
358     check_lifetimes(cx, &enm.generics);
359 
360     if enm.variants.is_empty() && !enm.explicit_repr && !enm.variants_from_header {
361         let span = span_for_enum_error(enm);
362         cx.error(
363             span,
364             "explicit #[repr(...)] is required for enum without any variants",
365         );
366     }
367 
368     for derive in &enm.derives {
369         if derive.what == Trait::Default || derive.what == Trait::ExternType {
370             let msg = format!("derive({}) on shared enum is not supported", derive);
371             cx.error(derive, msg);
372         }
373     }
374 }
375 
check_api_type(cx: &mut Check, ety: &ExternType)376 fn check_api_type(cx: &mut Check, ety: &ExternType) {
377     check_reserved_name(cx, &ety.name.rust);
378     check_lifetimes(cx, &ety.generics);
379 
380     for derive in &ety.derives {
381         if derive.what == Trait::ExternType && ety.lang == Lang::Rust {
382             continue;
383         }
384         let lang = match ety.lang {
385             Lang::Rust => "Rust",
386             Lang::Cxx => "C++",
387         };
388         let msg = format!(
389             "derive({}) on opaque {} type is not supported yet",
390             derive, lang,
391         );
392         cx.error(derive, msg);
393     }
394 
395     if !ety.bounds.is_empty() {
396         let bounds = &ety.bounds;
397         let span = quote!(#(#bounds)*);
398         cx.error(span, "extern type bounds are not implemented yet");
399     }
400 
401     if let Some(reasons) = cx.types.required_trivial.get(&ety.name.rust) {
402         let msg = format!(
403             "needs a cxx::ExternType impl in order to be used as {}",
404             trivial::as_what(&ety.name, reasons),
405         );
406         cx.error(ety, msg);
407     }
408 }
409 
check_api_fn(cx: &mut Check, efn: &ExternFn)410 fn check_api_fn(cx: &mut Check, efn: &ExternFn) {
411     match efn.lang {
412         Lang::Cxx => {
413             if !efn.generics.params.is_empty() && !efn.trusted {
414                 let ref span = span_for_generics_error(efn);
415                 cx.error(span, "extern C++ function with lifetimes must be declared in `unsafe extern \"C++\"` block");
416             }
417         }
418         Lang::Rust => {
419             if !efn.generics.params.is_empty() && efn.unsafety.is_none() {
420                 let ref span = span_for_generics_error(efn);
421                 let message = format!(
422                     "must be `unsafe fn {}` in order to expose explicit lifetimes to C++",
423                     efn.name.rust,
424                 );
425                 cx.error(span, message);
426             }
427         }
428     }
429 
430     check_generics(cx, &efn.sig.generics);
431 
432     if let Some(receiver) = &efn.receiver {
433         let ref span = span_for_receiver_error(receiver);
434 
435         if receiver.ty.rust == "Self" {
436             let mutability = match receiver.mutable {
437                 true => "mut ",
438                 false => "",
439             };
440             let msg = format!(
441                 "unnamed receiver type is only allowed if the surrounding extern block contains exactly one extern type; use `self: &{mutability}TheType`",
442                 mutability = mutability,
443             );
444             cx.error(span, msg);
445         } else if cx.types.enums.contains_key(&receiver.ty.rust) {
446             cx.error(
447                 span,
448                 "unsupported receiver type; C++ does not allow member functions on enums",
449             );
450         } else if !cx.types.structs.contains_key(&receiver.ty.rust)
451             && !cx.types.cxx.contains(&receiver.ty.rust)
452             && !cx.types.rust.contains(&receiver.ty.rust)
453         {
454             cx.error(span, "unrecognized receiver type");
455         } else if receiver.mutable && !receiver.pinned && is_opaque_cxx(cx, &receiver.ty.rust) {
456             cx.error(
457                 span,
458                 format!(
459                     "mutable reference to opaque C++ type requires a pin -- use `self: Pin<&mut {}>`",
460                     receiver.ty.rust,
461                 ),
462             );
463         }
464     }
465 
466     for arg in &efn.args {
467         if let Type::Fn(_) = arg.ty {
468             if efn.lang == Lang::Rust {
469                 cx.error(
470                     arg,
471                     "passing a function pointer from C++ to Rust is not implemented yet",
472                 );
473             }
474         } else if let Type::Ptr(_) = arg.ty {
475             if efn.sig.unsafety.is_none() {
476                 cx.error(
477                     arg,
478                     "pointer argument requires that the function be marked unsafe",
479                 );
480             }
481         } else if is_unsized(cx, &arg.ty) {
482             let desc = describe(cx, &arg.ty);
483             let msg = format!("passing {} by value is not supported", desc);
484             cx.error(arg, msg);
485         }
486     }
487 
488     if let Some(ty) = &efn.ret {
489         if let Type::Fn(_) = ty {
490             cx.error(ty, "returning a function pointer is not implemented yet");
491         } else if is_unsized(cx, ty) {
492             let desc = describe(cx, ty);
493             let msg = format!("returning {} by value is not supported", desc);
494             cx.error(ty, msg);
495         }
496     }
497 
498     if efn.lang == Lang::Cxx {
499         check_mut_return_restriction(cx, efn);
500     }
501 }
502 
check_api_type_alias(cx: &mut Check, alias: &TypeAlias)503 fn check_api_type_alias(cx: &mut Check, alias: &TypeAlias) {
504     check_lifetimes(cx, &alias.generics);
505 
506     for derive in &alias.derives {
507         let msg = format!("derive({}) on extern type alias is not supported", derive);
508         cx.error(derive, msg);
509     }
510 }
511 
check_api_impl(cx: &mut Check, imp: &Impl)512 fn check_api_impl(cx: &mut Check, imp: &Impl) {
513     let ty = &imp.ty;
514 
515     check_lifetimes(cx, &imp.impl_generics);
516 
517     if let Some(negative) = imp.negative_token {
518         let span = quote!(#negative #ty);
519         cx.error(span, "negative impl is not supported yet");
520         return;
521     }
522 
523     match ty {
524         Type::RustBox(ty)
525         | Type::RustVec(ty)
526         | Type::UniquePtr(ty)
527         | Type::SharedPtr(ty)
528         | Type::WeakPtr(ty)
529         | Type::CxxVector(ty) => {
530             if let Type::Ident(inner) = &ty.inner {
531                 if Atom::from(&inner.rust).is_none() {
532                     return;
533                 }
534             }
535         }
536         _ => {}
537     }
538 
539     cx.error(imp, "unsupported Self type of explicit impl");
540 }
541 
check_mut_return_restriction(cx: &mut Check, efn: &ExternFn)542 fn check_mut_return_restriction(cx: &mut Check, efn: &ExternFn) {
543     if efn.sig.unsafety.is_some() {
544         // Unrestricted as long as the function is made unsafe-to-call.
545         return;
546     }
547 
548     match &efn.ret {
549         Some(Type::Ref(ty)) if ty.mutable => {}
550         Some(Type::SliceRef(slice)) if slice.mutable => {}
551         _ => return,
552     }
553 
554     if let Some(receiver) = &efn.receiver {
555         if receiver.mutable {
556             return;
557         }
558         let resolve = match cx.types.try_resolve(&receiver.ty) {
559             Some(resolve) => resolve,
560             None => return,
561         };
562         if !resolve.generics.lifetimes.is_empty() {
563             return;
564         }
565     }
566 
567     struct FindLifetimeMut<'a> {
568         cx: &'a Check<'a>,
569         found: bool,
570     }
571 
572     impl<'t, 'a> Visit<'t> for FindLifetimeMut<'a> {
573         fn visit_type(&mut self, ty: &'t Type) {
574             self.found |= match ty {
575                 Type::Ref(ty) => ty.mutable,
576                 Type::SliceRef(slice) => slice.mutable,
577                 Type::Ident(ident) if Atom::from(&ident.rust).is_none() => {
578                     match self.cx.types.try_resolve(ident) {
579                         Some(resolve) => !resolve.generics.lifetimes.is_empty(),
580                         None => true,
581                     }
582                 }
583                 _ => false,
584             };
585             visit::visit_type(self, ty);
586         }
587     }
588 
589     let mut visitor = FindLifetimeMut { cx, found: false };
590 
591     for arg in &efn.args {
592         visitor.visit_type(&arg.ty);
593     }
594 
595     if visitor.found {
596         return;
597     }
598 
599     cx.error(
600         efn,
601         "&mut return type is not allowed unless there is a &mut argument",
602     );
603 }
604 
check_reserved_name(cx: &mut Check, ident: &Ident)605 fn check_reserved_name(cx: &mut Check, ident: &Ident) {
606     if ident == "Box"
607         || ident == "UniquePtr"
608         || ident == "SharedPtr"
609         || ident == "WeakPtr"
610         || ident == "Vec"
611         || ident == "CxxVector"
612         || ident == "str"
613         || Atom::from(ident).is_some()
614     {
615         cx.error(ident, "reserved name");
616     }
617 }
618 
check_reserved_lifetime(cx: &mut Check, lifetime: &Lifetime)619 fn check_reserved_lifetime(cx: &mut Check, lifetime: &Lifetime) {
620     if lifetime.ident == "static" {
621         match cx.generator {
622             Generator::Macro => { /* rustc already reports this */ }
623             Generator::Build => {
624                 cx.error(lifetime, error::RESERVED_LIFETIME);
625             }
626         }
627     }
628 }
629 
check_lifetimes(cx: &mut Check, generics: &Lifetimes)630 fn check_lifetimes(cx: &mut Check, generics: &Lifetimes) {
631     for lifetime in &generics.lifetimes {
632         check_reserved_lifetime(cx, lifetime);
633     }
634 }
635 
check_generics(cx: &mut Check, generics: &Generics)636 fn check_generics(cx: &mut Check, generics: &Generics) {
637     for generic_param in &generics.params {
638         if let GenericParam::Lifetime(def) = generic_param {
639             check_reserved_lifetime(cx, &def.lifetime);
640         }
641     }
642 }
643 
is_unsized(cx: &mut Check, ty: &Type) -> bool644 fn is_unsized(cx: &mut Check, ty: &Type) -> bool {
645     match ty {
646         Type::Ident(ident) => {
647             let ident = &ident.rust;
648             ident == CxxString || is_opaque_cxx(cx, ident) || cx.types.rust.contains(ident)
649         }
650         Type::Array(array) => is_unsized(cx, &array.inner),
651         Type::CxxVector(_) | Type::Fn(_) | Type::Void(_) => true,
652         Type::RustBox(_)
653         | Type::RustVec(_)
654         | Type::UniquePtr(_)
655         | Type::SharedPtr(_)
656         | Type::WeakPtr(_)
657         | Type::Ref(_)
658         | Type::Ptr(_)
659         | Type::Str(_)
660         | Type::SliceRef(_) => false,
661     }
662 }
663 
is_opaque_cxx(cx: &mut Check, ty: &Ident) -> bool664 fn is_opaque_cxx(cx: &mut Check, ty: &Ident) -> bool {
665     cx.types.cxx.contains(ty)
666         && !cx.types.structs.contains_key(ty)
667         && !cx.types.enums.contains_key(ty)
668         && !(cx.types.aliases.contains_key(ty) && cx.types.required_trivial.contains_key(ty))
669 }
670 
span_for_struct_error(strct: &Struct) -> TokenStream671 fn span_for_struct_error(strct: &Struct) -> TokenStream {
672     let struct_token = strct.struct_token;
673     let mut brace_token = Group::new(Delimiter::Brace, TokenStream::new());
674     brace_token.set_span(strct.brace_token.span.join());
675     quote!(#struct_token #brace_token)
676 }
677 
span_for_enum_error(enm: &Enum) -> TokenStream678 fn span_for_enum_error(enm: &Enum) -> TokenStream {
679     let enum_token = enm.enum_token;
680     let mut brace_token = Group::new(Delimiter::Brace, TokenStream::new());
681     brace_token.set_span(enm.brace_token.span.join());
682     quote!(#enum_token #brace_token)
683 }
684 
span_for_receiver_error(receiver: &Receiver) -> TokenStream685 fn span_for_receiver_error(receiver: &Receiver) -> TokenStream {
686     let ampersand = receiver.ampersand;
687     let lifetime = &receiver.lifetime;
688     let mutability = receiver.mutability;
689     if receiver.shorthand {
690         let var = receiver.var;
691         quote!(#ampersand #lifetime #mutability #var)
692     } else {
693         let ty = &receiver.ty;
694         quote!(#ampersand #lifetime #mutability #ty)
695     }
696 }
697 
span_for_generics_error(efn: &ExternFn) -> TokenStream698 fn span_for_generics_error(efn: &ExternFn) -> TokenStream {
699     let unsafety = efn.unsafety;
700     let fn_token = efn.fn_token;
701     let generics = &efn.generics;
702     quote!(#unsafety #fn_token #generics)
703 }
704 
describe(cx: &mut Check, ty: &Type) -> String705 fn describe(cx: &mut Check, ty: &Type) -> String {
706     match ty {
707         Type::Ident(ident) => {
708             if cx.types.structs.contains_key(&ident.rust) {
709                 "struct".to_owned()
710             } else if cx.types.enums.contains_key(&ident.rust) {
711                 "enum".to_owned()
712             } else if cx.types.aliases.contains_key(&ident.rust) {
713                 "C++ type".to_owned()
714             } else if cx.types.cxx.contains(&ident.rust) {
715                 "opaque C++ type".to_owned()
716             } else if cx.types.rust.contains(&ident.rust) {
717                 "opaque Rust type".to_owned()
718             } else if Atom::from(&ident.rust) == Some(CxxString) {
719                 "C++ string".to_owned()
720             } else if Atom::from(&ident.rust) == Some(Char) {
721                 "C char".to_owned()
722             } else {
723                 ident.rust.to_string()
724             }
725         }
726         Type::RustBox(_) => "Box".to_owned(),
727         Type::RustVec(_) => "Vec".to_owned(),
728         Type::UniquePtr(_) => "unique_ptr".to_owned(),
729         Type::SharedPtr(_) => "shared_ptr".to_owned(),
730         Type::WeakPtr(_) => "weak_ptr".to_owned(),
731         Type::Ref(_) => "reference".to_owned(),
732         Type::Ptr(_) => "raw pointer".to_owned(),
733         Type::Str(_) => "&str".to_owned(),
734         Type::CxxVector(_) => "C++ vector".to_owned(),
735         Type::SliceRef(_) => "slice".to_owned(),
736         Type::Fn(_) => "function pointer".to_owned(),
737         Type::Void(_) => "()".to_owned(),
738         Type::Array(_) => "array".to_owned(),
739     }
740 }
741