Eine aufbereitete Darstellung der Quelle

 
     
 
 
Anforderungen  |   Konzepte  |   Entwurf  |   Entwicklung  |   Qualitätssicherung  |   Lebenszyklus  |   Steuerung
 
 
 
 

Benutzer

Quelle  attrs.rs

  Sprache: Rust
 

//! #[diplomat::attr] and other attributes

use crate::ast;
use crate::ast::attrs::{AttrInheritContext, DiplomatBackendAttrCfg, StandardAttribute};
use crate::hir::lowering::ErrorStore;
use crate::hir::{
    EnumVariant, LoweringError, Method, Mutability, OpaqueId, ReturnType, SelfType, SuccessType,
    TraitDef, Type, TypeDef, TypeId,
};
use syn::Meta;

pub use crate::ast::attrs::RenameAttr;

/// Diplomat attribute that can be specified on items, methods, and enum variants. These
/// can be used to control the codegen in a particular backend.
///
/// Most of these are specified via `#[diplomat::attr(some cfg here, attrname)]`, where `some cfg here`
/// can be used to pick which backends something applies to.
#[non_exhaustive]
#[derive(Clone, Default, Debug)]
pub struct Attrs {
    /// "disable" this item: do not generate code for it in the backend
    ///
    /// This attribute is always inherited except to variants
    pub disable: bool,
    /// Mark this item deprecated in FFI.
    pub deprecated: Option<String>,
    /// An optional namespace. None is equivalent to the root namespace.
    ///
    /// This attribute is inherited to types (and is not allowed elsewhere)
    pub namespace: Option<String>,
    /// Rename this item/method/variant
    ///
    /// This attribute is inherited except through methods and variants (and is not allowed on variants)
    pub rename: RenameAttr,
    /// Rename this item in the C ABI. This *must* be respected by backends.
    ///
    /// This attribute is inherited except through variants
    pub abi_rename: RenameAttr,
    /// This method is "special": it should generate something other than a regular method on the other side.
    /// This can be something like a constructor, an accessor, a stringifier etc.
    ///
    /// This attribute does not participate in inheritance and must always
    /// be specified on individual methods
    pub special_method: Option<SpecialMethod>,
    /// This user-defined type can be used as the error type in a Result.
    pub custom_errors: bool,

    /// This user-defined type has a "default" state that can be computed purely foreign-language-side
    ///
    /// Can be applied to enum variants to signal the default
    pub default: bool,

    /// From #[diplomat::demo()]. Created from [`crate::ast::attrs::Attrs::demo_attrs`].
    /// List of attributes specific to automatic demo generation.
    /// Currently just for demo_gen in diplomat-tool (which generates sample webpages), but could be used for broader purposes (i.e., demo Android apps)
    pub demo_attrs: DemoInfo,
    /// From #[diplomat::attr()]. If true, generates a mocking interface for this type.
    pub generate_mocking_interface: bool,
    /// From #[diplomat::attr()]. If true, Diplomat will check that this struct has the same memory layout in backends which support it. Allows this struct to be used in slices ([`super::Slice::Struct`]) and to be borrowed in function parameters.
    pub abi_compatible: bool,
}

// #region: Demo specific attributes.

/// For `#[diplomat::demo(input(...))]`, stored in [DemoInfo::input_cfg].
#[non_exhaustive]
#[derive(Clone, Default, Debug)]
pub struct DemoInputCFG {
    /// `#[diplomat::demo(input(label = "..."))]`
    /// Label that this input parameter should have. Let demo_gen pick a valid name if this is empty.
    ///
    /// For instance <label for="v">Number Here</label><input name="v"/>
    pub label: String,

    /// `#[diplomat::demo(input(default_value = "..."))]`
    /// Sets the default value for a parameter.
    ///
    /// Should ALWAYS be a string. The HTML renderer is expected to do validation for us.
    pub default_value: String,
}

#[non_exhaustive]
#[derive(Clone, Default, Debug)]
pub struct DemoInfo {
    /// `#[diplomat::demo(generate)]`. If automatic generation is disabled by default (see [`diplomat_tool::demo_gen::DemoConfig`]), then the below render terminus will be allowed to generate.
    pub generate: bool,

    /// `#[diplomat::demo(default_constructor)]`
    /// We search for any methods specially tagged with `Constructor`, but if there's are no default Constructors and there's NamedConstructor that you want to be default instead, use this.
    /// TODO: Should probably ignore other `Constructors` if a default has been set.
    pub default_constructor: bool,

    /// `#[diplomat::demo(external)]` represents an item that we will not evaluate, and should be passed to the rendering engine to provide.
    pub external: bool,

    /// `#[diplomat::demo(custom_func = "/file/name/here.mjs")]` can be used above any `struct` definition in the bridge. The linked `.mjs` should contain a JS definition of functions that should be bundled with demo_gen's output.
    ///
    /// We call these functions "custom functions", as they are JS functions that are not automagically generated by demo_gen, but rather included as part of its JS output in the `RenderInfo` object.
    ///
    /// For more information on custom functions (and their use), see the relevant chapter in [the book](https://rust-diplomat.github.io/diplomat/demo_gen/custom_functions.html).
    ///
    /// Files are located relative to lib.rs.
    ///
    pub custom_func: Option<String>,

    /// `#[diplomat::demo(input(...))]` represents configuration options for anywhere we might expect user input.
    pub input_cfg: DemoInputCFG,
}

// #endregion

/// Attributes that mark methods as "special"
#[non_exhaustive]
#[derive(Clone, Debug)]
pub enum SpecialMethod {
    /// A constructor.
    ///
    /// Must return Self (or Result<Self> for backends with `fallible_constructors` enabled )
    Constructor,
    /// A named constructor, with optional name. If the name isn't specified, it will be derived
    /// from the method name
    ///
    /// Must return Self (or Result<Self> for backends with `fallible_constructors` enabled )
    NamedConstructor(Option<String>),

    /// A getter, with optional name. If the name isn't specified, it will be derived
    /// from the method name
    ///
    /// Must have no parameters and must return something.
    Getter(Option<String>),
    /// A setter, with optional name. If the name isn't specified, it will be derived
    /// from the method name
    ///
    /// Must have no return type (aside from potentially a `Result<(), _>`) and must have one parameter
    Setter(Option<String>),
    /// A stringifier. Must have no parameters and return a string (DiplomatWrite)
    Stringifier,
    /// A comparison operator. Currently not universally supported
    Comparison,
    /// An iterator (a type that is mutated to produce new values)
    Iterator,
    /// An iterable (a type that can produce an iterator)
    Iterable,
    /// Indexes into the type using an integer
    Indexer,

    /// Arithmetic operators. May not return references
    Add,
    Sub,
    Mul,
    Div,

    /// In-place arithmetic operators. Must not return a value
    AddAssign,
    SubAssign,
    MulAssign,
    DivAssign,
}

impl SpecialMethod {
    pub fn from_path_and_meta(path: &str, meta: &Meta) -> Result<Option<Self>, LoweringError> {
        let parse_meta = |meta| match StandardAttribute::from_meta(meta) {
            Ok(StandardAttribute::String(s)) => Ok(Some(s)),
            Ok(StandardAttribute::Empty) => Ok(None),
            Ok(_) | Err(_) => Err(LoweringError::Other(format!(
                "`{path}` must have a single string parameter or no parameter"
            ))),
        };

        match path {
            "constructor" => Ok(Some(Self::Constructor)),
            "named_constructor" => Ok(Some(Self::NamedConstructor(parse_meta(meta)?))),
            "getter" => Ok(Some(Self::Getter(parse_meta(meta)?))),
            "setter" => Ok(Some(Self::Setter(parse_meta(meta)?))),
            "stringifier" => Ok(Some(Self::Stringifier)),
            "comparison" => Ok(Some(Self::Comparison)),
            "iterator" => Ok(Some(Self::Iterator)),
            "iterable" => Ok(Some(Self::Iterable)),
            "indexer" => Ok(Some(Self::Indexer)),
            "add" => Ok(Some(Self::Add)),
            "sub" => Ok(Some(Self::Sub)),
            "mul" => Ok(Some(Self::Mul)),
            "div" => Ok(Some(Self::Div)),
            "add_assign" => Ok(Some(Self::AddAssign)),
            "sub_assign" => Ok(Some(Self::SubAssign)),
            "mul_assign" => Ok(Some(Self::MulAssign)),
            "div_assign" => Ok(Some(Self::DivAssign)),
            _ => Ok(None),
        }
    }
    // Returns the standard operator string (if any) associated with this special method
    pub fn operator_str(&self) -> Option<&str> {
        match self {
            SpecialMethod::Add => Some("+"),
            SpecialMethod::Sub => Some("-"),
            SpecialMethod::Mul => Some("*"),
            SpecialMethod::Div => Some("/"),
            SpecialMethod::AddAssign => Some("+="),
            SpecialMethod::SubAssign => Some("-="),
            SpecialMethod::MulAssign => Some("*="),
            SpecialMethod::DivAssign => Some("/="),
            SpecialMethod::Indexer => Some("[]"),
            _ => None,
        }
    }
}

/// For special methods that affect type semantics, whether this type has this method.
///
/// This will likely only contain a subset of special methods, but feel free to add more as needed.
#[derive(Debug, Default)]
#[non_exhaustive]
pub struct SpecialMethodPresence {
    pub comparator: bool,
    /// If it is an iterator, the type it iterates over
    pub iterator: Option<SuccessType>,
    /// If it is an iterable, the iterator type it returns (*not* the type it iterates over,
    /// perform lookup on that type to access)
    pub iterable: Option<OpaqueId>,
}

/// Where the attribute was found. Some attributes are only allowed in some contexts
/// (e.g. namespaces cannot be specified on methods)
#[non_exhaustive] // might add module attrs in the future
#[derive(Debug)]
pub enum AttributeContext<'a, 'b> {
    Type(TypeDef<'a>),
    Trait(&'a TraitDef),
    EnumVariant(&'a EnumVariant),
    Method(&'a Method, Option<TypeId>, &'mut SpecialMethodPresence),
    Function(&'a Method),
    Module,
    Param,
    SelfParam,
    Field,
}

fn maybe_error_unsupported(
    auto_found: bool,
    attribute: &str,
    backend: &str,
    errors: &mut ErrorStore,
) {
    if !auto_found {
        errors.push(LoweringError::Other(format!(
            "`{attribute}` not supported in backend {backend}"
        )));
    }
}
impl Attrs {
    pub fn from_ast(
        ast: &ast::Attrs,
        validator: &(impl AttributeValidator + ?Sized),
        parent_attrs: &Attrs,
        errors: &mut ErrorStore,
    ) -> Self {
        let mut this = parent_attrs.clone();
        // Backends must support this since it applies to the macro/C code.
        // No special inheritance, was already appropriately inherited in AST
        this.abi_rename = ast.abi_rename.clone();

        this.deprecated = ast.deprecated.clone();

        let support = validator.attrs_supported();
        let backend = validator.primary_name();
        for attr in &ast.attrs {
            let mut auto_found = false;
            match validator.satisfies_cfg(&attr.cfg, Some(&mut auto_found)) {
                Ok(satisfies) if !satisfies => continue,
                Err(e) => {
                    errors.push(e);
                    continue;
                }
                Ok(_) => {}
            };

            let path = attr.meta.path();
            if let Some(path) = path.get_ident() {
                let path = path.to_string();

                let warn_auto = |errors: &mut ErrorStore| {
                    if auto_found {
                        errors.push(LoweringError::Other(format!(
                            "Diplomat attribute {path:?} gated on 'auto' but is not one that works with 'auto'"
                        )));
                    }
                };

                // Check against the set of attributes that can have platform support
                if support.check_string(&path) == Some(false) {
                    maybe_error_unsupported(auto_found, &path, backend, errors);
                    continue;
                }

                match SpecialMethod::from_path_and_meta(&path, &attr.meta) {
                    Ok(Some(kind)) => {
                        if let Some(ref existing) = this.special_method {
                            errors.push(LoweringError::Other(format!(
                                    "Multiple special method markers found on the same method, found {path} and {existing:?}"
                                )));
                        } else {
                            this.special_method = Some(kind);
                        }
                    }
                    Err(error) => errors.push(error),
                    Ok(None) => match path.as_str() {
                        // No match found in the special methods, check the other keywords
                        "disable" => {
                            if let Meta::Path(_) = attr.meta {
                                if this.disable {
                                    errors.push(LoweringError::Other(
                                        "Duplicate `disable` attribute".into(),
                                    ));
                                } else {
                                    this.disable = true;
                                }
                            } else {
                                errors.push(LoweringError::Other(
                                    "`disable` must be a simple path".into(),
                                ))
                            }
                            warn_auto(errors);
                        }
                        "default" => {
                            if let Meta::Path(_) = attr.meta {
                                if this.default {
                                    errors.push(LoweringError::Other(
                                        "Duplicate `default` attribute".into(),
                                    ));
                                } else {
                                    this.default = true;
                                }
                            } else {
                                errors.push(LoweringError::Other(
                                    "`default` must be a simple path".into(),
                                ))
                            }
                        }
                        "rename" => {
                            match RenameAttr::from_meta(&attr.meta) {
                                Ok(rename) => {
                                    // We use the override extend mode: a single ast::Attrs
                                    // will have had these attributes inherited into the list by appending
                                    // to the end; so a later attribute in the list is more pertinent.
                                    this.rename.extend(&rename);
                                }
                                Err(e) => errors.push(LoweringError::Other(format!(
                                    "`rename` attr failed to parse: {e:?}"
                                ))),
                            }
                            warn_auto(errors);
                        }
                        "namespace" => match StandardAttribute::from_meta(&attr.meta) {
                            Ok(StandardAttribute::String(s)) if s.is_empty() => {
                                this.namespace = None
                            }
                            Ok(StandardAttribute::String(s)) => this.namespace = Some(s),
                            Ok(_) | Err(_) => {
                                errors.push(LoweringError::Other(
                                    "`namespace` must have a single string parameter".to_string(),
                                ));
                            }
                        },
                        "error" => {
                            this.custom_errors = true;
                        }
                        "generate_mocking_interface" => {
                            if !support.generate_mocking_interface {
                                maybe_error_unsupported(
                                    auto_found,
                                    "generate_mocking_interface",
                                    backend,
                                    errors,
                                );
                                continue;
                            }
                            this.generate_mocking_interface = true;
                        }
                        "abi_compatible" => {
                            if !support.abi_compatibles {
                                maybe_error_unsupported(
                                    auto_found,
                                    "abi_compatible",
                                    backend,
                                    errors,
                                );
                                continue;
                            }
                            this.abi_compatible = true;
                        }
                        _ => {
                            errors.push(LoweringError::Other(format!(
                                "Unknown diplomat attribute {path}: expected one of: `disable, rename, namespace, constructor, stringifier, comparison, named_constructor, getter, setter, indexer, error`"
                            )));
                        }
                    },
                }
            }
        }

        for attr in &ast.demo_attrs {
            let path = attr.meta.path();
            if let Some(path_ident) = path.get_ident() {
                if path_ident == "external" {
                    this.demo_attrs.external = true;
                } else if path_ident == "default_constructor" {
                    this.demo_attrs.default_constructor = true;
                } else if path_ident == "generate" {
                    this.demo_attrs.generate = true;
                } else if path_ident == "input" {
                    let meta_list = attr
                        .meta
                        .require_list()
                        .expect("Could not get MetaList, expected #[diplomat::demo(input(...))]");

                    meta_list
                        .parse_nested_meta(|meta| {
                            if meta.path.is_ident("label") {
                                let value = meta.value()?;
                                let s: syn::LitStr = value.parse()?;
                                this.demo_attrs.input_cfg.label = s.value();
                                Ok(())
                            } else if meta.path.is_ident("default_value") {
                                let value = meta.value()?;

                                let str_val: String;

                                let ahead = value.lookahead1();
                                if ahead.peek(syn::LitFloat) {
                                    let s: syn::LitFloat = value.parse()?;
                                    str_val = s.base10_parse::<f64>()?.to_string();
                                } else if ahead.peek(syn::LitInt) {
                                    let s: syn::LitInt = value.parse()?;
                                    str_val = s.base10_parse::<i64>()?.to_string();
                                } else {
                                    let s: syn::LitStr = value.parse()?;
                                    str_val = s.value();
                                }
                                this.demo_attrs.input_cfg.default_value = str_val;
                                Ok(())
                            } else {
                                Err(meta.error(format!(
                                    "Unsupported ident {:?}",
                                    meta.path.get_ident()
                                )))
                            }
                        })
                        .expect("Could not read input(...)");
                } else if path_ident == "custom_func" {
                    let v = &attr.meta.require_name_value().unwrap().value;

                    if let syn::Expr::Lit(s) = v {
                        if let syn::Lit::Str(string) = &s.lit {
                            this.demo_attrs.custom_func = Some(string.value());
                        } else {
                            errors.push(LoweringError::Other(format!(
                                "#[diplomat::demo(custom_func={s:?}) must be a literal string."
                            )));
                        }
                    } else {
                        errors.push(LoweringError::Other(format!(
                            "#[diplomat::demo(custom_func={v:?}) must be a literal string."
                        )));
                    }
                } else {
                    errors.push(LoweringError::Other(format!(
                        "Unknown demo_attr: {path_ident:?}"
                    )));
                }
            } else {
                errors.push(LoweringError::Other(format!("Unknown demo_attr: {path:?}")));
            }
        }

        this
    }

    /// Validate that this attribute is allowed in this context
    pub(cratefn validate(
        &self,
        validator: &(impl AttributeValidator + ?Sized),
        mut context: AttributeContext,
        errors: &mut ErrorStore,
    ) {
        // use an exhaustive destructure so new attributes are handled
        let Attrs {
            disable,
            deprecated: _deprecated,
            namespace,
            rename,
            abi_rename,
            special_method,
            custom_errors,
            default,
            demo_attrs: _,
            generate_mocking_interface,
            abi_compatible,
        } = &self;

        if *disable && matches!(context, AttributeContext::EnumVariant(..)) {
            errors.push(LoweringError::Other(
                "`disable` cannot be used on enum variants".into(),
            ))
        }

        if let Some(ref special) = special_method {
            if let AttributeContext::Method(method, self_id, ref mut special_method_presence) =
                context
            {
                let check_param_count = |name: &str, count: usize, errors: &mut ErrorStore| {
                    if method.params.len() != count {
                        errors.push(LoweringError::Other(format!(
                            "{name} must have exactly {count} parameter{}",
                            if count == 1 { "" } else { "s" }
                        )))
                    }
                };
                let check_self_param = |name: &str, need_self: bool, errors: &mut ErrorStore| {
                    if method.param_self.is_some() != need_self {
                        errors.push(LoweringError::Other(format!(
                            "{name} must{} accept a self parameter",
                            if need_self { "" } else { " not" }
                        )));
                    }
                };
                match special {
                    SpecialMethod::Constructor | SpecialMethod::NamedConstructor(..) => {
                        check_self_param("Constructors"false, errors);
                        let output = method.output.success_type();
                        match method.output {
                            ReturnType::Infallible(_) => (),
                            ReturnType::Fallible(..) => {
                                // Only report an error if constructors *are* supported but failable constructors *arent*
                                if validator.attrs_supported().constructors
                                    && !validator.attrs_supported().fallible_constructors
                                {
                                    errors.push(LoweringError::Other(
                                        "This backend doesn't support fallible constructors"
                                            .to_string(),
                                    ))
                                }
                            }
                            ReturnType::Nullable(..) => {
                                errors.push(LoweringError::Other("Diplomat doesn't support turning nullable methods into constructors".to_string()));
                            }
                        }

                        if let SuccessType::OutType(t) = &output {
                            if t.id() != self_id || self_id.is_none() {
                                errors.push(LoweringError::Other(
                                    "Constructors must return Self!".to_string(),
                                ));
                            }
                        } else {
                            errors.push(LoweringError::Other(
                                "Constructors must return Self!".to_string(),
                            ));
                        }
                    }
                    SpecialMethod::Getter(_) => {
                        if !method.params.is_empty() {
                            errors
                                .push(LoweringError::Other("Getter cannot have parameters".into()));
                        }
                        if method.param_self.is_none()
                            && !validator.attrs_supported().static_accessors
                        {
                            errors.push(LoweringError::Other(format!("No self parameter on Getter {} but static_acessors are not supported",method.name.as_str())));
                        }

                        // Currently does not forbid nullable getters, could if desired
                    }

                    SpecialMethod::Setter(_) => {
                        if !matches!(method.output.success_type(), SuccessType::Unit) {
                            errors.push(LoweringError::Other("Setters must return unit".into()));
                        }
                        if method.param_self.is_none()
                            && !validator.attrs_supported().static_accessors
                        {
                            errors.push(LoweringError::Other(format!("No self parameter on Setter {} but static_acessors are not supported",method.name.as_str())));
                        }
                        check_param_count("Setter"1, errors);
                        // Currently does not forbid fallible setters, could if desired
                    }
                    SpecialMethod::Stringifier => {
                        if !method.params.is_empty() {
                            errors
                                .push(LoweringError::Other("Getter cannot have parameters".into()));
                        }
                        if !matches!(method.output.success_type(), SuccessType::Write) {
                            errors.push(LoweringError::Other(
                                "Stringifier must return string".into(),
                            ));
                        }
                    }
                    SpecialMethod::Comparison => {
                        check_param_count("Comparator"1, errors);
                        if special_method_presence.comparator {
                            errors.push(LoweringError::Other(
                                "Cannot define two comparators on the same type".into(),
                            ));
                        }
                        special_method_presence.comparator = true;
                        // In the long run we can actually support heterogeneous comparators. Not a priority right now.
                        const COMPARATOR_ERROR: &str =
                            "Comparator's parameter must be identical to self";
                        check_self_param("Comparators"true, errors);

                        if let Some(ref selfty) = method.param_self {
                            if let Some(param) = method.params.first() {
                                match (&selfty.ty, ¶m.ty) {
                                    (SelfType::Opaque(p), Type::Opaque(p2)) => {
                                        if p.tcx_id != p2.tcx_id {
                                            errors.push(LoweringError::Other(
                                                COMPARATOR_ERROR.into(),
                                            ));
                                        }

                                        if p.owner.mutability != Mutability::Immutable
                                            || p2.owner.mutability != Mutability::Immutable
                                        {
                                            errors.push(LoweringError::Other(
                                                "comparators must accept immutable parameters"
                                                    .into(),
                                            ));
                                        }

                                        if p2.optional.0 {
                                            errors.push(LoweringError::Other(
                                                "comparators must accept non-optional parameters"
                                                    .into(),
                                            ));
                                        }
                                    }
                                    (SelfType::Struct(p), Type::Struct(p2)) => {
                                        if p.tcx_id != p2.tcx_id {
                                            errors.push(LoweringError::Other(
                                                COMPARATOR_ERROR.into(),
                                            ));
                                        }

                                        if p.owner
                                            .as_borrowed()
                                            .map(|o| !o.mutability.is_immutable())
                                            .unwrap_or(false)
                                            || p2
                                                .owner
                                                .as_borrowed()
                                                .map(|o| !o.mutability.is_immutable())
                                                .unwrap_or(false)
                                        {
                                            errors.push(LoweringError::Other(
                                                "comparators must accept immutable parameters"
                                                    .into(),
                                            ));
                                        }
                                    }
                                    (SelfType::Enum(p), Type::Enum(p2)) => {
                                        if p.tcx_id != p2.tcx_id {
                                            errors.push(LoweringError::Other(
                                                COMPARATOR_ERROR.into(),
                                            ));
                                        }
                                    }
                                    _ => {
                                        errors.push(LoweringError::Other(COMPARATOR_ERROR.into()));
                                    }
                                }
                            }
                        }
                    }
                    SpecialMethod::Iterator => {
                        if special_method_presence.iterator.is_some() {
                            errors.push(LoweringError::Other(
                                "Cannot mark type as iterator twice".into(),
                            ));
                        }
                        check_param_count("Iterator"0, errors);
                        // In theory we could support struct and enum iterators. The benefit is slight:
                        // it generates probably inefficient code whilst being rather weird when it comes to the
                        // "structs and enums convert across the boundary" norm for backends.
                        //
                        // Essentially, the `&mut self` behavior won't work right.
                        //
                        // Furthermore, in some backends (like Dart) defining an iterator may requiring adding fields,
                        // which may not be possible for enums, and would still be an odd-one-out field for structs.g s
                        check_self_param("Iterator"true, errors);
                        if let Some(this) = &method.param_self {
                            if !matches!(this.ty, SelfType::Opaque(..)) {
                                errors.push(LoweringError::Other(
                                    "Iterators only allowed on opaques".into(),
                                ))
                            }
                        }

                        if let ReturnType::Nullable(ref o) = method.output {
                            if let SuccessType::Unit = o {
                                errors.push(LoweringError::Other(
                                    "Iterator method must return something".into(),
                                ));
                            }
                            special_method_presence.iterator = Some(o.clone());
                        } else if let ReturnType::Infallible(SuccessType::OutType(
                            crate::hir::OutType::Opaque(
                                ref o @ crate::hir::OpaquePath {
                                    optional: crate::hir::Optional(true),
                                    ..
                                },
                            ),
                        )) = method.output
                        {
                            let mut o = o.clone();
                            o.optional = crate::hir::Optional(false);

                            special_method_presence.iterator =
                                Some(SuccessType::OutType(crate::hir::OutType::Opaque(o)));
                        } else {
                            errors.push(LoweringError::Other(
                                "Iterator method must return nullable value".into(),
                            ));
                        }
                    }
                    SpecialMethod::Iterable => {
                        if special_method_presence.iterable.is_some() {
                            errors.push(LoweringError::Other(
                                "Cannot mark type as iterable twice".into(),
                            ));
                        }
                        check_param_count("Iterator"0, errors);
                        check_self_param("Iterables"true, errors);

                        match method.output.success_type() {
                            SuccessType::OutType(ty) => {
                                if let Some(TypeId::Opaque(id)) = ty.id() {
                                    special_method_presence.iterable = Some(id);
                                } else {
                                    errors.push(LoweringError::Other(
                                        "Iterables must return a custom opaque type".into(),
                                    ))
                                }
                            }
                            _ => errors.push(LoweringError::Other(
                                "Iterables must return a custom type".into(),
                            )),
                        }
                    }
                    SpecialMethod::Indexer => {
                        check_param_count("Indexer"1, errors);
                        check_self_param("Indexer"true, errors);

                        if method.output.success_type().is_unit() {
                            errors.push(LoweringError::Other("Indexer must return a value".into()));
                        }
                    }
                    e @ (SpecialMethod::Add
                    | SpecialMethod::Sub
                    | SpecialMethod::Mul
                    | SpecialMethod::Div) => {
                        let name = match e {
                            SpecialMethod::Add => "Add",
                            SpecialMethod::Sub => "Sub",
                            SpecialMethod::Mul => "Mul",
                            SpecialMethod::Div => "Div",
                            _ => unreachable!(),
                        };

                        check_param_count(name, 1, errors);
                        check_self_param(name, true, errors);

                        if method.output.success_type().is_unit() {
                            errors
                                .push(LoweringError::Other(format!("{name} must return a value")));
                        }
                    }
                    e @ (SpecialMethod::AddAssign
                    | SpecialMethod::SubAssign
                    | SpecialMethod::MulAssign
                    | SpecialMethod::DivAssign) => {
                        let name = match e {
                            SpecialMethod::AddAssign => "AddAssign",
                            SpecialMethod::SubAssign => "SubAssign",
                            SpecialMethod::MulAssign => "MulAssign",
                            SpecialMethod::DivAssign => "DivAssign",
                            _ => unreachable!(),
                        };
                        check_param_count(name, 1, errors);
                        check_self_param(name, true, errors);
                        if let Some(self_param) = &method.param_self {
                            if matches!(self_param.ty, SelfType::Struct(_) | SelfType::Enum(_)) {
                                errors.push(LoweringError::Other("*Assign arithmetic operations not allowed on non-opaque types. \
                                     Use the non-mutating arithmetic operators instead".to_string()));
                            } else if self_param.ty.is_immutably_borrowed() {
                                errors.push(LoweringError::Other(format!(
                                    "{name} must take self by mutable reference"
                                )));
                            }
                        }
                        if !method.output.success_type().is_unit() {
                            errors.push(LoweringError::Other(format!(
                                "{name} must not return a value"
                            )));
                        }
                    }
                }
            } else {
                errors.push(LoweringError::Other(format!("Special method (type {special:?}) not allowed on non-method context {context:?}")))
            }
        }

        if namespace.is_some()
            && matches!(
                context,
                AttributeContext::Method(..) | AttributeContext::EnumVariant(..)
            )
        {
            errors.push(LoweringError::Other(
                "`namespace` can only be used on types".to_string(),
            ));
        }
        if *default && !matches!(context, AttributeContext::EnumVariant(..)) {
            errors.push(LoweringError::Other(
                "`default` can only be used on types and enum variants".to_string(),
            ));
        }
        if matches!(
            context,
            AttributeContext::Param | AttributeContext::SelfParam | AttributeContext::Field
        ) {
            if *disable {
                errors.push(LoweringError::Other(format!(
                    "`disable`s cannot be used on an {context:?}."
                )));
            }

            if namespace.is_some() {
                errors.push(LoweringError::Other(format!(
                    "`namespace` cannot be used on an {context:?}."
                )));
            }

            if !rename.is_empty() || !abi_rename.is_empty() {
                errors.push(LoweringError::Other(format!(
                    "`rename`s cannot be used on an {context:?}."
                )));
            }

            if special_method.is_some() {
                errors.push(LoweringError::Other(format!(
                    "{context:?} cannot be special methods."
                )));
            }
        }

        if *custom_errors
            && !matches!(
                context,
                AttributeContext::Type(..)
                    | AttributeContext::Trait(..)
                    | AttributeContext::Function(..)
            )
        {
            errors.push(LoweringError::Other(
                "`error` can only be used on types".to_string(),
            ));
        }
        if *generate_mocking_interface
            && !matches!(context, AttributeContext::Type(TypeDef::Opaque(..)))
        {
            errors.push(LoweringError::Other(
                "`generate_mocking_interface` can only be used on opaque types".to_string(),
            ));
        }

        if *abi_compatible && !matches!(context, AttributeContext::Type(TypeDef::Struct(..))) {
            errors.push(LoweringError::Other(
                "`abi_compatible` can only be used on non-output-only struct types.".into(),
            ));
        }
    }

    pub(cratefn for_inheritance(&self, context: AttrInheritContext) -> Attrs {
        let rename = self.rename.attrs_for_inheritance(context, false);

        // Disabling shouldn't inherit to variants
        let disable = if context == AttrInheritContext::Variant {
            false
        } else {
            self.disable
        };
        let namespace = if matches!(
            context,
            AttrInheritContext::Module | AttrInheritContext::Type
        ) {
            self.namespace.clone()
        } else {
            None
        };

        Attrs {
            disable,
            deprecated: None,
            rename,
            namespace,
            // Should not inherit from enums to their variants
            default: false,
            // Was already inherited on the AST side
            abi_rename: Default::default(),
            // Never inherited
            special_method: None,
            // Not inherited
            custom_errors: false,
            demo_attrs: Default::default(),
            // Not inherited
            generate_mocking_interface: false,
            abi_compatible: false,
        }
    }
}

/// Non-exhaustive list of what attributes and other features your backend is able to handle, based on #[diplomat::attr(...)] contents.
/// Set this through an [`AttributeValidator`].
///
/// See [`SpecialMethod`] and [`Attrs`] for your specific implementation needs.
///
/// For example, the current dart backend supports [`BackendAttrSupport::constructors`]. So when it encounters:
/// ```ignore
/// struct Sample {}
/// impl Sample {
///     #[diplomat::attr(constructor)]
///     pub fn new() -> Box<Self> {
///         Box::new(Sample{})
///     }
/// }
///
/// ```
///
/// It generates
/// ```dart
/// factory Sample()
/// ```
///
/// If a backend does not support a specific `#[diplomat::attr(...)]`, it may error.
#[non_exhaustive]
#[derive(Copy, Clone, Debug, Default)]
pub struct BackendAttrSupport {
    /// Namespacing types, e.g. C++ `namespace`.
    pub namespacing: bool,
    /// Rust can directly acccess the memory of this language, like C and C++.
    /// This is not supported in any garbage-collected language.
    pub memory_sharing: bool,
    /// This language's structs are non-exhaustive by default, i.e. adding
    /// fields is not a breaking change.
    pub non_exhaustive_structs: bool,
    /// Whether the language supports method overloading
    pub method_overloading: bool,
    /// Whether the language uses UTF-8 strings
    pub utf8_strings: bool,
    /// Whether the language uses UTF-16 strings
    pub utf16_strings: bool,
    /// Whether the language supports using slices with 'static lifetimes.
    pub static_slices: bool,

    /// Whether the language supports marking types as having a default value
    pub defaults: bool,

    // Special methods
    /// Marking a method as a constructor to generate special constructor methods.
    pub constructors: bool,
    /// Marking a method as a named constructor to generate special named constructor methods.
    pub named_constructors: bool,
    /// Marking constructors as being able to return errors. This is possible in languages where
    /// errors are thrown as exceptions (Dart), but not for example in C++, where errors are
    /// returned as values (constructors usually have to return the type itself).
    pub fallible_constructors: bool,
    /// Marking methods as field getters and setters, see [`SpecialMethod::Getter`] and [`SpecialMethod::Setter`]
    pub accessors: bool,
    /// Marking *static* methods as field getters and setters, see [`SpecialMethod::Getter`] and [`SpecialMethod::Setter`]
    pub static_accessors: bool,
    /// Marking a method as the `to_string` method, which is special in this language.
    pub stringifiers: bool,
    /// Marking a method as the `compare_to` method, which is special in this language.
    pub comparators: bool,
    /// Marking a method as the `next` method, which is special in this language.
    pub iterators: bool,
    /// Marking a method as the `iterator` method, which is special in this language.
    pub iterables: bool,
    /// Marking a method as the `[]` operator, which is special in this language.
    pub indexing: bool,
    /// Marking a method as an arithmetic operator (+-*/[=])
    pub arithmetic: bool,
    /// Support for Option<Struct> and Option<Primitive>
    pub option: bool,
    /// Allowing callback arguments
    pub callbacks: bool,
    /// Allowing traits
    pub traits: bool,
    /// Marking a user-defined type as being a valid error result type.
    pub custom_errors: bool,
    /// Traits are safe to Send between threads (safe to mark as std::marker::Send)
    pub traits_are_send: bool,
    /// Traits are safe to Sync between threads (safe to mark as std::marker::Sync)
    pub traits_are_sync: bool,
    /// Whether to generate mocking interface.
    pub generate_mocking_interface: bool,
    /// Passing of structs that only hold (non-slice) primitive types
    /// (for use in slices and languages that support taking direct pointers to structs):
    pub abi_compatibles: bool,
    /// Whether or not the language supports &Struct or &mut Struct
    pub struct_refs: bool,
    /// Whether the language supports generating functions not associated with any type.
    pub free_functions: bool,
}

impl BackendAttrSupport {
    #[cfg(test)]
    fn all_true() -> Self {
        Self {
            namespacing: true,
            memory_sharing: true,
            non_exhaustive_structs: true,
            method_overloading: true,
            utf8_strings: true,
            utf16_strings: true,
            static_slices: true,
            defaults: true,

            constructors: true,
            named_constructors: true,
            fallible_constructors: true,
            static_accessors: true,
            accessors: true,
            stringifiers: true,
            comparators: true,
            iterators: true,
            iterables: true,
            indexing: true,
            arithmetic: true,
            option: true,
            callbacks: true,
            traits: true,
            custom_errors: true,
            traits_are_send: true,
            traits_are_sync: true,
            generate_mocking_interface: true,
            abi_compatibles: true,
            struct_refs: true,
            free_functions: true,
        }
    }

    fn check_string(&self, v: &str) -> Option<bool> {
        match v {
            "namespacing" => Some(self.namespacing),
            "memory_sharing" => Some(self.memory_sharing),
            "non_exhaustive_structs" => Some(self.non_exhaustive_structs),
            "method_overloading" => Some(self.method_overloading),
            "utf8_strings" => Some(self.utf8_strings),
            "utf16_strings" => Some(self.utf16_strings),
            "static_slices" => Some(self.static_slices),
            "default" => Some(self.defaults),
            "constructors" => Some(self.constructors),
            "named_constructors" => Some(self.named_constructors),
            "fallible_constructors" => Some(self.fallible_constructors),
            "accessors" => Some(self.accessors),
            "stringifiers" => Some(self.stringifiers),
            "comparators" => Some(self.comparators),
            "iterators" => Some(self.iterators),
            "iterables" => Some(self.iterables),
            "indexing" => Some(self.indexing),
            "arithmetic" => Some(self.arithmetic),
            "option" => Some(self.option),
            "callbacks" => Some(self.callbacks),
            "traits" => Some(self.traits),
            "custom_errors" => Some(self.custom_errors),
            "traits_are_send" => Some(self.traits_are_send),
            "traits_are_sync" => Some(self.traits_are_sync),
            "abi_compatibles" => Some(self.abi_compatibles),
            "struct_refs" => Some(self.struct_refs),
            "free_functions" => Some(self.free_functions),
            _ => None,
        }
    }
}

/// Defined by backends when validating attributes
pub trait AttributeValidator {
    /// The primary name of the backend, for use in diagnostics
    fn primary_name(&self) -> &str;
    /// Does this backend satisfy `cfg(backend_name)`?
    /// (Backends are allowed to satisfy multiple backend names, useful when there
    /// are multiple backends for a language)
    fn is_backend(&self, backend_name: &str) -> bool;
    /// does this backend satisfy cfg(name = value)?
    fn is_name_value(&self, name: &str, value: &str) -> Result<bool, LoweringError>;
    /// What backedn attrs does this support?
    fn attrs_supported(&self) -> BackendAttrSupport;

    /// Provided, checks if type satisfies a `DiplomatBackendAttrCfg`
    ///
    /// auto_found helps check for `auto`, which is only allowed within `any` and at the top level. When `None`,
    /// `auto` is not allowed.
    fn satisfies_cfg(
        &self,
        cfg: &DiplomatBackendAttrCfg,
        mut auto_found: Option<&mut bool>,
    ) -> Result<bool, LoweringError> {
        Ok(match *cfg {
            DiplomatBackendAttrCfg::Not(ref c) => !self.satisfies_cfg(c, None)?,
            DiplomatBackendAttrCfg::Any(ref cs) => {
                #[allow(clippy::needless_option_as_deref)]
                // False positive: we need this for reborrowing
                for c in cs {
                    if self.satisfies_cfg(c, auto_found.as_deref_mut())? {
                        return Ok(true);
                    }
                }
                false
            }
            DiplomatBackendAttrCfg::All(ref cs) => {
                for c in cs {
                    if !self.satisfies_cfg(c, None)? {
                        return Ok(false);
                    }
                }
                true
            }
            DiplomatBackendAttrCfg::Auto => {
                if let Some(found) = auto_found {
                    *found = true;
                    return Ok(true);
                } else {
                    return Err(LoweringError::Other("auto in diplomat::attr() is only allowed at the top level and within `any`".into()));
                }
            }
            DiplomatBackendAttrCfg::Star => true,
            DiplomatBackendAttrCfg::BackendName(ref n) => self.is_backend(n),
            DiplomatBackendAttrCfg::NameValue(ref n, ref v) => self.is_name_value(n, v)?,
        })
    }

    // Provided, constructs an attribute
    fn attr_from_ast(
        &self,
        ast: &ast::Attrs,
        parent_attrs: &Attrs,
        errors: &mut ErrorStore,
    ) -> Attrs {
        Attrs::from_ast(ast, self, parent_attrs, errors)
    }

    // Provided: validates an attribute in the context in which it was constructed
    fn validate(&self, attrs: &Attrs, context: AttributeContext, errors: &mut ErrorStore) {
        attrs.validate(self, context, errors)
    }
}

/// A basic attribute validator
#[non_exhaustive]
#[derive(Default)]
pub struct BasicAttributeValidator {
    /// The primary name of this backend (should be unique, ideally)
    pub backend_name: String,
    /// The attributes supported
    pub support: BackendAttrSupport,
    /// Additional names for this backend
    pub other_backend_names: Vec<String>,
    /// override is_name_value()
    #[allow(clippy::type_complexity)] // dyn fn is not that complex
    pub is_name_value: Option<Box<dyn Fn(&str, &str) -> bool>>,
}

impl BasicAttributeValidator {
    pub fn new(backend_name: &str) -> Self {
        BasicAttributeValidator {
            backend_name: backend_name.into(),
            ..Self::default()
        }
    }
}

impl AttributeValidator for BasicAttributeValidator {
    fn primary_name(&self) -> &str {
        &self.backend_name
    }
    fn is_backend(&self, backend_name: &str) -> bool {
        self.backend_name == backend_name
            || self.other_backend_names.iter().any(|n| n == backend_name)
    }
    fn is_name_value(&self, name: &str, value: &str) -> Result<bool, LoweringError> {
        Ok(if name == "supports" {
            // destructure so new fields are forced to be added
            let BackendAttrSupport {
                namespacing,
                memory_sharing,
                non_exhaustive_structs,
                method_overloading,
                utf8_strings,
                utf16_strings,
                static_slices,
                defaults,

                constructors,
                named_constructors,
                fallible_constructors,
                accessors,
                static_accessors,
                stringifiers,
                comparators,
                iterators,
                iterables,
                indexing,
                arithmetic,
                option,
                callbacks,
                traits,
                custom_errors,
                traits_are_send,
                traits_are_sync,
                generate_mocking_interface,
                abi_compatibles,
                struct_refs,
                free_functions,
            } = self.support;
            match value {
                "namespacing" => namespacing,
                "memory_sharing" => memory_sharing,
                "non_exhaustive_structs" => non_exhaustive_structs,
                "method_overloading" => method_overloading,
                "utf8_strings" => utf8_strings,
                "utf16_strings" => utf16_strings,
                "static_slices" => static_slices,
                "defaults" => defaults,

                "constructors" => constructors,
                "named_constructors" => named_constructors,
                "fallible_constructors" => fallible_constructors,
                "accessors" => accessors,
                "static_accessors" => static_accessors,
                "stringifiers" => stringifiers,
                "comparators" => comparators,
                "iterators" => iterators,
                "iterables" => iterables,
                "indexing" => indexing,
                "arithmetic" => arithmetic,
                "option" => option,
                "callbacks" => callbacks,
                "traits" => traits,
                "custom_errors" => custom_errors,
                "traits_are_send" => traits_are_send,
                "traits_are_sync" => traits_are_sync,
                "generate_mocking_interface" => generate_mocking_interface,
                "abi_compatibles" => abi_compatibles,
                "struct_refs" => struct_refs,
                "free_functions" => free_functions,
                _ => {
                    return Err(LoweringError::Other(format!(
                        "Unknown supports = value found: {value}"
                    )))
                }
            }
        } else if let Some(ref nv) = self.is_name_value {
            nv(name, value)
        } else {
            false
        })
    }
    fn attrs_supported(&self) -> BackendAttrSupport {
        self.support
    }
}

#[cfg(test)]
mod tests {
    use crate::hir;
    use std::fmt::Write;

    macro_rules! uitest_lowering_attr {
        ($attrs:expr, $($file:tt)*) => {
            let parsed: syn::File = syn::parse_quote! { $($file)* };

            let mut output = String::new();

            let mut attr_validator = hir::BasicAttributeValidator::new("tests");
            attr_validator.support = $attrs;
            match hir::TypeContext::from_syn(&parsed, Default::default(), attr_validator) {
                Ok(_context) => (),
                Err(e) => {
                    for (ctx, err) in e {
                        writeln!(&mut output, "Lowering error in {ctx}: {err}").unwrap();
                    }
                }
            };
            insta::with_settings!({}, {
                insta::assert_snapshot!(output)
            });
        }
    }

    #[test]
    fn test_auto() {
        uitest_lowering_attr! { hir::BackendAttrSupport { comparators: true, ..Default::default()},
            #[diplomat::bridge]
            mod ffi {
                use std::cmp;

                #[diplomat::opaque]
                #[diplomat::attr(auto, namespace = "should_not_show_up")]
                struct Opaque;


                impl Opaque {
                    #[diplomat::attr(auto, comparison)]
                    pub fn comparator_static(&self, other: &Opaque) -> cmp::Ordering {
                        todo!()
                    }
                    #[diplomat::attr(*, iterator)]
                    pub fn next(&mut self) -> Option<u8> {
                        self.0.next()
                    }
                    #[diplomat::attr(auto, rename = "bar")]
                    pub fn auto_doesnt_work_on_renames(&self) {
                    }
                    #[diplomat::attr(auto, disable)]
                    pub fn auto_doesnt_work_on_disables(&self) {
                    }
                }

            }
        }
    }

    #[test]
    fn test_comparator() {
        uitest_lowering_attr! { hir::BackendAttrSupport::all_true(),
            #[diplomat::bridge]
            mod ffi {
                use std::cmp;

                #[diplomat::opaque]
                struct Opaque;

                struct Struct {
                    field: u8
                }


                impl Opaque {
                    #[diplomat::attr(auto, comparison)]
                    pub fn comparator_static(other: &Opaque) -> cmp::Ordering {
                        todo!()
                    }
                    #[diplomat::attr(auto, comparison)]
                    pub fn comparator_none(&self) -> cmp::Ordering {
                        todo!()
                    }
                    #[diplomat::attr(auto, comparison)]
                    pub fn comparator_othertype(other: Struct) -> cmp::Ordering {
                        todo!()
                    }
                    #[diplomat::attr(auto, comparison)]
                    pub fn comparator_badreturn(&self, other: &Opaque) -> u8 {
                        todo!()
                    }
                    #[diplomat::attr(auto, comparison)]
                    pub fn comparison_correct(&self, other: &Opaque) -> cmp::Ordering {
                        todo!()
                    }
                    pub fn comparison_unmarked(&self, other: &Opaque) -> cmp::Ordering {
                        todo!()
                    }
                    pub fn ordering_wrong(&self, other: cmp::Ordering) {
                        todo!()
                    }
                    #[diplomat::attr(auto, comparison)]
                    pub fn comparison_mut(&self, other: &mut Opaque) -> cmp::Ordering {
                        todo!()
                    }
                    #[diplomat::attr(auto, comparison)]
                    pub fn comparison_opt(&self, other: Option<&Opaque>) -> cmp::Ordering {
                        todo!()
                    }
                }

                impl Struct {
                    #[diplomat::attr(auto, comparison)]
                    pub fn comparison_other(self, other: &Opaque) -> cmp::Ordering {
                        todo!()
                    }
                    #[diplomat::attr(auto, comparison)]
                    pub fn comparison_correct(self, other: Self) -> cmp::Ordering {
                        todo!()
                    }

                    #[diplomat::attr(auto, comparison)]
                    pub fn comparison_ref(&self, other: &Self) -> cmp::Ordering {
                        todo!()
                    }

                    #[diplomat::attr(auto, comparison)]
                    pub fn comparison_mut(&mut self, other: &Self) -> cmp::Ordering {
                        todo!()
                    }
                }
            }
        }
    }

    #[test]
    fn test_iterator() {
        uitest_lowering_attr! { hir::BackendAttrSupport::all_true(),
            #[diplomat::bridge]
            mod ffi {

                #[diplomat::opaque]
                struct Opaque(Vec<u8>);
                #[diplomat::opaque]
                struct OpaqueIterator<'a>(std::slice::Iter<'a>);


                impl Opaque {
                    #[diplomat::attr(auto, iterable)]
                    pub fn iterable<'a>(&'self) -> Box<OpaqueIterator<'a>> {
                        Box::new(OpaqueIterator(self.0.iter()))
                    }
                }

                impl OpaqueIterator {
                    #[diplomat::attr(auto, iterator)]
                    pub fn next(&mut self) -> Option<u8> {
                        self.0.next()
                    }
                }

                #[diplomat::opaque]
                struct Broken;

                impl Broken {
                    #[diplomat::attr(auto, iterable)]
                    pub fn iterable_no_return(&self) {}
                    #[diplomat::attr(auto, iterable)]
                    pub fn iterable_no_self() -> Box<BrokenIterator> { todo!() }

                    #[diplomat::attr(auto, iterable)]
                    pub fn iterable_non_custom(&self) -> u8 { todo!() }
                }

                #[diplomat::opaque]
                struct BrokenIterator;

                impl BrokenIterator {
                    #[diplomat::attr(auto, iterator)]
                    pub fn iterator_no_return(&self) {}
                    #[diplomat::attr(auto, iterator)]
                    pub fn iterator_no_self() -> Option<u8> { todo!() }

                    #[diplomat::attr(auto, iterator)]
                    pub fn iterator_no_option(&self) -> u8 { todo!() }
                }
            }
        }
    }

    #[test]
    fn test_unsupported_features() {
        uitest_lowering_attr! { hir::BackendAttrSupport::default(),
            #[diplomat::bridge]
            mod ffi {
                use std::cmp;
                use diplomat_runtime::DiplomatOption;

                #[diplomat::opaque]
                struct Opaque;

                struct Struct {
                    pub a: u8,
                    pub b: u8,
                    pub c: DiplomatOption<u8>,
                }

                struct Struct2 {
                    pub a: DiplomatOption<Struct>,
                }

                #[diplomat::out]
                struct OutStruct {
                    pub option: DiplomatOption<u8>
                }


                impl Opaque {
                    pub fn take_option(&self, option: DiplomatOption<u8>) {
                        todo!()
                    }
                    // Always ok since this translates to a Resulty return
                    pub fn returning_option_is_ok(&self) -> Option<u8> {
                        todo!()
                    }
                }

            }
        }
    }

    #[test]
    fn test_mocking_interface_for_opaque_type() {
        uitest_lowering_attr! { hir::BackendAttrSupport::all_true(),
            #[diplomat::bridge]
            mod ffi {
                #[diplomat::opaque]
                #[diplomat::attr(tests, generate_mocking_interface)]
                pub struct Foo {
                    pub x: u32,
                    pub y: u32,
                }

                impl Foo {
                    pub fn new() -> Box<Self> {
                        Box::new(Self { x: 0, y: 0 })
                    }

                    pub fn get_x(&self) -> u32 {
                        self.x
                    }

                    pub fn get_y(&self) -> u32 {
                        self.y
                    }
                }
            }
        }
    }

    #[test]
    fn test_mocking_interface_for_non_opaque_type() {
        uitest_lowering_attr! { hir::BackendAttrSupport::all_true(),
            #[diplomat::bridge]
            mod ffi {
                #[diplomat::attr(tests, generate_mocking_interface)]
                pub struct Foo {
                    pub x: u32,
                    pub y: u32,
                }

                impl Foo {
                    pub fn new() -> Self {
                        Self { x: 0, y: 0 }
                    }

                    pub fn get_x(self) -> u32 {
                        self.x
                    }

                    pub fn get_y(self) -> u32 {
                        self.y
                    }
                }
            }
        }
    }

    #[test]
    fn test_mocking_interface_for_unsupported_backend() {
        uitest_lowering_attr! { hir::BackendAttrSupport::default(),
            #[diplomat::bridge]
            mod ffi {
                #[diplomat::attr(tests, generate_mocking_interface)]
                pub struct Foo {
                    pub x: u32,
                    pub y: u32,
                }

                impl Foo {
                    pub fn new() -> Self {
                        Self { x: 0, y: 0 }
                    }

                    pub fn get_x(self) -> u32 {
                        self.x
                    }

                    pub fn get_y(self) -> u32 {
                        self.y
                    }
                }
            }
        }
    }

    #[test]
    fn test_primitive_struct_slices() {
        uitest_lowering_attr! { hir::BackendAttrSupport::all_true(),
            #[diplomat::bridge]
            mod ffi {
                #[diplomat::attr(auto, abi_compatible)]
                pub struct Foo {
                    pub x: u32,
                    pub y: u32
                }

                impl Foo {
                    pub fn takes_slice(sl : &[Foo]) {
                        todo!()
                    }
                }
            }
        }
    }

    #[test]
    fn test_primitive_struct_slices_for_unsupported_backend() {
        uitest_lowering_attr! { hir::BackendAttrSupport::default(),
            #[diplomat::bridge]
            mod ffi {
                #[diplomat::attr(auto, abi_compatible)]
                pub struct Foo {
                    pub x: u32,
                    pub y: u32
                }

                impl Foo {
                    pub fn takes_slice(sl : &[Foo]) {
                        todo!()
                    }
                }
            }
        }
    }

    #[test]
    fn test_struct_ref_for_unsupported_backend() {
        uitest_lowering_attr! { hir::BackendAttrSupport::default(),
            #[diplomat::bridge]
            mod ffi {
                #[diplomat::attr(auto, abi_compatible)]
                pub struct Foo {
                    pub x: u32,
                    pub y: u32
                }

                impl Foo {
                    pub fn takes_mut(&mut self) {
                        todo!()
                    }
                }
            }
        }
    }
}

Messung V0.5 in Prozent
C=86 H=99 G=92

¤ Dauer der Verarbeitung: 0.33 Sekunden  (vorverarbeitet am  2026-08-27) ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

PVS Prover

Isabelle Prover

NIST Cobol Testsuite

Cephes Mathematical Library

Vienna Development Method

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.






                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Open Source Software

     Quellcodebibliothek
     Eigene Quellcodes
     Fremde Quellcodes
     Suchen

Jenseits des Üblichen ....

Besucherstatistik

Besucherstatistik

Statistik
#Sources=434850
#Domains=661743