cxx_build/syntax/
check.rs

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