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 pub fn is_error_message(&self) -> bool {
183 self.name.as_str() == ERROR_M_ARG_NAME
184 }
185}
186
187impl Spanned for AttributeArg {
188 fn span(&self) -> Span {
189 self.span.clone()
190 }
191}
192
193#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
203pub struct Attribute {
204 pub direction: AttributeDirection,
208 pub name: Ident,
209 pub args: Vec<AttributeArg>,
210 pub span: Span,
211 pub kind: AttributeKind,
212}
213
214#[derive(Clone, Copy, Debug, Eq, PartialEq, Serialize, Deserialize)]
215pub enum AttributeDirection {
216 Inner,
217 Outer,
218}
219
220impl From<&AttributeHashKind> for AttributeDirection {
221 fn from(value: &AttributeHashKind) -> Self {
222 match value {
223 AttributeHashKind::Inner(_) => Self::Inner,
224 AttributeHashKind::Outer(_) => Self::Outer,
225 }
226 }
227}
228
229pub struct ArgsMultiplicity {
232 min: usize,
233 max: usize,
234}
235
236impl ArgsMultiplicity {
237 pub fn zero() -> Self {
238 Self { min: 0, max: 0 }
239 }
240 pub fn arbitrary() -> Self {
241 Self {
242 min: 0,
243 max: usize::MAX,
244 }
245 }
246 pub fn exactly(num: usize) -> Self {
247 Self { min: num, max: num }
248 }
249 pub fn at_least(num: usize) -> Self {
250 Self {
251 min: num,
252 max: usize::MAX,
253 }
254 }
255 pub fn at_most(num: usize) -> Self {
256 Self { min: 0, max: num }
257 }
258 pub fn between(min: usize, max: usize) -> Self {
259 assert!(
260 min <= max,
261 "min must be less than or equal to max; min was {min}, max was {max}"
262 );
263 Self { min, max }
264 }
265 pub fn contains(&self, value: usize) -> bool {
266 self.min <= value && value <= self.max
267 }
268}
269
270impl From<&ArgsMultiplicity> for (usize, usize) {
271 fn from(value: &ArgsMultiplicity) -> Self {
272 (value.min, value.max)
273 }
274}
275
276pub enum ExpectedArgs {
278 None,
280 Any,
284 MustBeIn(Vec<&'static str>),
287 ShouldBeIn(Vec<&'static str>),
290}
291
292impl ExpectedArgs {
293 pub(crate) fn args_names(&self) -> Vec<&'static str> {
297 match self {
298 ExpectedArgs::None | ExpectedArgs::Any => vec![],
299 ExpectedArgs::MustBeIn(expected_args) | ExpectedArgs::ShouldBeIn(expected_args) => {
300 expected_args.clone()
301 }
302 }
303 }
304}
305
306pub enum ArgsExpectValues {
319 Yes,
324 No,
329 Maybe,
334}
335
336#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash, Serialize, Deserialize)]
338pub enum AttributeKind {
339 Unknown,
346 DocComment,
347 Storage,
348 Inline,
349 Test,
350 Payable,
351 Allow,
352 Cfg,
353 Deprecated,
354 Fallback,
355 ErrorType,
356 Error,
357}
358
359#[derive(Debug, Clone, Copy, PartialEq, Eq)]
361pub enum TraitItemParent {
362 Abi,
363 Trait,
364}
365
366#[derive(Debug, Clone, Copy, PartialEq, Eq)]
368pub enum StructOrEnumField {
369 StructField,
370 EnumField,
371}
372
373impl AttributeKind {
374 pub fn from_attribute_name(name: &str) -> Self {
375 match name {
376 DOC_COMMENT_ATTRIBUTE_NAME => AttributeKind::DocComment,
377 STORAGE_ATTRIBUTE_NAME => AttributeKind::Storage,
378 INLINE_ATTRIBUTE_NAME => AttributeKind::Inline,
379 TEST_ATTRIBUTE_NAME => AttributeKind::Test,
380 PAYABLE_ATTRIBUTE_NAME => AttributeKind::Payable,
381 ALLOW_ATTRIBUTE_NAME => AttributeKind::Allow,
382 CFG_ATTRIBUTE_NAME => AttributeKind::Cfg,
383 DEPRECATED_ATTRIBUTE_NAME => AttributeKind::Deprecated,
384 FALLBACK_ATTRIBUTE_NAME => AttributeKind::Fallback,
385 ERROR_TYPE_ATTRIBUTE_NAME => AttributeKind::ErrorType,
386 ERROR_ATTRIBUTE_NAME => AttributeKind::Error,
387 _ => AttributeKind::Unknown,
388 }
389 }
390
391 pub fn allows_multiple(&self) -> bool {
401 use AttributeKind::*;
402 match self {
403 Unknown => true,
404 DocComment => true,
405 Storage => false,
406 Inline => false,
407 Test => false,
408 Payable => false,
409 Allow => true,
410 Cfg => true,
411 Deprecated => false,
412 Fallback => false,
413 ErrorType => false,
414 Error => false,
415 }
416 }
417}
418
419impl Attribute {
420 pub fn is_doc_comment(&self) -> bool {
421 self.kind == AttributeKind::DocComment
422 }
423
424 pub fn is_inner(&self) -> bool {
425 self.direction == AttributeDirection::Inner
426 }
427
428 pub fn is_outer(&self) -> bool {
429 self.direction == AttributeDirection::Outer
430 }
431
432 pub(crate) fn args_multiplicity(&self) -> ArgsMultiplicity {
433 use ArgsMultiplicity as Multiplicity;
434 use AttributeKind::*;
435 match self.kind {
436 Unknown => Multiplicity::arbitrary(),
437 DocComment => Multiplicity::exactly(1),
441 Storage => Multiplicity::between(1, 2),
443 Inline => Multiplicity::exactly(1),
445 Test => Multiplicity::at_most(1),
447 Payable => Multiplicity::zero(),
448 Allow => Multiplicity::at_least(1),
449 Cfg => Multiplicity::exactly(1),
450 Deprecated => Multiplicity::at_most(1),
452 Fallback => Multiplicity::zero(),
453 ErrorType => Multiplicity::zero(),
454 Error => Multiplicity::exactly(1),
455 }
456 }
457
458 pub(crate) fn check_args_multiplicity(&self, handler: &Handler) -> Result<(), ErrorEmitted> {
459 if !self.args_multiplicity().contains(self.args.len()) {
460 Err(handler.emit_err(
461 ConvertParseTreeError::InvalidAttributeArgsMultiplicity {
462 span: if self.args.is_empty() {
463 self.name.span()
464 } else {
465 Span::join(
466 self.args.first().unwrap().span(),
467 &self.args.last().unwrap().span,
468 )
469 },
470 attribute: self.name.clone(),
471 args_multiplicity: (&self.args_multiplicity()).into(),
472 num_of_args: self.args.len(),
473 }
474 .into(),
475 ))
476 } else {
477 Ok(())
478 }
479 }
480
481 pub(crate) fn can_have_arguments(&self) -> bool {
482 let args_multiplicity = self.args_multiplicity();
483 args_multiplicity.min != 0 || args_multiplicity.max != 0
484 }
485
486 pub(crate) fn expected_args(&self) -> ExpectedArgs {
487 use AttributeKind::*;
488 use ExpectedArgs::*;
489 match self.kind {
490 Unknown => Any,
491 DocComment => Any,
492 Storage => MustBeIn(vec![STORAGE_READ_ARG_NAME, STORAGE_WRITE_ARG_NAME]),
493 Inline => MustBeIn(vec![INLINE_ALWAYS_ARG_NAME, INLINE_NEVER_ARG_NAME]),
494 Test => MustBeIn(vec![TEST_SHOULD_REVERT_ARG_NAME]),
495 Payable => None,
496 Allow => ShouldBeIn(vec![ALLOW_DEAD_CODE_ARG_NAME, ALLOW_DEPRECATED_ARG_NAME]),
497 Cfg => {
498 let mut args = vec![
499 CFG_PROGRAM_TYPE_ARG_NAME,
501 CFG_TARGET_ARG_NAME,
502 ];
503 args.extend(Feature::CFG.iter().sorted());
504 MustBeIn(args)
505 }
506 Deprecated => MustBeIn(vec![DEPRECATED_NOTE_ARG_NAME]),
507 Fallback => None,
508 ErrorType => None,
509 Error => MustBeIn(vec![ERROR_M_ARG_NAME]),
510 }
511 }
512
513 pub(crate) fn args_expect_values(&self) -> ArgsExpectValues {
514 use ArgsExpectValues::*;
515 use AttributeKind::*;
516 match self.kind {
517 Unknown => Maybe,
518 DocComment => No,
520 Storage => No,
521 Inline => No,
522 Test => Maybe,
524 Payable => No,
525 Allow => No,
526 Cfg => Yes,
527 Deprecated => Yes,
529 Fallback => No,
530 ErrorType => No,
531 Error => Yes,
533 }
534 }
535
536 pub(crate) fn can_annotate_module_kind(&self) -> bool {
537 use AttributeKind::*;
538 match self.kind {
539 Unknown => false,
540 DocComment => self.direction == AttributeDirection::Inner,
541 Storage => false,
542 Inline => false,
543 Test => false,
544 Payable => false,
545 Allow => false,
546 Cfg => false,
547 Deprecated => false,
550 Fallback => false,
551 ErrorType => false,
552 Error => false,
553 }
554 }
555
556 pub(crate) fn can_annotate_item_kind(&self, item_kind: &ItemKind) -> bool {
557 if matches!(item_kind, ItemKind::Error(..)) {
568 return true;
569 }
570
571 use AttributeKind::*;
572 match self.kind {
573 Unknown => !matches!(item_kind, ItemKind::Submodule(_)),
574 DocComment => {
576 self.direction == AttributeDirection::Outer
577 && !matches!(item_kind, ItemKind::Submodule(_))
578 }
579 Storage => matches!(item_kind, ItemKind::Fn(_)),
580 Inline => matches!(item_kind, ItemKind::Fn(_)),
581 Test => matches!(item_kind, ItemKind::Fn(_)),
582 Payable => false,
583 Allow => !matches!(item_kind, ItemKind::Submodule(_)),
584 Cfg => !matches!(item_kind, ItemKind::Submodule(_)),
585 Deprecated => match item_kind {
587 ItemKind::Submodule(_) => false,
588 ItemKind::Use(_) => false,
589 ItemKind::Struct(_) => true,
590 ItemKind::Enum(_) => true,
591 ItemKind::Fn(_) => true,
592 ItemKind::Trait(_) => false,
593 ItemKind::Impl(_) => false,
594 ItemKind::Abi(_) => false,
595 ItemKind::Const(_) => true,
596 ItemKind::Storage(_) => false,
597 ItemKind::Configurable(_) => false,
600 ItemKind::TypeAlias(_) => false,
601 ItemKind::Error(_, _) => true,
602 },
603 Fallback => matches!(item_kind, ItemKind::Fn(_)),
604 ErrorType => matches!(item_kind, ItemKind::Enum(_)),
605 Error => false,
606 }
607 }
608
609 pub(crate) fn can_annotate_struct_or_enum_field(
614 &self,
615 struct_or_enum_field: StructOrEnumField,
616 ) -> bool {
617 use AttributeKind::*;
618 match self.kind {
619 Unknown => true,
620 DocComment => self.direction == AttributeDirection::Outer,
621 Storage => false,
622 Inline => false,
623 Test => false,
624 Payable => false,
625 Allow => true,
626 Cfg => true,
627 Deprecated => true,
628 Fallback => false,
629 ErrorType => false,
630 Error => struct_or_enum_field == StructOrEnumField::EnumField,
631 }
632 }
633
634 pub(crate) fn can_annotate_abi_or_trait_item(
635 &self,
636 item: &ItemTraitItem,
637 parent: TraitItemParent,
638 ) -> bool {
639 use AttributeKind::*;
640 match self.kind {
641 Unknown => true,
642 DocComment => self.direction == AttributeDirection::Outer,
643 Storage => matches!(item, ItemTraitItem::Fn(..)),
644 Inline => false,
647 Test => false,
648 Payable => parent == TraitItemParent::Abi && matches!(item, ItemTraitItem::Fn(..)),
649 Allow => true,
650 Cfg => true,
651 Deprecated => false,
653 Fallback => false,
654 ErrorType => false,
655 Error => false,
656 }
657 }
658
659 pub(crate) fn can_annotate_impl_item(
660 &self,
661 item: &ItemImplItem,
662 parent: ImplItemParent,
663 ) -> bool {
664 use AttributeKind::*;
665 match self.kind {
666 Unknown => true,
667 DocComment => self.direction == AttributeDirection::Outer,
668 Storage => matches!(item, ItemImplItem::Fn(..)),
669 Inline => matches!(item, ItemImplItem::Fn(..)),
670 Test => false,
671 Payable => parent == ImplItemParent::Contract,
672 Allow => true,
673 Cfg => true,
674 Deprecated => !matches!(item, ItemImplItem::Type(_)),
675 Fallback => false,
676 ErrorType => false,
677 Error => false,
678 }
679 }
680
681 pub(crate) fn can_annotate_abi_or_trait_item_fn(
682 &self,
683 abi_or_trait_item: TraitItemParent,
684 ) -> bool {
685 use AttributeKind::*;
686 match self.kind {
687 Unknown => true,
688 DocComment => self.direction == AttributeDirection::Outer,
689 Storage => true,
690 Inline => true,
691 Test => false,
692 Payable => abi_or_trait_item == TraitItemParent::Abi,
693 Allow => true,
694 Cfg => true,
695 Deprecated => true,
696 Fallback => false,
697 ErrorType => false,
698 Error => false,
699 }
700 }
701
702 pub(crate) fn can_annotate_storage_entry(&self) -> bool {
703 use AttributeKind::*;
704 match self.kind {
705 Unknown => true,
706 DocComment => self.direction == AttributeDirection::Outer,
707 Storage => false,
708 Inline => false,
709 Test => false,
710 Payable => false,
711 Allow => true,
712 Cfg => true,
713 Deprecated => false,
715 Fallback => false,
716 ErrorType => false,
717 Error => false,
718 }
719 }
720
721 pub(crate) fn can_annotate_configurable_field(&self) -> bool {
722 use AttributeKind::*;
723 match self.kind {
724 Unknown => true,
725 DocComment => self.direction == AttributeDirection::Outer,
726 Storage => false,
727 Inline => false,
728 Test => false,
729 Payable => false,
730 Allow => true,
731 Cfg => true,
732 Deprecated => true,
733 Fallback => false,
734 ErrorType => false,
735 Error => false,
736 }
737 }
738
739 pub(crate) fn can_only_annotate_help(&self, target_friendly_name: &str) -> Vec<&'static str> {
740 use AttributeKind::*;
743 let help = match self.kind {
744 Unknown => vec![],
745 DocComment => match self.direction {
746 AttributeDirection::Inner => vec![
747 "Inner doc comments (`//!`) can only document modules and must be",
748 "at the beginning of the module file, before the module kind.",
749 ],
750 AttributeDirection::Outer => if target_friendly_name.starts_with("module kind") {
751 vec![
752 "To document modules, use inner doc comments (`//!`). E.g.:",
753 "//! This doc comment documents a module.",
754 ]
755 } else {
756 vec![]
757 },
758 },
759 Storage => {
760 if target_friendly_name == "function signature" {
761 vec![
762 "\"storage\" attribute can only annotate functions that have an implementation.",
763 "Function signatures in ABI and trait declarations do not have implementations.",
764 ]
765 } else {
766 vec![
767 "\"storage\" attribute can only annotate functions.",
768 ]
769 }
770 },
771 Inline => vec!["\"inline\" attribute can only annotate functions."],
772 Test => vec!["\"test\" attribute can only annotate module functions."],
773 Payable => vec![
774 "\"payable\" attribute can only annotate:",
775 " - ABI function signatures and their implementations in contracts,",
776 " - provided ABI functions.",
777 ],
778 Allow => vec![],
779 Cfg => vec![],
780 Deprecated => vec![
782 "\"deprecated\" attribute is currently not implemented for all elements that could be deprecated.",
783 ],
784 Fallback => vec!["\"fallback\" attribute can only annotate module functions in a contract module."],
785 ErrorType => vec!["\"error_type\" attribute can only annotate enums."],
786 Error => vec!["\"error\" attribute can only annotate enum variants of enums annotated with the \"error_type\" attribute."],
787 };
788
789 if help.is_empty() && target_friendly_name.starts_with("module kind") {
790 vec!["Annotating module kinds (contract, script, predicate, or library) is currently not implemented."]
791 } else {
792 help
793 }
794 }
795}
796
797#[derive(Default, Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
809pub struct Attributes {
810 attributes: Arc<Vec<Attribute>>,
820 deprecated_attr_index: Option<usize>,
824}
825
826impl Attributes {
827 pub fn new(attribute_decls: &[AttributeDecl]) -> Attributes {
828 let mut attributes: Vec<Attribute> = vec![];
829 for attr_decl in attribute_decls {
830 let attrs = attr_decl.attribute.get().into_iter();
831 for attr in attrs {
832 let name = attr.name.as_str();
833 let args = attr
834 .args
835 .as_ref()
836 .map(|parens| {
837 parens
838 .get()
839 .into_iter()
840 .cloned()
841 .map(|arg| AttributeArg {
842 name: arg.name.clone(),
843 value: arg.value.clone(),
844 span: arg.span(),
845 })
846 .collect()
847 })
848 .unwrap_or_default();
849
850 let attribute = Attribute {
851 direction: (&attr_decl.hash_kind).into(),
852 name: attr.name.clone(),
853 args,
854 span: attr_decl.span(),
855 kind: AttributeKind::from_attribute_name(name),
856 };
857
858 attributes.push(attribute);
859 }
860 }
861
862 Attributes {
863 deprecated_attr_index: attributes
864 .iter()
865 .rposition(|attr| attr.kind == AttributeKind::Deprecated),
866 attributes: Arc::new(attributes),
867 }
868 }
869
870 pub fn is_empty(&self) -> bool {
871 self.attributes.is_empty()
872 }
873
874 pub fn first(&self) -> Option<&Attribute> {
876 self.attributes.first()
877 }
878
879 pub fn known_attribute_names(&self) -> &'static [&'static str] {
880 KNOWN_ATTRIBUTE_NAMES
881 }
882
883 pub fn all(&self) -> impl Iterator<Item = &Attribute> {
884 self.attributes.iter()
885 }
886
887 pub fn all_as_slice(&self) -> &[Attribute] {
888 self.attributes.as_slice()
889 }
890
891 pub fn all_by_kind<F>(&self, predicate: F) -> IndexMap<AttributeKind, Vec<&Attribute>>
892 where
893 F: Fn(&&Attribute) -> bool,
894 {
895 let mut result = IndexMap::<_, Vec<&Attribute>>::new();
896 for attr in self.attributes.iter().filter(predicate) {
897 result.entry(attr.kind).or_default().push(attr);
898 }
899 result
900 }
901
902 pub fn of_kind(&self, kind: AttributeKind) -> impl Iterator<Item = &Attribute> {
903 self.attributes.iter().filter(move |attr| attr.kind == kind)
904 }
905
906 pub fn has_any_of_kind(&self, kind: AttributeKind) -> bool {
907 self.of_kind(kind).any(|_| true)
908 }
909
910 pub fn unknown(&self) -> impl Iterator<Item = &Attribute> {
911 self.attributes
912 .iter()
913 .filter(|attr| attr.kind == AttributeKind::Unknown)
914 }
915
916 pub fn has_allow_dead_code(&self) -> bool {
917 self.has_allow(|arg| arg.is_allow_dead_code())
918 }
919
920 pub fn has_allow_deprecated(&self) -> bool {
921 self.has_allow(|arg| arg.is_allow_deprecated())
922 }
923
924 fn has_allow(&self, arg_filter: impl Fn(&AttributeArg) -> bool) -> bool {
925 self.of_kind(AttributeKind::Allow)
926 .flat_map(|attribute| &attribute.args)
927 .any(arg_filter)
928 }
929
930 pub fn has_error_type(&self) -> bool {
931 self.of_kind(AttributeKind::ErrorType).any(|_| true)
932 }
933
934 pub fn has_error(&self) -> bool {
935 self.of_kind(AttributeKind::Error).any(|_| true)
936 }
937
938 pub fn inline(&self) -> Option<Inline> {
941 match self
945 .of_kind(AttributeKind::Inline)
946 .last()?
947 .args
948 .last()?
949 .name
950 .as_str()
951 {
952 INLINE_NEVER_ARG_NAME => Some(Inline::Never),
953 INLINE_ALWAYS_ARG_NAME => Some(Inline::Always),
954 _ => None,
955 }
956 }
957
958 pub fn purity(&self) -> Purity {
961 let Some(storage_attr) = self.of_kind(AttributeKind::Storage).last() else {
964 return Purity::Pure;
965 };
966
967 let mut purity = Purity::Pure;
968
969 let mut add_impurity = |new_impurity, counter_impurity| {
970 if purity == Purity::Pure {
971 purity = new_impurity;
972 } else if purity == counter_impurity {
973 purity = Purity::ReadsWrites;
974 }
975 };
976
977 for arg in storage_attr.args.iter() {
978 match arg.name.as_str() {
979 STORAGE_READ_ARG_NAME => add_impurity(Purity::Reads, Purity::Writes),
980 STORAGE_WRITE_ARG_NAME => add_impurity(Purity::Writes, Purity::Reads),
981 _ => {}
982 }
983 }
984
985 purity
986 }
987
988 pub fn deprecated(&self) -> Option<&Attribute> {
991 self.deprecated_attr_index
992 .map(|index| &self.attributes[index])
993 }
994
995 pub fn test(&self) -> Option<&Attribute> {
998 self.of_kind(AttributeKind::Test).last()
1000 }
1001
1002 pub fn error(&self) -> Option<&Attribute> {
1005 self.of_kind(AttributeKind::Error).last()
1007 }
1008
1009 pub fn error_message(&self) -> Option<&String> {
1015 self.error().and_then(|error_attr| {
1017 error_attr
1018 .args
1019 .iter()
1020 .filter(|arg| arg.is_error_message())
1021 .next_back()
1022 .and_then(|arg| arg.get_string_opt(&Handler::default()).ok().flatten())
1023 })
1024 }
1025}
1026
1027pub struct AllowDeprecatedEnterToken {
1028 diff: i32,
1029}
1030
1031#[derive(Default)]
1032pub struct AllowDeprecatedState {
1033 allowed: u32,
1034}
1035impl AllowDeprecatedState {
1036 pub(crate) fn enter(&mut self, attributes: Attributes) -> AllowDeprecatedEnterToken {
1037 if attributes.has_allow_deprecated() {
1038 self.allowed += 1;
1039
1040 AllowDeprecatedEnterToken { diff: -1 }
1041 } else {
1042 AllowDeprecatedEnterToken { diff: 0 }
1043 }
1044 }
1045
1046 pub(crate) fn exit(&mut self, token: AllowDeprecatedEnterToken) {
1047 self.allowed = self.allowed.saturating_add_signed(token.diff);
1048 }
1049
1050 pub(crate) fn is_allowed(&self) -> bool {
1051 self.allowed > 0
1052 }
1053}