1use indexmap::IndexMap;
49use itertools::Itertools;
50use serde::{Deserialize, Serialize};
51use std::{hash::Hash, sync::Arc};
52use sway_ast::{
53 attribute::*, AttributeDecl, ImplItemParent, ItemImplItem, ItemKind, ItemTraitItem, Literal,
54};
55use sway_error::{
56 convert_parse_tree_error::ConvertParseTreeError,
57 handler::{ErrorEmitted, Handler},
58};
59use sway_features::Feature;
60use sway_types::{Ident, Span, Spanned};
61
62use crate::language::{Inline, Purity};
63
64#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
65pub struct AttributeArg {
66 pub name: Ident,
67 pub value: Option<Literal>,
68 pub span: Span,
69}
70
71impl AttributeArg {
72 pub fn get_string(
77 &self,
78 handler: &Handler,
79 attribute: &Attribute,
80 ) -> Result<&String, ErrorEmitted> {
81 match &self.value {
82 Some(literal) => match literal {
83 Literal::String(lit_string) => Ok(&lit_string.parsed),
84 _ => Err(handler.emit_err(
85 ConvertParseTreeError::InvalidAttributeArgValueType {
86 span: literal.span(),
87 arg: self.name.clone(),
88 expected_type: "str",
89 received_type: literal.friendly_type_name(),
90 }
91 .into(),
92 )),
93 },
94 None => Err(handler.emit_err(
95 ConvertParseTreeError::InvalidAttributeArgExpectsValue {
96 attribute: attribute.name.clone(),
97 arg: (&self.name).into(),
98 value_span: None,
99 }
100 .into(),
101 )),
102 }
103 }
104
105 pub fn get_string_opt(&self, handler: &Handler) -> Result<Option<&String>, ErrorEmitted> {
108 match &self.value {
109 Some(literal) => match literal {
110 Literal::String(lit_string) => Ok(Some(&lit_string.parsed)),
111 _ => Err(handler.emit_err(
112 ConvertParseTreeError::InvalidAttributeArgValueType {
113 span: literal.span(),
114 arg: self.name.clone(),
115 expected_type: "str",
116 received_type: literal.friendly_type_name(),
117 }
118 .into(),
119 )),
120 },
121 None => Ok(None),
122 }
123 }
124
125 pub fn get_bool(&self, handler: &Handler, attribute: &Attribute) -> Result<bool, ErrorEmitted> {
130 match &self.value {
131 Some(literal) => match literal {
132 Literal::Bool(lit_bool) => Ok(lit_bool.kind.into()),
133 _ => Err(handler.emit_err(
134 ConvertParseTreeError::InvalidAttributeArgValueType {
135 span: literal.span(),
136 arg: self.name.clone(),
137 expected_type: "bool",
138 received_type: literal.friendly_type_name(),
139 }
140 .into(),
141 )),
142 },
143 None => Err(handler.emit_err(
144 ConvertParseTreeError::InvalidAttributeArgExpectsValue {
145 attribute: attribute.name.clone(),
146 arg: (&self.name).into(),
147 value_span: None,
148 }
149 .into(),
150 )),
151 }
152 }
153
154 pub fn is_allow_dead_code(&self) -> bool {
155 self.name.as_str() == ALLOW_DEAD_CODE_ARG_NAME
156 }
157
158 pub fn is_allow_deprecated(&self) -> bool {
159 self.name.as_str() == ALLOW_DEPRECATED_ARG_NAME
160 }
161
162 pub fn is_cfg_target(&self) -> bool {
163 self.name.as_str() == CFG_TARGET_ARG_NAME
164 }
165
166 pub fn is_cfg_program_type(&self) -> bool {
167 self.name.as_str() == CFG_PROGRAM_TYPE_ARG_NAME
168 }
169
170 pub fn is_cfg_experimental(&self) -> bool {
171 Feature::CFG.contains(&self.name.as_str())
172 }
173
174 pub fn is_deprecated_note(&self) -> bool {
175 self.name.as_str() == DEPRECATED_NOTE_ARG_NAME
176 }
177
178 pub fn is_test_should_revert(&self) -> bool {
179 self.name.as_str() == TEST_SHOULD_REVERT_ARG_NAME
180 }
181}
182
183impl Spanned for AttributeArg {
184 fn span(&self) -> Span {
185 self.span.clone()
186 }
187}
188
189#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
199pub struct Attribute {
200 pub direction: AttributeDirection,
204 pub name: Ident,
205 pub args: Vec<AttributeArg>,
206 pub span: Span,
207 pub kind: AttributeKind,
208}
209
210#[derive(Clone, Copy, Debug, Eq, PartialEq, Serialize, Deserialize)]
211pub enum AttributeDirection {
212 Inner,
213 Outer,
214}
215
216impl From<&AttributeHashKind> for AttributeDirection {
217 fn from(value: &AttributeHashKind) -> Self {
218 match value {
219 AttributeHashKind::Inner(_) => Self::Inner,
220 AttributeHashKind::Outer(_) => Self::Outer,
221 }
222 }
223}
224
225pub struct ArgsMultiplicity {
228 min: usize,
229 max: usize,
230}
231
232impl ArgsMultiplicity {
233 pub fn zero() -> Self {
234 Self { min: 0, max: 0 }
235 }
236 pub fn arbitrary() -> Self {
237 Self {
238 min: 0,
239 max: usize::MAX,
240 }
241 }
242 pub fn exactly(num: usize) -> Self {
243 Self { min: num, max: num }
244 }
245 pub fn at_least(num: usize) -> Self {
246 Self {
247 min: num,
248 max: usize::MAX,
249 }
250 }
251 pub fn at_most(num: usize) -> Self {
252 Self { min: 0, max: num }
253 }
254 pub fn between(min: usize, max: usize) -> Self {
255 assert!(
256 min <= max,
257 "min must be less than or equal to max; min was {min}, max was {max}"
258 );
259 Self { min, max }
260 }
261 pub fn contains(&self, value: usize) -> bool {
262 self.min <= value && value <= self.max
263 }
264}
265
266impl From<&ArgsMultiplicity> for (usize, usize) {
267 fn from(value: &ArgsMultiplicity) -> Self {
268 (value.min, value.max)
269 }
270}
271
272pub enum ExpectedArgs {
274 None,
276 Any,
280 MustBeIn(Vec<&'static str>),
283 ShouldBeIn(Vec<&'static str>),
286}
287
288impl ExpectedArgs {
289 pub(crate) fn args_names(&self) -> Vec<&'static str> {
293 match self {
294 ExpectedArgs::None | ExpectedArgs::Any => vec![],
295 ExpectedArgs::MustBeIn(expected_args) | ExpectedArgs::ShouldBeIn(expected_args) => {
296 expected_args.clone()
297 }
298 }
299 }
300}
301
302pub enum ArgsExpectValues {
315 Yes,
320 No,
325 Maybe,
330}
331
332#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash, Serialize, Deserialize)]
334pub enum AttributeKind {
335 Unknown,
342 DocComment,
343 Storage,
344 Inline,
345 Test,
346 Payable,
347 Allow,
348 Cfg,
349 Deprecated,
350 Fallback,
351 ErrorType,
352 Error,
353}
354
355#[derive(Debug, Clone, Copy, PartialEq, Eq)]
357pub enum TraitItemParent {
358 Abi,
359 Trait,
360}
361
362#[derive(Debug, Clone, Copy, PartialEq, Eq)]
364pub enum StructOrEnumField {
365 StructField,
366 EnumField,
367}
368
369impl AttributeKind {
370 pub fn from_attribute_name(name: &str) -> Self {
371 match name {
372 DOC_COMMENT_ATTRIBUTE_NAME => AttributeKind::DocComment,
373 STORAGE_ATTRIBUTE_NAME => AttributeKind::Storage,
374 INLINE_ATTRIBUTE_NAME => AttributeKind::Inline,
375 TEST_ATTRIBUTE_NAME => AttributeKind::Test,
376 PAYABLE_ATTRIBUTE_NAME => AttributeKind::Payable,
377 ALLOW_ATTRIBUTE_NAME => AttributeKind::Allow,
378 CFG_ATTRIBUTE_NAME => AttributeKind::Cfg,
379 DEPRECATED_ATTRIBUTE_NAME => AttributeKind::Deprecated,
380 FALLBACK_ATTRIBUTE_NAME => AttributeKind::Fallback,
381 ERROR_TYPE_ATTRIBUTE_NAME => AttributeKind::ErrorType,
382 ERROR_ATTRIBUTE_NAME => AttributeKind::Error,
383 _ => AttributeKind::Unknown,
384 }
385 }
386
387 pub fn allows_multiple(&self) -> bool {
397 use AttributeKind::*;
398 match self {
399 Unknown => true,
400 DocComment => true,
401 Storage => false,
402 Inline => false,
403 Test => false,
404 Payable => false,
405 Allow => true,
406 Cfg => true,
407 Deprecated => false,
408 Fallback => false,
409 ErrorType => false,
410 Error => false,
411 }
412 }
413}
414
415impl Attribute {
416 pub fn is_doc_comment(&self) -> bool {
417 self.kind == AttributeKind::DocComment
418 }
419
420 pub fn is_inner(&self) -> bool {
421 self.direction == AttributeDirection::Inner
422 }
423
424 pub fn is_outer(&self) -> bool {
425 self.direction == AttributeDirection::Outer
426 }
427
428 pub(crate) fn args_multiplicity(&self) -> ArgsMultiplicity {
429 use ArgsMultiplicity as Multiplicity;
430 use AttributeKind::*;
431 match self.kind {
432 Unknown => Multiplicity::arbitrary(),
433 DocComment => Multiplicity::exactly(1),
437 Storage => Multiplicity::between(1, 2),
439 Inline => Multiplicity::exactly(1),
441 Test => Multiplicity::at_most(1),
443 Payable => Multiplicity::zero(),
444 Allow => Multiplicity::at_least(1),
445 Cfg => Multiplicity::exactly(1),
446 Deprecated => Multiplicity::at_most(1),
448 Fallback => Multiplicity::zero(),
449 ErrorType => Multiplicity::zero(),
450 Error => Multiplicity::exactly(1),
451 }
452 }
453
454 pub(crate) fn check_args_multiplicity(&self, handler: &Handler) -> Result<(), ErrorEmitted> {
455 if !self.args_multiplicity().contains(self.args.len()) {
456 Err(handler.emit_err(
457 ConvertParseTreeError::InvalidAttributeArgsMultiplicity {
458 span: if self.args.is_empty() {
459 self.name.span()
460 } else {
461 Span::join(
462 self.args.first().unwrap().span(),
463 &self.args.last().unwrap().span,
464 )
465 },
466 attribute: self.name.clone(),
467 args_multiplicity: (&self.args_multiplicity()).into(),
468 num_of_args: self.args.len(),
469 }
470 .into(),
471 ))
472 } else {
473 Ok(())
474 }
475 }
476
477 pub(crate) fn can_have_arguments(&self) -> bool {
478 let args_multiplicity = self.args_multiplicity();
479 args_multiplicity.min != 0 || args_multiplicity.max != 0
480 }
481
482 pub(crate) fn expected_args(&self) -> ExpectedArgs {
483 use AttributeKind::*;
484 use ExpectedArgs::*;
485 match self.kind {
486 Unknown => Any,
487 DocComment => Any,
488 Storage => MustBeIn(vec![STORAGE_READ_ARG_NAME, STORAGE_WRITE_ARG_NAME]),
489 Inline => MustBeIn(vec![INLINE_ALWAYS_ARG_NAME, INLINE_NEVER_ARG_NAME]),
490 Test => MustBeIn(vec![TEST_SHOULD_REVERT_ARG_NAME]),
491 Payable => None,
492 Allow => ShouldBeIn(vec![ALLOW_DEAD_CODE_ARG_NAME, ALLOW_DEPRECATED_ARG_NAME]),
493 Cfg => {
494 let mut args = vec![
495 CFG_PROGRAM_TYPE_ARG_NAME,
497 CFG_TARGET_ARG_NAME,
498 ];
499 args.extend(Feature::CFG.iter().sorted());
500 MustBeIn(args)
501 }
502 Deprecated => MustBeIn(vec![DEPRECATED_NOTE_ARG_NAME]),
503 Fallback => None,
504 ErrorType => None,
505 Error => MustBeIn(vec![ERROR_M_ARG_NAME]),
506 }
507 }
508
509 pub(crate) fn args_expect_values(&self) -> ArgsExpectValues {
510 use ArgsExpectValues::*;
511 use AttributeKind::*;
512 match self.kind {
513 Unknown => Maybe,
514 DocComment => No,
516 Storage => No,
517 Inline => No,
518 Test => Maybe,
520 Payable => No,
521 Allow => No,
522 Cfg => Yes,
523 Deprecated => Yes,
525 Fallback => No,
526 ErrorType => No,
527 Error => Yes,
529 }
530 }
531
532 pub(crate) fn can_annotate_module_kind(&self) -> bool {
533 use AttributeKind::*;
534 match self.kind {
535 Unknown => false,
536 DocComment => self.direction == AttributeDirection::Inner,
537 Storage => false,
538 Inline => false,
539 Test => false,
540 Payable => false,
541 Allow => false,
542 Cfg => false,
543 Deprecated => false,
546 Fallback => false,
547 ErrorType => false,
548 Error => false,
549 }
550 }
551
552 pub(crate) fn can_annotate_item_kind(&self, item_kind: &ItemKind) -> bool {
553 if matches!(item_kind, ItemKind::Error(..)) {
564 return true;
565 }
566
567 use AttributeKind::*;
568 match self.kind {
569 Unknown => !matches!(item_kind, ItemKind::Submodule(_)),
570 DocComment => {
572 self.direction == AttributeDirection::Outer
573 && !matches!(item_kind, ItemKind::Submodule(_))
574 }
575 Storage => matches!(item_kind, ItemKind::Fn(_)),
576 Inline => matches!(item_kind, ItemKind::Fn(_)),
577 Test => matches!(item_kind, ItemKind::Fn(_)),
578 Payable => false,
579 Allow => !matches!(item_kind, ItemKind::Submodule(_)),
580 Cfg => !matches!(item_kind, ItemKind::Submodule(_)),
581 Deprecated => match item_kind {
583 ItemKind::Submodule(_) => false,
584 ItemKind::Use(_) => false,
585 ItemKind::Struct(_) => true,
586 ItemKind::Enum(_) => true,
587 ItemKind::Fn(_) => true,
588 ItemKind::Trait(_) => false,
589 ItemKind::Impl(_) => false,
590 ItemKind::Abi(_) => false,
591 ItemKind::Const(_) => true,
592 ItemKind::Storage(_) => false,
593 ItemKind::Configurable(_) => false,
596 ItemKind::TypeAlias(_) => false,
597 ItemKind::Error(_, _) => true,
598 },
599 Fallback => matches!(item_kind, ItemKind::Fn(_)),
600 ErrorType => matches!(item_kind, ItemKind::Enum(_)),
601 Error => false,
602 }
603 }
604
605 pub(crate) fn can_annotate_struct_or_enum_field(
610 &self,
611 struct_or_enum_field: StructOrEnumField,
612 ) -> bool {
613 use AttributeKind::*;
614 match self.kind {
615 Unknown => true,
616 DocComment => self.direction == AttributeDirection::Outer,
617 Storage => false,
618 Inline => false,
619 Test => false,
620 Payable => false,
621 Allow => true,
622 Cfg => true,
623 Deprecated => true,
624 Fallback => false,
625 ErrorType => false,
626 Error => struct_or_enum_field == StructOrEnumField::EnumField,
627 }
628 }
629
630 pub(crate) fn can_annotate_abi_or_trait_item(
631 &self,
632 item: &ItemTraitItem,
633 parent: TraitItemParent,
634 ) -> bool {
635 use AttributeKind::*;
636 match self.kind {
637 Unknown => true,
638 DocComment => self.direction == AttributeDirection::Outer,
639 Storage => matches!(item, ItemTraitItem::Fn(..)),
640 Inline => false,
643 Test => false,
644 Payable => parent == TraitItemParent::Abi && matches!(item, ItemTraitItem::Fn(..)),
645 Allow => true,
646 Cfg => true,
647 Deprecated => false,
649 Fallback => false,
650 ErrorType => false,
651 Error => false,
652 }
653 }
654
655 pub(crate) fn can_annotate_impl_item(
656 &self,
657 item: &ItemImplItem,
658 parent: ImplItemParent,
659 ) -> bool {
660 use AttributeKind::*;
661 match self.kind {
662 Unknown => true,
663 DocComment => self.direction == AttributeDirection::Outer,
664 Storage => matches!(item, ItemImplItem::Fn(..)),
665 Inline => matches!(item, ItemImplItem::Fn(..)),
666 Test => false,
667 Payable => parent == ImplItemParent::Contract,
668 Allow => true,
669 Cfg => true,
670 Deprecated => !matches!(item, ItemImplItem::Type(_)),
671 Fallback => false,
672 ErrorType => false,
673 Error => false,
674 }
675 }
676
677 pub(crate) fn can_annotate_abi_or_trait_item_fn(
678 &self,
679 abi_or_trait_item: TraitItemParent,
680 ) -> bool {
681 use AttributeKind::*;
682 match self.kind {
683 Unknown => true,
684 DocComment => self.direction == AttributeDirection::Outer,
685 Storage => true,
686 Inline => true,
687 Test => false,
688 Payable => abi_or_trait_item == TraitItemParent::Abi,
689 Allow => true,
690 Cfg => true,
691 Deprecated => true,
692 Fallback => false,
693 ErrorType => false,
694 Error => false,
695 }
696 }
697
698 pub(crate) fn can_annotate_storage_entry(&self) -> bool {
699 use AttributeKind::*;
700 match self.kind {
701 Unknown => true,
702 DocComment => self.direction == AttributeDirection::Outer,
703 Storage => false,
704 Inline => false,
705 Test => false,
706 Payable => false,
707 Allow => true,
708 Cfg => true,
709 Deprecated => false,
711 Fallback => false,
712 ErrorType => false,
713 Error => false,
714 }
715 }
716
717 pub(crate) fn can_annotate_configurable_field(&self) -> bool {
718 use AttributeKind::*;
719 match self.kind {
720 Unknown => true,
721 DocComment => self.direction == AttributeDirection::Outer,
722 Storage => false,
723 Inline => false,
724 Test => false,
725 Payable => false,
726 Allow => true,
727 Cfg => true,
728 Deprecated => true,
729 Fallback => false,
730 ErrorType => false,
731 Error => false,
732 }
733 }
734
735 pub(crate) fn can_only_annotate_help(&self, target_friendly_name: &str) -> Vec<&'static str> {
736 use AttributeKind::*;
739 let help = match self.kind {
740 Unknown => vec![],
741 DocComment => match self.direction {
742 AttributeDirection::Inner => vec![
743 "Inner doc comments (`//!`) can only document modules and must be",
744 "at the beginning of the module file, before the module kind.",
745 ],
746 AttributeDirection::Outer => if target_friendly_name.starts_with("module kind") {
747 vec![
748 "To document modules, use inner doc comments (`//!`). E.g.:",
749 "//! This doc comment documents a module.",
750 ]
751 } else {
752 vec![]
753 },
754 },
755 Storage => {
756 if target_friendly_name == "function signature" {
757 vec![
758 "\"storage\" attribute can only annotate functions that have an implementation.",
759 "Function signatures in ABI and trait declarations do not have implementations.",
760 ]
761 } else {
762 vec![
763 "\"storage\" attribute can only annotate functions.",
764 ]
765 }
766 },
767 Inline => vec!["\"inline\" attribute can only annotate functions."],
768 Test => vec!["\"test\" attribute can only annotate module functions."],
769 Payable => vec![
770 "\"payable\" attribute can only annotate:",
771 " - ABI function signatures and their implementations in contracts,",
772 " - provided ABI functions.",
773 ],
774 Allow => vec![],
775 Cfg => vec![],
776 Deprecated => vec![
778 "\"deprecated\" attribute is currently not implemented for all elements that could be deprecated.",
779 ],
780 Fallback => vec!["\"fallback\" attribute can only annotate module functions in a contract module."],
781 ErrorType => vec!["\"error_type\" attribute can only annotate enums."],
782 Error => vec!["\"error\" attribute can only annotate enum variants of enums annotated with the \"error_type\" attribute."],
783 };
784
785 if help.is_empty() && target_friendly_name.starts_with("module kind") {
786 vec!["Annotating module kinds (contract, script, predicate, or library) is currently not implemented."]
787 } else {
788 help
789 }
790 }
791}
792
793#[derive(Default, Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
805pub struct Attributes {
806 attributes: Arc<Vec<Attribute>>,
816 deprecated_attr_index: Option<usize>,
820}
821
822impl Attributes {
823 pub fn new(attribute_decls: &[AttributeDecl]) -> Attributes {
824 let mut attributes: Vec<Attribute> = vec![];
825 for attr_decl in attribute_decls {
826 let attrs = attr_decl.attribute.get().into_iter();
827 for attr in attrs {
828 let name = attr.name.as_str();
829 let args = attr
830 .args
831 .as_ref()
832 .map(|parens| {
833 parens
834 .get()
835 .into_iter()
836 .cloned()
837 .map(|arg| AttributeArg {
838 name: arg.name.clone(),
839 value: arg.value.clone(),
840 span: arg.span(),
841 })
842 .collect()
843 })
844 .unwrap_or_default();
845
846 let attribute = Attribute {
847 direction: (&attr_decl.hash_kind).into(),
848 name: attr.name.clone(),
849 args,
850 span: attr_decl.span(),
851 kind: AttributeKind::from_attribute_name(name),
852 };
853
854 attributes.push(attribute);
855 }
856 }
857
858 Attributes {
859 deprecated_attr_index: attributes
860 .iter()
861 .rposition(|attr| attr.kind == AttributeKind::Deprecated),
862 attributes: Arc::new(attributes),
863 }
864 }
865
866 pub fn is_empty(&self) -> bool {
867 self.attributes.is_empty()
868 }
869
870 pub fn first(&self) -> Option<&Attribute> {
872 self.attributes.first()
873 }
874
875 pub fn known_attribute_names(&self) -> &'static [&'static str] {
876 KNOWN_ATTRIBUTE_NAMES
877 }
878
879 pub fn all(&self) -> impl Iterator<Item = &Attribute> {
880 self.attributes.iter()
881 }
882
883 pub fn all_as_slice(&self) -> &[Attribute] {
884 self.attributes.as_slice()
885 }
886
887 pub fn all_by_kind<F>(&self, predicate: F) -> IndexMap<AttributeKind, Vec<&Attribute>>
888 where
889 F: Fn(&&Attribute) -> bool,
890 {
891 let mut result = IndexMap::<_, Vec<&Attribute>>::new();
892 for attr in self.attributes.iter().filter(predicate) {
893 result.entry(attr.kind).or_default().push(attr);
894 }
895 result
896 }
897
898 pub fn of_kind(&self, kind: AttributeKind) -> impl Iterator<Item = &Attribute> {
899 self.attributes.iter().filter(move |attr| attr.kind == kind)
900 }
901
902 pub fn has_any_of_kind(&self, kind: AttributeKind) -> bool {
903 self.of_kind(kind).any(|_| true)
904 }
905
906 pub fn unknown(&self) -> impl Iterator<Item = &Attribute> {
907 self.attributes
908 .iter()
909 .filter(|attr| attr.kind == AttributeKind::Unknown)
910 }
911
912 pub fn has_allow_dead_code(&self) -> bool {
913 self.has_allow(|arg| arg.is_allow_dead_code())
914 }
915
916 pub fn has_allow_deprecated(&self) -> bool {
917 self.has_allow(|arg| arg.is_allow_deprecated())
918 }
919
920 fn has_allow(&self, arg_filter: impl Fn(&AttributeArg) -> bool) -> bool {
921 self.of_kind(AttributeKind::Allow)
922 .flat_map(|attribute| &attribute.args)
923 .any(arg_filter)
924 }
925
926 pub fn has_error_type(&self) -> bool {
927 self.of_kind(AttributeKind::ErrorType).any(|_| true)
928 }
929
930 pub fn has_error(&self) -> bool {
931 self.of_kind(AttributeKind::Error).any(|_| true)
932 }
933
934 pub fn inline(&self) -> Option<Inline> {
937 match self
941 .of_kind(AttributeKind::Inline)
942 .last()?
943 .args
944 .last()?
945 .name
946 .as_str()
947 {
948 INLINE_NEVER_ARG_NAME => Some(Inline::Never),
949 INLINE_ALWAYS_ARG_NAME => Some(Inline::Always),
950 _ => None,
951 }
952 }
953
954 pub fn purity(&self) -> Purity {
957 let Some(storage_attr) = self.of_kind(AttributeKind::Storage).last() else {
960 return Purity::Pure;
961 };
962
963 let mut purity = Purity::Pure;
964
965 let mut add_impurity = |new_impurity, counter_impurity| {
966 if purity == Purity::Pure {
967 purity = new_impurity;
968 } else if purity == counter_impurity {
969 purity = Purity::ReadsWrites;
970 }
971 };
972
973 for arg in storage_attr.args.iter() {
974 match arg.name.as_str() {
975 STORAGE_READ_ARG_NAME => add_impurity(Purity::Reads, Purity::Writes),
976 STORAGE_WRITE_ARG_NAME => add_impurity(Purity::Writes, Purity::Reads),
977 _ => {}
978 }
979 }
980
981 purity
982 }
983
984 pub fn deprecated(&self) -> Option<&Attribute> {
987 self.deprecated_attr_index
988 .map(|index| &self.attributes[index])
989 }
990
991 pub fn test(&self) -> Option<&Attribute> {
994 self.of_kind(AttributeKind::Test).last()
996 }
997
998 pub fn error(&self) -> Option<&Attribute> {
1001 self.of_kind(AttributeKind::Error).last()
1003 }
1004}
1005
1006pub struct AllowDeprecatedEnterToken {
1007 diff: i32,
1008}
1009
1010#[derive(Default)]
1011pub struct AllowDeprecatedState {
1012 allowed: u32,
1013}
1014impl AllowDeprecatedState {
1015 pub(crate) fn enter(&mut self, attributes: Attributes) -> AllowDeprecatedEnterToken {
1016 if attributes.has_allow_deprecated() {
1017 self.allowed += 1;
1018
1019 AllowDeprecatedEnterToken { diff: -1 }
1020 } else {
1021 AllowDeprecatedEnterToken { diff: 0 }
1022 }
1023 }
1024
1025 pub(crate) fn exit(&mut self, token: AllowDeprecatedEnterToken) {
1026 self.allowed = self.allowed.saturating_add_signed(token.diff);
1027 }
1028
1029 pub(crate) fn is_allowed(&self) -> bool {
1030 self.allowed > 0
1031 }
1032}