use indexmap::IndexMap;
use itertools::Itertools;
use serde::{Deserialize, Serialize};
use std::{hash::Hash, sync::Arc};
use sway_ast::{
attribute::*, AttributeDecl, ImplItemParent, ItemImplItem, ItemKind, ItemTraitItem, Literal,
};
use sway_error::{
convert_parse_tree_error::ConvertParseTreeError,
handler::{ErrorEmitted, Handler},
};
use sway_features::Feature;
use sway_types::{Ident, Span, Spanned};
use crate::language::{Inline, Purity, Trace};
#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
pub struct AttributeArg {
pub name: Ident,
pub value: Option<Literal>,
pub span: Span,
}
impl AttributeArg {
pub fn get_string(
&self,
handler: &Handler,
attribute: &Attribute,
) -> Result<&String, ErrorEmitted> {
match &self.value {
Some(literal) => match literal {
Literal::String(lit_string) => Ok(&lit_string.parsed),
_ => Err(handler.emit_err(
ConvertParseTreeError::InvalidAttributeArgValueType {
span: literal.span(),
arg: self.name.clone(),
expected_type: "str",
received_type: literal.friendly_type_name(),
}
.into(),
)),
},
None => Err(handler.emit_err(
ConvertParseTreeError::InvalidAttributeArgExpectsValue {
attribute: attribute.name.clone(),
arg: (&self.name).into(),
value_span: None,
}
.into(),
)),
}
}
pub fn get_string_opt(&self, handler: &Handler) -> Result<Option<&String>, ErrorEmitted> {
match &self.value {
Some(literal) => match literal {
Literal::String(lit_string) => Ok(Some(&lit_string.parsed)),
_ => Err(handler.emit_err(
ConvertParseTreeError::InvalidAttributeArgValueType {
span: literal.span(),
arg: self.name.clone(),
expected_type: "str",
received_type: literal.friendly_type_name(),
}
.into(),
)),
},
None => Ok(None),
}
}
pub fn get_bool(&self, handler: &Handler, attribute: &Attribute) -> Result<bool, ErrorEmitted> {
match &self.value {
Some(literal) => match literal {
Literal::Bool(lit_bool) => Ok(lit_bool.kind.into()),
_ => Err(handler.emit_err(
ConvertParseTreeError::InvalidAttributeArgValueType {
span: literal.span(),
arg: self.name.clone(),
expected_type: "bool",
received_type: literal.friendly_type_name(),
}
.into(),
)),
},
None => Err(handler.emit_err(
ConvertParseTreeError::InvalidAttributeArgExpectsValue {
attribute: attribute.name.clone(),
arg: (&self.name).into(),
value_span: None,
}
.into(),
)),
}
}
pub fn is_allow_dead_code(&self) -> bool {
self.name.as_str() == ALLOW_DEAD_CODE_ARG_NAME
}
pub fn is_allow_deprecated(&self) -> bool {
self.name.as_str() == ALLOW_DEPRECATED_ARG_NAME
}
pub fn is_cfg_target(&self) -> bool {
self.name.as_str() == CFG_TARGET_ARG_NAME
}
pub fn is_cfg_program_type(&self) -> bool {
self.name.as_str() == CFG_PROGRAM_TYPE_ARG_NAME
}
pub fn is_cfg_experimental(&self) -> bool {
Feature::CFG.contains(&self.name.as_str())
}
pub fn is_deprecated_note(&self) -> bool {
self.name.as_str() == DEPRECATED_NOTE_ARG_NAME
}
pub fn is_test_should_revert(&self) -> bool {
self.name.as_str() == TEST_SHOULD_REVERT_ARG_NAME
}
pub fn is_error_message(&self) -> bool {
self.name.as_str() == ERROR_M_ARG_NAME
}
}
impl Spanned for AttributeArg {
fn span(&self) -> Span {
self.span.clone()
}
}
#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
pub struct Attribute {
pub direction: AttributeDirection,
pub name: Ident,
pub args: Vec<AttributeArg>,
pub span: Span,
pub kind: AttributeKind,
}
#[derive(Clone, Copy, Debug, Eq, PartialEq, Serialize, Deserialize)]
pub enum AttributeDirection {
Inner,
Outer,
}
impl From<&AttributeHashKind> for AttributeDirection {
fn from(value: &AttributeHashKind) -> Self {
match value {
AttributeHashKind::Inner(_) => Self::Inner,
AttributeHashKind::Outer(_) => Self::Outer,
}
}
}
pub struct ArgsMultiplicity {
min: usize,
max: usize,
}
impl ArgsMultiplicity {
pub fn zero() -> Self {
Self { min: 0, max: 0 }
}
pub fn arbitrary() -> Self {
Self {
min: 0,
max: usize::MAX,
}
}
pub fn exactly(num: usize) -> Self {
Self { min: num, max: num }
}
pub fn at_least(num: usize) -> Self {
Self {
min: num,
max: usize::MAX,
}
}
pub fn at_most(num: usize) -> Self {
Self { min: 0, max: num }
}
pub fn between(min: usize, max: usize) -> Self {
assert!(
min <= max,
"min must be less than or equal to max; min was {min}, max was {max}"
);
Self { min, max }
}
pub fn contains(&self, value: usize) -> bool {
self.min <= value && value <= self.max
}
}
impl From<&ArgsMultiplicity> for (usize, usize) {
fn from(value: &ArgsMultiplicity) -> Self {
(value.min, value.max)
}
}
pub enum ExpectedArgs {
None,
Any,
MustBeIn(Vec<&'static str>),
ShouldBeIn(Vec<&'static str>),
}
impl ExpectedArgs {
pub(crate) fn args_names(&self) -> Vec<&'static str> {
match self {
ExpectedArgs::None | ExpectedArgs::Any => vec![],
ExpectedArgs::MustBeIn(expected_args) | ExpectedArgs::ShouldBeIn(expected_args) => {
expected_args.clone()
}
}
}
}
pub enum ArgsExpectValues {
Yes,
No,
Maybe,
}
#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash, Serialize, Deserialize)]
pub enum AttributeKind {
Unknown,
DocComment,
Storage,
Inline,
Test,
Payable,
Allow,
Cfg,
Deprecated,
Fallback,
ErrorType,
Error,
Trace,
AbiName,
Event,
Indexed,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum TraitItemParent {
Abi,
Trait,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum StructOrEnumField {
StructField,
EnumField,
}
impl AttributeKind {
pub fn from_attribute_name(name: &str) -> Self {
match name {
DOC_COMMENT_ATTRIBUTE_NAME => AttributeKind::DocComment,
STORAGE_ATTRIBUTE_NAME => AttributeKind::Storage,
INLINE_ATTRIBUTE_NAME => AttributeKind::Inline,
TEST_ATTRIBUTE_NAME => AttributeKind::Test,
PAYABLE_ATTRIBUTE_NAME => AttributeKind::Payable,
ALLOW_ATTRIBUTE_NAME => AttributeKind::Allow,
CFG_ATTRIBUTE_NAME => AttributeKind::Cfg,
DEPRECATED_ATTRIBUTE_NAME => AttributeKind::Deprecated,
FALLBACK_ATTRIBUTE_NAME => AttributeKind::Fallback,
ERROR_TYPE_ATTRIBUTE_NAME => AttributeKind::ErrorType,
ERROR_ATTRIBUTE_NAME => AttributeKind::Error,
TRACE_ATTRIBUTE_NAME => AttributeKind::Trace,
ABI_NAME_ATTRIBUTE_NAME => AttributeKind::AbiName,
EVENT_ATTRIBUTE_NAME => AttributeKind::Event,
INDEXED_ATTRIBUTE_NAME => AttributeKind::Indexed,
_ => AttributeKind::Unknown,
}
}
pub fn allows_multiple(&self) -> bool {
use AttributeKind::*;
match self {
Unknown => true,
DocComment => true,
Storage => false,
Inline => false,
Test => false,
Payable => false,
Allow => true,
Cfg => true,
Deprecated => false,
Fallback => false,
ErrorType => false,
Error => false,
Trace => false,
AbiName => false,
Event => false,
Indexed => false,
}
}
}
impl Attribute {
pub fn is_doc_comment(&self) -> bool {
self.kind == AttributeKind::DocComment
}
pub fn is_inner(&self) -> bool {
self.direction == AttributeDirection::Inner
}
pub fn is_outer(&self) -> bool {
self.direction == AttributeDirection::Outer
}
pub(crate) fn args_multiplicity(&self) -> ArgsMultiplicity {
use ArgsMultiplicity as Multiplicity;
use AttributeKind::*;
match self.kind {
Unknown => Multiplicity::arbitrary(),
DocComment => Multiplicity::exactly(1),
Storage => Multiplicity::between(1, 2),
Inline => Multiplicity::exactly(1),
Test => Multiplicity::at_most(1),
Payable => Multiplicity::zero(),
Allow => Multiplicity::at_least(1),
Cfg => Multiplicity::exactly(1),
Deprecated => Multiplicity::at_most(1),
Fallback => Multiplicity::zero(),
ErrorType => Multiplicity::zero(),
Error => Multiplicity::exactly(1),
Trace => Multiplicity::exactly(1),
AbiName => Multiplicity::exactly(1),
Event => Multiplicity::zero(),
Indexed => Multiplicity::zero(),
}
}
pub(crate) fn check_args_multiplicity(&self, handler: &Handler) -> Result<(), ErrorEmitted> {
if !self.args_multiplicity().contains(self.args.len()) {
Err(handler.emit_err(
ConvertParseTreeError::InvalidAttributeArgsMultiplicity {
span: if self.args.is_empty() {
self.name.span()
} else {
Span::join(
self.args.first().unwrap().span(),
&self.args.last().unwrap().span,
)
},
attribute: self.name.clone(),
args_multiplicity: (&self.args_multiplicity()).into(),
num_of_args: self.args.len(),
}
.into(),
))
} else {
Ok(())
}
}
pub(crate) fn can_have_arguments(&self) -> bool {
let args_multiplicity = self.args_multiplicity();
args_multiplicity.min != 0 || args_multiplicity.max != 0
}
pub(crate) fn expected_args(&self) -> ExpectedArgs {
use AttributeKind::*;
use ExpectedArgs::*;
match self.kind {
Unknown => Any,
DocComment => Any,
Storage => MustBeIn(vec![STORAGE_READ_ARG_NAME, STORAGE_WRITE_ARG_NAME]),
Inline => MustBeIn(vec![INLINE_ALWAYS_ARG_NAME, INLINE_NEVER_ARG_NAME]),
Test => MustBeIn(vec![TEST_SHOULD_REVERT_ARG_NAME]),
Payable => None,
Allow => ShouldBeIn(vec![ALLOW_DEAD_CODE_ARG_NAME, ALLOW_DEPRECATED_ARG_NAME]),
Cfg => {
let mut args = vec![
CFG_PROGRAM_TYPE_ARG_NAME,
CFG_TARGET_ARG_NAME,
];
args.extend(Feature::CFG.iter().sorted());
MustBeIn(args)
}
Deprecated => MustBeIn(vec![DEPRECATED_NOTE_ARG_NAME]),
Fallback => None,
ErrorType => None,
Error => MustBeIn(vec![ERROR_M_ARG_NAME]),
Trace => MustBeIn(vec![TRACE_ALWAYS_ARG_NAME, TRACE_NEVER_ARG_NAME]),
AbiName => MustBeIn(vec![ABI_NAME_NAME_ARG_NAME]),
Event => None,
Indexed => None,
}
}
pub(crate) fn args_expect_values(&self) -> ArgsExpectValues {
use ArgsExpectValues::*;
use AttributeKind::*;
match self.kind {
Unknown => Maybe,
DocComment => No,
Storage => No,
Inline => No,
Test => Maybe,
Payable => No,
Allow => No,
Cfg => Yes,
Deprecated => Yes,
Fallback => No,
ErrorType => No,
Error => Yes,
Trace => No,
AbiName => Yes,
Event => No,
Indexed => No,
}
}
pub(crate) fn can_annotate_module_kind(&self) -> bool {
use AttributeKind::*;
match self.kind {
Unknown => false,
DocComment => self.direction == AttributeDirection::Inner,
Storage => false,
Inline => false,
Test => false,
Payable => false,
Allow => false,
Cfg => false,
Deprecated => false,
Fallback => false,
ErrorType => false,
Error => false,
Trace => false,
AbiName => false,
Event => false,
Indexed => false,
}
}
pub(crate) fn can_annotate_abi(&self) -> bool {
use AttributeKind::*;
match self.kind {
Unknown => true,
DocComment => self.direction == AttributeDirection::Outer,
Storage => false,
Inline => false,
Test => false,
Payable => false,
Allow => true,
Cfg => true,
Deprecated => false,
Fallback => false,
ErrorType => false,
Error => false,
Trace => false,
AbiName => false,
Event => false,
Indexed => false,
}
}
pub(crate) fn can_annotate_item_kind(&self, item_kind: &ItemKind) -> bool {
if matches!(item_kind, ItemKind::Error(..)) {
return true;
}
use AttributeKind::*;
match self.kind {
Unknown => !matches!(item_kind, ItemKind::Submodule(_)),
DocComment => {
self.direction == AttributeDirection::Outer
&& !matches!(item_kind, ItemKind::Submodule(_))
}
Storage => matches!(item_kind, ItemKind::Fn(_)),
Inline => matches!(item_kind, ItemKind::Fn(_)),
Test => matches!(item_kind, ItemKind::Fn(_)),
Payable => false,
Allow => !matches!(item_kind, ItemKind::Submodule(_)),
Cfg => !matches!(item_kind, ItemKind::Submodule(_)),
Deprecated => match item_kind {
ItemKind::Submodule(_) => false,
ItemKind::Use(_) => false,
ItemKind::Struct(_) => true,
ItemKind::Enum(_) => true,
ItemKind::Fn(_) => true,
ItemKind::Trait(_) => false,
ItemKind::Impl(_) => false,
ItemKind::Abi(_) => false,
ItemKind::Const(_) => true,
ItemKind::Storage(_) => false,
ItemKind::Configurable(_) => false,
ItemKind::TypeAlias(_) => false,
ItemKind::Error(_, _) => true,
},
Fallback => matches!(item_kind, ItemKind::Fn(_)),
ErrorType => matches!(item_kind, ItemKind::Enum(_)),
Error => false,
Trace => matches!(item_kind, ItemKind::Fn(_)),
AbiName => matches!(item_kind, ItemKind::Struct(_) | ItemKind::Enum(_)),
Event => matches!(item_kind, ItemKind::Struct(_) | ItemKind::Enum(_)),
Indexed => false,
}
}
pub(crate) fn can_annotate_struct_or_enum_field(
&self,
struct_or_enum_field: StructOrEnumField,
) -> bool {
use AttributeKind::*;
match self.kind {
Unknown => true,
DocComment => self.direction == AttributeDirection::Outer,
Storage => false,
Inline => false,
Test => false,
Payable => false,
Allow => true,
Cfg => true,
Deprecated => true,
Fallback => false,
ErrorType => false,
Error => struct_or_enum_field == StructOrEnumField::EnumField,
Trace => false,
AbiName => false,
Event => false,
Indexed => matches!(struct_or_enum_field, StructOrEnumField::StructField),
}
}
pub(crate) fn can_annotate_abi_or_trait_interface_item(
&self,
item: &ItemTraitItem,
parent: TraitItemParent,
) -> bool {
use AttributeKind::*;
match self.kind {
Unknown => true,
DocComment => self.direction == AttributeDirection::Outer,
Storage => matches!(item, ItemTraitItem::Fn(..)),
Inline => false,
Test => false,
Payable => parent == TraitItemParent::Abi && matches!(item, ItemTraitItem::Fn(..)),
Allow => true,
Cfg => true,
Deprecated => false,
Fallback => false,
ErrorType => false,
Error => false,
Trace => false,
AbiName => false,
Event => false,
Indexed => false,
}
}
pub(crate) fn can_annotate_abi_or_trait_interface_fn(&self, parent: TraitItemParent) -> bool {
use AttributeKind::*;
match self.kind {
Unknown => true,
DocComment => self.direction == AttributeDirection::Outer,
Storage => true,
Inline => false,
Test => false,
Payable => parent == TraitItemParent::Abi,
Allow => true,
Cfg => true,
Deprecated => false,
Fallback => false,
ErrorType => false,
Error => false,
Trace => false,
AbiName => false,
Event => false,
Indexed => false,
}
}
pub(crate) fn can_annotate_abi_or_trait_interface_const(
&self,
_parent: TraitItemParent,
) -> bool {
use AttributeKind::*;
match self.kind {
Unknown => true,
DocComment => self.direction == AttributeDirection::Outer,
Storage => false,
Inline => false,
Test => false,
Payable => false,
Allow => true,
Cfg => true,
Deprecated => false,
Fallback => false,
ErrorType => false,
Error => false,
Trace => false,
AbiName => false,
Event => false,
Indexed => false,
}
}
pub(crate) fn can_annotate_impl_item(
&self,
item: &ItemImplItem,
parent: ImplItemParent,
) -> bool {
use AttributeKind::*;
match self.kind {
Unknown => true,
DocComment => self.direction == AttributeDirection::Outer,
Storage => matches!(item, ItemImplItem::Fn(..)),
Inline => matches!(item, ItemImplItem::Fn(..)),
Test => false,
Payable => matches!(item, ItemImplItem::Fn(..)) && parent == ImplItemParent::Contract,
Allow => true,
Cfg => true,
Deprecated => !matches!(item, ItemImplItem::Type(_)),
Fallback => false,
ErrorType => false,
Error => false,
Trace => matches!(item, ItemImplItem::Fn(..)),
AbiName => false,
Event => false,
Indexed => false,
}
}
pub(crate) fn can_annotate_abi_or_trait_provided_fn(&self, parent: TraitItemParent) -> bool {
use AttributeKind::*;
match self.kind {
Unknown => true,
DocComment => self.direction == AttributeDirection::Outer,
Storage => true,
Inline => true,
Test => false,
Payable => parent == TraitItemParent::Abi,
Allow => true,
Cfg => true,
Deprecated => true,
Fallback => false,
ErrorType => false,
Error => false,
Trace => true,
AbiName => false,
Event => false,
Indexed => false,
}
}
pub(crate) fn can_annotate_storage_entry(&self) -> bool {
use AttributeKind::*;
match self.kind {
Unknown => true,
DocComment => self.direction == AttributeDirection::Outer,
Storage => false,
Inline => false,
Test => false,
Payable => false,
Allow => true,
Cfg => true,
Deprecated => false,
Fallback => false,
ErrorType => false,
Error => false,
Trace => false,
AbiName => false,
Event => false,
Indexed => false,
}
}
pub(crate) fn can_annotate_configurable_field(&self) -> bool {
use AttributeKind::*;
match self.kind {
Unknown => true,
DocComment => self.direction == AttributeDirection::Outer,
Storage => false,
Inline => false,
Test => false,
Payable => false,
Allow => true,
Cfg => true,
Deprecated => true,
Fallback => false,
ErrorType => false,
Error => false,
Trace => false,
AbiName => false,
Event => false,
Indexed => false,
}
}
pub(crate) fn can_only_annotate_help(&self, target_friendly_name: &str) -> Vec<&'static str> {
use AttributeKind::*;
let help = match self.kind {
Unknown => vec![],
DocComment => match self.direction {
AttributeDirection::Inner => vec![
"Inner doc comments (`//!`) can only document modules and must be",
"at the beginning of the module file, before the module kind.",
],
AttributeDirection::Outer => if target_friendly_name.starts_with("module kind") {
vec![
"To document modules, use inner doc comments (`//!`). E.g.:",
"//! This doc comment documents a module.",
]
} else {
vec![]
},
},
Storage => {
if target_friendly_name == "function signature" {
vec![
"\"storage\" attribute can only annotate functions that have an implementation.",
"Function signatures in ABI and trait declarations do not have implementations.",
]
} else {
vec![
"\"storage\" attribute can only annotate functions.",
]
}
},
Inline => vec!["\"inline\" attribute can only annotate functions."],
Test => vec!["\"test\" attribute can only annotate module functions."],
Payable => vec![
"\"payable\" attribute can only annotate:",
" - ABI function signatures and their implementations in contracts,",
" - provided ABI functions.",
],
Allow => vec![],
Cfg => vec![],
Deprecated => vec![
"\"deprecated\" attribute is currently not implemented for all elements that could be deprecated.",
],
Fallback => vec!["\"fallback\" attribute can only annotate module functions in a contract module."],
ErrorType => vec!["\"error_type\" attribute can only annotate enums."],
Error => vec!["\"error\" attribute can only annotate enum variants of enums annotated with the \"error_type\" attribute."],
Trace => vec!["\"trace\" attribute can only annotate functions."],
AbiName => vec![
"\"abi_name\" attribute can only annotate structs and enums.",
],
Event => vec![
"\"event\" attribute can only annotate structs or enums.",
],
Indexed => vec![
"\"indexed\" attribute can only annotate struct fields.",
],
};
if help.is_empty() && target_friendly_name.starts_with("module kind") {
vec!["Annotating module kinds (contract, script, predicate, or library) is currently not implemented."]
} else {
help
}
}
}
#[derive(Default, Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
pub struct Attributes {
attributes: Arc<Vec<Attribute>>,
deprecated_attr_index: Option<usize>,
}
impl Attributes {
pub fn new(attribute_decls: &[AttributeDecl]) -> Attributes {
let mut attributes: Vec<Attribute> = vec![];
for attr_decl in attribute_decls {
let attrs = attr_decl.attribute.get().into_iter();
for attr in attrs {
let name = attr.name.as_str();
let args = attr
.args
.as_ref()
.map(|parens| {
parens
.get()
.into_iter()
.map(|arg| AttributeArg {
name: arg.name.clone(),
value: arg.value.clone(),
span: arg.span(),
})
.collect()
})
.unwrap_or_default();
let attribute = Attribute {
direction: (&attr_decl.hash_kind).into(),
name: attr.name.clone(),
args,
span: attr_decl.span(),
kind: AttributeKind::from_attribute_name(name),
};
attributes.push(attribute);
}
}
Attributes {
deprecated_attr_index: attributes
.iter()
.rposition(|attr| attr.kind == AttributeKind::Deprecated),
attributes: Arc::new(attributes),
}
}
pub fn retain_from(other: &Attributes, f: impl Fn(&Attribute) -> bool) -> Self {
let attributes = other
.attributes
.iter()
.filter(|attr| f(attr))
.cloned()
.collect_vec();
Self {
deprecated_attr_index: attributes
.iter()
.rposition(|attr| attr.kind == AttributeKind::Deprecated),
attributes: Arc::new(attributes),
}
}
pub fn is_empty(&self) -> bool {
self.attributes.is_empty()
}
pub fn first(&self) -> Option<&Attribute> {
self.attributes.first()
}
pub fn known_attribute_names(&self) -> &'static [&'static str] {
KNOWN_ATTRIBUTE_NAMES
}
pub fn all(&self) -> impl Iterator<Item = &Attribute> {
self.attributes.iter()
}
pub fn all_as_slice(&self) -> &[Attribute] {
self.attributes.as_slice()
}
pub fn all_by_kind<F>(&self, predicate: F) -> IndexMap<AttributeKind, Vec<&Attribute>>
where
F: Fn(&&Attribute) -> bool,
{
let mut result = IndexMap::<_, Vec<&Attribute>>::new();
for attr in self.attributes.iter().filter(predicate) {
result.entry(attr.kind).or_default().push(attr);
}
result
}
pub fn of_kind(&self, kind: AttributeKind) -> impl Iterator<Item = &Attribute> {
self.attributes.iter().filter(move |attr| attr.kind == kind)
}
pub fn has_any_of_kind(&self, kind: AttributeKind) -> bool {
self.of_kind(kind).any(|_| true)
}
pub fn unknown(&self) -> impl Iterator<Item = &Attribute> {
self.attributes
.iter()
.filter(|attr| attr.kind == AttributeKind::Unknown)
}
pub fn has_allow_dead_code(&self) -> bool {
self.has_allow(|arg| arg.is_allow_dead_code())
}
pub fn has_allow_deprecated(&self) -> bool {
self.has_allow(|arg| arg.is_allow_deprecated())
}
fn has_allow(&self, arg_filter: impl Fn(&AttributeArg) -> bool) -> bool {
self.of_kind(AttributeKind::Allow)
.flat_map(|attribute| &attribute.args)
.any(arg_filter)
}
pub fn has_error_type(&self) -> bool {
self.of_kind(AttributeKind::ErrorType).any(|_| true)
}
pub fn has_error(&self) -> bool {
self.of_kind(AttributeKind::Error).any(|_| true)
}
pub fn inline(&self) -> Option<Inline> {
match self
.of_kind(AttributeKind::Inline)
.last()?
.args
.last()?
.name
.as_str()
{
INLINE_NEVER_ARG_NAME => Some(Inline::Never),
INLINE_ALWAYS_ARG_NAME => Some(Inline::Always),
_ => None,
}
}
pub fn trace(&self) -> Option<Trace> {
match self
.of_kind(AttributeKind::Trace)
.last()?
.args
.last()?
.name
.as_str()
{
TRACE_NEVER_ARG_NAME => Some(Trace::Never),
TRACE_ALWAYS_ARG_NAME => Some(Trace::Always),
_ => None,
}
}
pub fn purity(&self) -> Purity {
let Some(storage_attr) = self.of_kind(AttributeKind::Storage).last() else {
return Purity::Pure;
};
let mut purity = Purity::Pure;
let mut add_impurity = |new_impurity, counter_impurity| {
if purity == Purity::Pure {
purity = new_impurity;
} else if purity == counter_impurity {
purity = Purity::ReadsWrites;
}
};
for arg in storage_attr.args.iter() {
match arg.name.as_str() {
STORAGE_READ_ARG_NAME => add_impurity(Purity::Reads, Purity::Writes),
STORAGE_WRITE_ARG_NAME => add_impurity(Purity::Writes, Purity::Reads),
_ => {}
}
}
purity
}
pub fn deprecated(&self) -> Option<&Attribute> {
self.deprecated_attr_index
.map(|index| &self.attributes[index])
}
pub fn abi_name(&self) -> Option<&Attribute> {
self.of_kind(AttributeKind::AbiName).last()
}
pub fn test(&self) -> Option<&Attribute> {
self.of_kind(AttributeKind::Test).last()
}
pub fn error(&self) -> Option<&Attribute> {
self.of_kind(AttributeKind::Error).last()
}
pub fn error_message(&self) -> Option<&String> {
self.error().and_then(|error_attr| {
error_attr
.args
.iter()
.rfind(|arg| arg.is_error_message())
.and_then(|arg| arg.get_string_opt(&Handler::default()).ok().flatten())
})
}
pub fn event(&self) -> Option<&Attribute> {
self.of_kind(AttributeKind::Event).last()
}
pub fn indexed(&self) -> Option<&Attribute> {
self.of_kind(AttributeKind::Indexed).last()
}
}
pub struct AllowDeprecatedEnterToken {
diff: i32,
}
#[derive(Default)]
pub struct AllowDeprecatedState {
allowed: u32,
}
impl AllowDeprecatedState {
pub(crate) fn enter(&mut self, attributes: Attributes) -> AllowDeprecatedEnterToken {
if attributes.has_allow_deprecated() {
self.allowed += 1;
AllowDeprecatedEnterToken { diff: -1 }
} else {
AllowDeprecatedEnterToken { diff: 0 }
}
}
pub(crate) fn exit(&mut self, token: AllowDeprecatedEnterToken) {
self.allowed = self.allowed.saturating_add_signed(token.diff);
}
pub(crate) fn is_allowed(&self) -> bool {
self.allowed > 0
}
}