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


Quelle  attrs.rs   Sprache: unbekannt

 
//! #[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,
    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,
    /// 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>,
}

/// 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 (writeable)
    Stringifier,
    /// A comparison operator. Currently unsupported
    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,
}

/// 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>),
    EnumVariant(&'a EnumVariant),
    Method(&'a Method, TypeId, &'b mut SpecialMethodPresence),
    Module,
}

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();

        let support = validator.attrs_supported();
        let backend = validator.primary_name();
        for attr in &ast.attrs {
            let satisfies = match validator.satisfies_cfg(&attr.cfg) {
                Ok(satisfies) => satisfies,
                Err(e) => {
                    errors.push(e);
                    continue;
                }
            };
            if satisfies {
                let path = attr.meta.path();
                if let Some(path) = path.get_ident() {
                    if path == "disable" {
                        if let Meta::Path(_) = attr.meta {
                            if this.disable {
                                errors.push(LoweringError::Other(
                                    "Duplicate `disable` attribute".into(),
                                ));
                            } else if !support.disabling {
                                errors.push(LoweringError::Other(format!(
                                    "`disable` not supported in backend {backend}"
                                )))
                            } else {
                                this.disable = true;
                            }
                        } else {
                            errors.push(LoweringError::Other(
                                "`disable` must be a simple path".into(),
                            ))
                        }
                    } else if path == "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:?}"
                            ))),
                        }
                    } else if path == "namespace" {
                        if !support.namespacing {
                            errors.push(LoweringError::Other(format!(
                                "`namespace` not supported in backend {backend}"
                            )));
                            continue;
                        }
                        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(),
                                ));
                                continue;
                            }
                        }
                    } else if path == "constructor"
                        || path == "stringifier"
                        || path == "comparison"
                        || path == "iterable"
                        || path == "iterator"
                        || path == "indexer"
                    {
                        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:?}"
                        )));
                            continue;
                        }
                        let kind = if path == "constructor" {
                            if !support.constructors {
                                errors.push(LoweringError::Other(format!(
                                    "constructor not supported in backend {backend}"
                                )))
                            }
                            SpecialMethod::Constructor
                        } else if path == "stringifier" {
                            if !support.stringifiers {
                                errors.push(LoweringError::Other(format!(
                                    "stringifier not supported in backend {backend}"
                                )))
                            }
                            SpecialMethod::Stringifier
                        } else if path == "iterable" {
                            if !support.iterables {
                                errors.push(LoweringError::Other(format!(
                                    "iterable not supported in backend {backend}"
                                )))
                            }
                            SpecialMethod::Iterable
                        } else if path == "iterator" {
                            if !support.iterators {
                                errors.push(LoweringError::Other(format!(
                                    "iterator not supported in backend {backend}"
                                )))
                            }
                            SpecialMethod::Iterator
                        } else if path == "indexer" {
                            if !support.indexing {
                                errors.push(LoweringError::Other(format!(
                                    "indexing not supported in backend {backend}"
                                )))
                            }
                            SpecialMethod::Indexer
                        } else {
                            if !support.comparators {
                                errors.push(LoweringError::Other(format!(
                                    "comparison overload not supported in backend {backend}"
                                )))
                            }
                            SpecialMethod::Comparison
                        };

                        this.special_method = Some(kind);
                    } else if path == "named_constructor" || path == "getter" || path == "setter" {
                        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:?}"
                        )));
                            continue;
                        }
                        let kind = if path == "named_constructor" {
                            if !support.named_constructors {
                                errors.push(LoweringError::Other(format!(
                                    "named constructors not supported in backend {backend}"
                                )))
                            }
                            SpecialMethod::NamedConstructor
                        } else if path == "getter" {
                            if !support.accessors {
                                errors.push(LoweringError::Other(format!(
                                    "accessors not supported in backend {backend}"
                                )))
                            }
                            SpecialMethod::Getter
                        } else {
                            if !support.accessors {
                                errors.push(LoweringError::Other(format!(
                                    "accessors not supported in backend {backend}"
                                )))
                            }
                            SpecialMethod::Setter
                        };
                        match StandardAttribute::from_meta(&attr.meta) {
                            Ok(StandardAttribute::String(s)) => {
                                this.special_method = Some(kind(Some(s)))
                            }
                            Ok(StandardAttribute::Empty) => this.special_method = Some(kind(None)),
                            Ok(_) | Err(_) => {
                                errors.push(LoweringError::Other(format!(
                                    "`{path}` must have a single string parameter or no parameter",
                                )));
                                continue;
                            }
                        }
                    } else {
                        errors.push(LoweringError::Other(format!(
                        "Unknown diplomat attribute {path}: expected one of: `disable, rename, namespace, constructor, stringifier, comparison, named_constructor, getter, setter, indexer`"
                    )));
                    }
                } else {
                    errors.push(LoweringError::Other(format!(
                        "Unknown diplomat attribute {path:?}: expected one of: `disable, rename, namespace, constructor, stringifier, comparison, named_constructor, getter, setter, indexer`"
                    )));
                }
            }
        }

        this
    }

    /// Validate that this attribute is allowed in this context
    pub(crate) fn validate(
        &self,
        validator: &(impl AttributeValidator + ?Sized),
        mut context: AttributeContext,
        errors: &mut ErrorStore,
    ) {
        // use an exhaustive destructure so new attributes are handled
        let Attrs {
            disable,
            namespace,
            rename: _,
            abi_rename: _,
            special_method,
        } = &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
            {
                match special {
                    SpecialMethod::Constructor | SpecialMethod::NamedConstructor(..) => {
                        if method.param_self.is_some() {
                            errors.push(LoweringError::Other(
                                "Constructors must not accept a self parameter".to_string(),
                            ))
                        }
                        let output = method.output.success_type();
                        match method.output {
                            ReturnType::Infallible(_) => (),
                            ReturnType::Fallible(..) => {
                                if !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() != Some(self_id) {
                                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()));
                        }

                        // 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.params.len() != 1 {
                            errors.push(LoweringError::Other(
                                "Setter must have exactly one parameter".into(),
                            ))
                        }

                        // 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::Writeable) {
                            errors.push(LoweringError::Other(
                                "Stringifier must return Writeable".into(),
                            ));
                        }
                    }
                    SpecialMethod::Comparison => {
                        if method.params.len() != 1 {
                            errors.push(LoweringError::Other(
                                "Comparator must have single parameter".into(),
                            ));
                        }
                        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";
                        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(),
                                            ));
                                        }
                                    }
                                    (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()));
                                    }
                                }
                            }
                        } else {
                            errors
                                .push(LoweringError::Other("Comparator must be non-static".into()));
                        }
                    }
                    SpecialMethod::Iterator => {
                        if special_method_presence.iterator.is_some() {
                            errors.push(LoweringError::Other(
                                "Cannot mark type as iterator twice".into(),
                            ));
                        }
                        if !method.params.is_empty() {
                            errors.push(LoweringError::Other(
                                "Iterators cannot take parameters".into(),
                            ))
                        }
                        // 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
                        if let Some(this) = &method.param_self {
                            if !matches!(this.ty, SelfType::Opaque(..)) {
                                errors.push(LoweringError::Other(
                                    "Iterators only allowed on opaques".into(),
                                ))
                            }
                        } else {
                            errors.push(LoweringError::Other("Iterators must take self".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(),
                            ));
                        }
                        if !method.params.is_empty() {
                            errors.push(LoweringError::Other(
                                "Iterables cannot take parameters".into(),
                            ))
                        }
                        if method.param_self.is_none() {
                            errors.push(LoweringError::Other("Iterables must take self".into()))
                        }

                        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 => {
                        if method.params.len() != 1 {
                            errors.push(LoweringError::Other(
                                "Indexer must have exactly one parameter".into(),
                            ));
                        }

                        if method.output.success_type().is_unit() {
                            errors.push(LoweringError::Other("Indexer must return a value".into()));
                        }
                    }
                }
            } 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(),
            ));
        }
    }

    pub(crate) fn 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,
            rename,
            namespace,
            // Was already inherited on the AST side
            abi_rename: Default::default(),
            // Never inherited
            special_method: None,
        }
    }
}

#[non_exhaustive]
#[derive(Copy, Clone, Debug, Default)]
pub struct BackendAttrSupport {
    pub disabling: bool,
    pub renaming: bool,
    pub namespacing: bool,
    pub constructors: bool,
    pub named_constructors: bool,
    pub fallible_constructors: bool,
    pub accessors: bool,
    pub stringifiers: bool,
    pub comparators: bool,
    pub memory_sharing: bool,
    pub iterators: bool,
    pub iterables: bool,
    pub indexing: bool,
    // more to be added: namespace, etc
}

impl BackendAttrSupport {
    #[cfg(test)]
    fn all_true() -> Self {
        Self {
            disabling: true,
            renaming: true,
            namespacing: true,
            constructors: true,
            named_constructors: true,
            fallible_constructors: true,
            accessors: true,
            stringifiers: true,
            comparators: true,
            memory_sharing: true,
            iterators: true,
            iterables: true,
            indexing: true,
        }
    }
}

/// 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`
    fn satisfies_cfg(&self, cfg: &DiplomatBackendAttrCfg) -> Result<bool, LoweringError> {
        Ok(match *cfg {
            DiplomatBackendAttrCfg::Not(ref c) => !self.satisfies_cfg(c)?,
            DiplomatBackendAttrCfg::Any(ref cs) => {
                for c in cs {
                    if self.satisfies_cfg(c)? {
                        return Ok(true);
                    }
                }
                false
            }
            DiplomatBackendAttrCfg::All(ref cs) => {
                for c in cs {
                    if !self.satisfies_cfg(c)? {
                        return Ok(false);
                    }
                }
                true
            }
            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 {
                disabling,
                renaming,
                namespacing,
                constructors,
                named_constructors,
                fallible_constructors,
                accessors,
                stringifiers,
                comparators,
                memory_sharing,
                iterators,
                iterables,
                indexing,
            } = self.support;
            match value {
                "disabling" => disabling,
                "renaming" => renaming,
                "namespacing" => namespacing,
                "constructors" => constructors,
                "named_constructors" => named_constructors,
                "fallible_constructors" => fallible_constructors,
                "accessors" => accessors,
                "stringifiers" => stringifiers,
                "comparators" => comparators,
                "memory_sharing" => memory_sharing,
                "iterators" => iterators,
                "iterables" => iterables,
                "indexing" => indexing,
                _ => {
                    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 {
        ($($file:tt)*) => {
            let parsed: syn::File = syn::parse_quote! { $($file)* };
            let custom_types = crate::ast::File::from(&parsed);
            let env = custom_types.all_types();

            let mut output = String::new();


            let mut attr_validator = hir::BasicAttributeValidator::new("tests");
            attr_validator.support = hir::BackendAttrSupport::all_true();
            match hir::TypeContext::from_ast(&env, 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_comparator() {
        uitest_lowering_attr! {
            #[diplomat::bridge]
            mod ffi {
                use std::cmp;

                #[diplomat::opaque]
                struct Opaque;

                struct Struct {
                    field: u8
                }


                impl Opaque {
                    #[diplomat::attr(*, comparison)]
                    pub fn comparator_static(other: &Opaque) -> cmp::Ordering {
                        todo!()
                    }
                    #[diplomat::attr(*, comparison)]
                    pub fn comparator_none(&self) -> cmp::Ordering {
                        todo!()
                    }
                    #[diplomat::attr(*, comparison)]
                    pub fn comparator_othertype(other: Struct) -> cmp::Ordering {
                        todo!()
                    }
                    #[diplomat::attr(*, comparison)]
                    pub fn comparator_badreturn(&self, other: &Opaque) -> u8 {
                        todo!()
                    }
                    #[diplomat::attr(*, 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(*, comparison)]
                    pub fn comparison_mut(&self, other: &mut Opaque) -> cmp::Ordering {
                        todo!()
                    }
                    #[diplomat::attr(*, comparison)]
                    pub fn comparison_opt(&self, other: Option<&Opaque>) -> cmp::Ordering {
                        todo!()
                    }
                }

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

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

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


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

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

                #[diplomat::opaque]
                struct Broken;

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

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

                #[diplomat::opaque]
                struct BrokenIterator;

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

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

[ Dauer der Verarbeitung: 0.43 Sekunden  (vorverarbeitet)  ]

                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Software

     Produkte
     Quellcodebibliothek

Aktivitäten

     Artikel über Sicherheit
     Anleitung zur Aktivierung von SSL

Muße

     Gedichte
     Musik
     Bilder

Jenseits des Üblichen ....

Besucherstatistik

Besucherstatistik

Monitoring

Montastic status badge