/// 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)] pubstruct 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)] pubenum 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] pubstruct 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)] pubenum AttributeContext<'a, 'b> { Type(TypeDef<'a>),
EnumVariant(&'a EnumVariant),
Method(&'a Method, TypeId, &'b mut SpecialMethodPresence),
Module,
}
impl Attrs { pubfn from_ast(
ast: &ast::Attrs,
validator: &(impl AttributeValidator + ?Sized),
parent_attrs: &Attrs,
errors: &mut ErrorStore,
) -> Self { letmut 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(); iflet Some(path) = path.get_ident() { if path == "disable" { iflet Meta::Path(_) = attr.meta { if this.disable {
errors.push(LoweringError::Other( "Duplicate `disable` attribute".into(),
));
} elseif !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(),
))
}
} elseif 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:?}"
))),
}
} elseif 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;
}
}
} elseif path == "constructor"
|| path == "stringifier"
|| path == "comparison"
|| path == "iterable"
|| path == "iterator"
|| path == "indexer"
{ iflet 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
} elseif path == "stringifier" { if !support.stringifiers {
errors.push(LoweringError::Other(format!( "stringifier not supported in backend {backend}"
)))
}
SpecialMethod::Stringifier
} elseif path == "iterable" { if !support.iterables {
errors.push(LoweringError::Other(format!( "iterable not supported in backend {backend}"
)))
}
SpecialMethod::Iterable
} elseif path == "iterator" { if !support.iterators {
errors.push(LoweringError::Other(format!( "iterator not supported in backend {backend}"
)))
}
SpecialMethod::Iterator
} elseif 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);
} elseif path == "named_constructor" || path == "getter" || path == "setter" { iflet 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
} elseif 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(),
))
}
iflet Some(ref special) = special_method { iflet AttributeContext::Method(method, self_id, refmut 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()));
}
}
iflet 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"; iflet Some(ref selfty) = method.param_self { iflet 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 iflet 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()))
}
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) => { iflet 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(),
));
}
}
// 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,
}
}
}
/// Defined by backends when validating attributes pubtrait 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 { ifself.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: 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)] pubstruct 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<dynFn(&str, &str) -> bool>>,
}
¤ Diese beiden folgenden Angebotsgruppen bietet das Unternehmen0.31Angebot
(Wie Sie bei der Firma Beratungs- und Dienstleistungen beauftragen können 2026-06-19)
¤
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.