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, Trace};
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 Trace,
358 AbiName,
359 Event,
360 Indexed,
361}
362
363#[derive(Debug, Clone, Copy, PartialEq, Eq)]
365pub enum TraitItemParent {
366 Abi,
367 Trait,
368}
369
370#[derive(Debug, Clone, Copy, PartialEq, Eq)]
372pub enum StructOrEnumField {
373 StructField,
374 EnumField,
375}
376
377impl AttributeKind {
378 pub fn from_attribute_name(name: &str) -> Self {
379 match name {
380 DOC_COMMENT_ATTRIBUTE_NAME => AttributeKind::DocComment,
381 STORAGE_ATTRIBUTE_NAME => AttributeKind::Storage,
382 INLINE_ATTRIBUTE_NAME => AttributeKind::Inline,
383 TEST_ATTRIBUTE_NAME => AttributeKind::Test,
384 PAYABLE_ATTRIBUTE_NAME => AttributeKind::Payable,
385 ALLOW_ATTRIBUTE_NAME => AttributeKind::Allow,
386 CFG_ATTRIBUTE_NAME => AttributeKind::Cfg,
387 DEPRECATED_ATTRIBUTE_NAME => AttributeKind::Deprecated,
388 FALLBACK_ATTRIBUTE_NAME => AttributeKind::Fallback,
389 ERROR_TYPE_ATTRIBUTE_NAME => AttributeKind::ErrorType,
390 ERROR_ATTRIBUTE_NAME => AttributeKind::Error,
391 TRACE_ATTRIBUTE_NAME => AttributeKind::Trace,
392 ABI_NAME_ATTRIBUTE_NAME => AttributeKind::AbiName,
393 EVENT_ATTRIBUTE_NAME => AttributeKind::Event,
394 INDEXED_ATTRIBUTE_NAME => AttributeKind::Indexed,
395 _ => AttributeKind::Unknown,
396 }
397 }
398
399 pub fn allows_multiple(&self) -> bool {
409 use AttributeKind::*;
410 match self {
411 Unknown => true,
412 DocComment => true,
413 Storage => false,
414 Inline => false,
415 Test => false,
416 Payable => false,
417 Allow => true,
418 Cfg => true,
419 Deprecated => false,
420 Fallback => false,
421 ErrorType => false,
422 Error => false,
423 Trace => false,
424 AbiName => false,
425 Event => false,
426 Indexed => false,
427 }
428 }
429}
430
431impl Attribute {
432 pub fn is_doc_comment(&self) -> bool {
433 self.kind == AttributeKind::DocComment
434 }
435
436 pub fn is_inner(&self) -> bool {
437 self.direction == AttributeDirection::Inner
438 }
439
440 pub fn is_outer(&self) -> bool {
441 self.direction == AttributeDirection::Outer
442 }
443
444 pub(crate) fn args_multiplicity(&self) -> ArgsMultiplicity {
445 use ArgsMultiplicity as Multiplicity;
446 use AttributeKind::*;
447 match self.kind {
448 Unknown => Multiplicity::arbitrary(),
449 DocComment => Multiplicity::exactly(1),
453 Storage => Multiplicity::between(1, 2),
455 Inline => Multiplicity::exactly(1),
457 Test => Multiplicity::at_most(1),
459 Payable => Multiplicity::zero(),
460 Allow => Multiplicity::at_least(1),
461 Cfg => Multiplicity::exactly(1),
462 Deprecated => Multiplicity::at_most(1),
464 Fallback => Multiplicity::zero(),
465 ErrorType => Multiplicity::zero(),
466 Error => Multiplicity::exactly(1),
467 Trace => Multiplicity::exactly(1),
469 AbiName => Multiplicity::exactly(1),
470 Event => Multiplicity::zero(),
471 Indexed => Multiplicity::zero(),
472 }
473 }
474
475 pub(crate) fn check_args_multiplicity(&self, handler: &Handler) -> Result<(), ErrorEmitted> {
476 if !self.args_multiplicity().contains(self.args.len()) {
477 Err(handler.emit_err(
478 ConvertParseTreeError::InvalidAttributeArgsMultiplicity {
479 span: if self.args.is_empty() {
480 self.name.span()
481 } else {
482 Span::join(
483 self.args.first().unwrap().span(),
484 &self.args.last().unwrap().span,
485 )
486 },
487 attribute: self.name.clone(),
488 args_multiplicity: (&self.args_multiplicity()).into(),
489 num_of_args: self.args.len(),
490 }
491 .into(),
492 ))
493 } else {
494 Ok(())
495 }
496 }
497
498 pub(crate) fn can_have_arguments(&self) -> bool {
499 let args_multiplicity = self.args_multiplicity();
500 args_multiplicity.min != 0 || args_multiplicity.max != 0
501 }
502
503 pub(crate) fn expected_args(&self) -> ExpectedArgs {
504 use AttributeKind::*;
505 use ExpectedArgs::*;
506 match self.kind {
507 Unknown => Any,
508 DocComment => Any,
509 Storage => MustBeIn(vec![STORAGE_READ_ARG_NAME, STORAGE_WRITE_ARG_NAME]),
510 Inline => MustBeIn(vec![INLINE_ALWAYS_ARG_NAME, INLINE_NEVER_ARG_NAME]),
511 Test => MustBeIn(vec![TEST_SHOULD_REVERT_ARG_NAME]),
512 Payable => None,
513 Allow => ShouldBeIn(vec![ALLOW_DEAD_CODE_ARG_NAME, ALLOW_DEPRECATED_ARG_NAME]),
514 Cfg => {
515 let mut args = vec![
516 CFG_PROGRAM_TYPE_ARG_NAME,
518 CFG_TARGET_ARG_NAME,
519 ];
520 args.extend(Feature::CFG.iter().sorted());
521 MustBeIn(args)
522 }
523 Deprecated => MustBeIn(vec![DEPRECATED_NOTE_ARG_NAME]),
524 Fallback => None,
525 ErrorType => None,
526 Error => MustBeIn(vec![ERROR_M_ARG_NAME]),
527 Trace => MustBeIn(vec![TRACE_ALWAYS_ARG_NAME, TRACE_NEVER_ARG_NAME]),
528 AbiName => MustBeIn(vec![ABI_NAME_NAME_ARG_NAME]),
529 Event => None,
530 Indexed => None,
531 }
532 }
533
534 pub(crate) fn args_expect_values(&self) -> ArgsExpectValues {
535 use ArgsExpectValues::*;
536 use AttributeKind::*;
537 match self.kind {
538 Unknown => Maybe,
539 DocComment => No,
541 Storage => No,
542 Inline => No,
543 Test => Maybe,
545 Payable => No,
546 Allow => No,
547 Cfg => Yes,
548 Deprecated => Yes,
550 Fallback => No,
551 ErrorType => No,
552 Error => Yes,
554 Trace => No,
555 AbiName => Yes,
556 Event => No,
557 Indexed => No,
558 }
559 }
560
561 pub(crate) fn can_annotate_module_kind(&self) -> bool {
562 use AttributeKind::*;
563 match self.kind {
564 Unknown => false,
565 DocComment => self.direction == AttributeDirection::Inner,
566 Storage => false,
567 Inline => false,
568 Test => false,
569 Payable => false,
570 Allow => false,
571 Cfg => false,
572 Deprecated => false,
575 Fallback => false,
576 ErrorType => false,
577 Error => false,
578 Trace => false,
579 AbiName => false,
580 Event => false,
581 Indexed => false,
582 }
583 }
584
585 pub(crate) fn can_annotate_item_kind(&self, item_kind: &ItemKind) -> bool {
586 if matches!(item_kind, ItemKind::Error(..)) {
597 return true;
598 }
599
600 use AttributeKind::*;
601 match self.kind {
602 Unknown => !matches!(item_kind, ItemKind::Submodule(_)),
603 DocComment => {
605 self.direction == AttributeDirection::Outer
606 && !matches!(item_kind, ItemKind::Submodule(_))
607 }
608 Storage => matches!(item_kind, ItemKind::Fn(_)),
609 Inline => matches!(item_kind, ItemKind::Fn(_)),
610 Test => matches!(item_kind, ItemKind::Fn(_)),
611 Payable => false,
612 Allow => !matches!(item_kind, ItemKind::Submodule(_)),
613 Cfg => !matches!(item_kind, ItemKind::Submodule(_)),
614 Deprecated => match item_kind {
616 ItemKind::Submodule(_) => false,
617 ItemKind::Use(_) => false,
618 ItemKind::Struct(_) => true,
619 ItemKind::Enum(_) => true,
620 ItemKind::Fn(_) => true,
621 ItemKind::Trait(_) => false,
622 ItemKind::Impl(_) => false,
623 ItemKind::Abi(_) => false,
624 ItemKind::Const(_) => true,
625 ItemKind::Storage(_) => false,
626 ItemKind::Configurable(_) => false,
629 ItemKind::TypeAlias(_) => false,
630 ItemKind::Error(_, _) => true,
631 },
632 Fallback => matches!(item_kind, ItemKind::Fn(_)),
633 ErrorType => matches!(item_kind, ItemKind::Enum(_)),
634 Error => false,
635 Trace => matches!(item_kind, ItemKind::Fn(_)),
636 AbiName => matches!(item_kind, ItemKind::Struct(_) | ItemKind::Enum(_)),
637 Event => matches!(item_kind, ItemKind::Struct(_) | ItemKind::Enum(_)),
638 Indexed => false,
639 }
640 }
641
642 pub(crate) fn can_annotate_struct_or_enum_field(
647 &self,
648 struct_or_enum_field: StructOrEnumField,
649 ) -> bool {
650 use AttributeKind::*;
651 match self.kind {
652 Unknown => true,
653 DocComment => self.direction == AttributeDirection::Outer,
654 Storage => false,
655 Inline => false,
656 Test => false,
657 Payable => false,
658 Allow => true,
659 Cfg => true,
660 Deprecated => true,
661 Fallback => false,
662 ErrorType => false,
663 Error => struct_or_enum_field == StructOrEnumField::EnumField,
664 Trace => false,
665 AbiName => false,
666 Event => false,
667 Indexed => matches!(struct_or_enum_field, StructOrEnumField::StructField),
668 }
669 }
670
671 pub(crate) fn can_annotate_abi_or_trait_item(
672 &self,
673 item: &ItemTraitItem,
674 parent: TraitItemParent,
675 ) -> bool {
676 use AttributeKind::*;
677 match self.kind {
678 Unknown => true,
679 DocComment => self.direction == AttributeDirection::Outer,
680 Storage => matches!(item, ItemTraitItem::Fn(..)),
681 Inline => false,
684 Test => false,
685 Payable => parent == TraitItemParent::Abi && matches!(item, ItemTraitItem::Fn(..)),
686 Allow => true,
687 Cfg => true,
688 Deprecated => false,
690 Fallback => false,
691 ErrorType => false,
692 Error => false,
693 Trace => false,
696 AbiName => false,
697 Event => false,
698 Indexed => false,
699 }
700 }
701
702 pub(crate) fn can_annotate_impl_item(
703 &self,
704 item: &ItemImplItem,
705 parent: ImplItemParent,
706 ) -> bool {
707 use AttributeKind::*;
708 match self.kind {
709 Unknown => true,
710 DocComment => self.direction == AttributeDirection::Outer,
711 Storage => matches!(item, ItemImplItem::Fn(..)),
712 Inline => matches!(item, ItemImplItem::Fn(..)),
713 Test => false,
714 Payable => parent == ImplItemParent::Contract,
715 Allow => true,
716 Cfg => true,
717 Deprecated => !matches!(item, ItemImplItem::Type(_)),
718 Fallback => false,
719 ErrorType => false,
720 Error => false,
721 Trace => matches!(item, ItemImplItem::Fn(..)),
722 AbiName => false,
723 Event => false,
724 Indexed => false,
725 }
726 }
727
728 pub(crate) fn can_annotate_abi_or_trait_item_fn(
729 &self,
730 abi_or_trait_item: TraitItemParent,
731 ) -> bool {
732 use AttributeKind::*;
733 match self.kind {
734 Unknown => true,
735 DocComment => self.direction == AttributeDirection::Outer,
736 Storage => true,
737 Inline => true,
738 Test => false,
739 Payable => abi_or_trait_item == TraitItemParent::Abi,
740 Allow => true,
741 Cfg => true,
742 Deprecated => true,
743 Fallback => false,
744 ErrorType => false,
745 Error => false,
746 Trace => true,
747 AbiName => false,
748 Event => false,
749 Indexed => false,
750 }
751 }
752
753 pub(crate) fn can_annotate_storage_entry(&self) -> bool {
754 use AttributeKind::*;
755 match self.kind {
756 Unknown => true,
757 DocComment => self.direction == AttributeDirection::Outer,
758 Storage => false,
759 Inline => false,
760 Test => false,
761 Payable => false,
762 Allow => true,
763 Cfg => true,
764 Deprecated => false,
766 Fallback => false,
767 ErrorType => false,
768 Error => false,
769 Trace => false,
770 AbiName => false,
771 Event => false,
772 Indexed => false,
773 }
774 }
775
776 pub(crate) fn can_annotate_configurable_field(&self) -> bool {
777 use AttributeKind::*;
778 match self.kind {
779 Unknown => true,
780 DocComment => self.direction == AttributeDirection::Outer,
781 Storage => false,
782 Inline => false,
783 Test => false,
784 Payable => false,
785 Allow => true,
786 Cfg => true,
787 Deprecated => true,
788 Fallback => false,
789 ErrorType => false,
790 Error => false,
791 Trace => false,
792 AbiName => false,
793 Event => false,
794 Indexed => false,
795 }
796 }
797
798 pub(crate) fn can_only_annotate_help(&self, target_friendly_name: &str) -> Vec<&'static str> {
799 use AttributeKind::*;
802 let help = match self.kind {
803 Unknown => vec![],
804 DocComment => match self.direction {
805 AttributeDirection::Inner => vec![
806 "Inner doc comments (`//!`) can only document modules and must be",
807 "at the beginning of the module file, before the module kind.",
808 ],
809 AttributeDirection::Outer => if target_friendly_name.starts_with("module kind") {
810 vec![
811 "To document modules, use inner doc comments (`//!`). E.g.:",
812 "//! This doc comment documents a module.",
813 ]
814 } else {
815 vec![]
816 },
817 },
818 Storage => {
819 if target_friendly_name == "function signature" {
820 vec![
821 "\"storage\" attribute can only annotate functions that have an implementation.",
822 "Function signatures in ABI and trait declarations do not have implementations.",
823 ]
824 } else {
825 vec![
826 "\"storage\" attribute can only annotate functions.",
827 ]
828 }
829 },
830 Inline => vec!["\"inline\" attribute can only annotate functions."],
831 Test => vec!["\"test\" attribute can only annotate module functions."],
832 Payable => vec![
833 "\"payable\" attribute can only annotate:",
834 " - ABI function signatures and their implementations in contracts,",
835 " - provided ABI functions.",
836 ],
837 Allow => vec![],
838 Cfg => vec![],
839 Deprecated => vec![
841 "\"deprecated\" attribute is currently not implemented for all elements that could be deprecated.",
842 ],
843 Fallback => vec!["\"fallback\" attribute can only annotate module functions in a contract module."],
844 ErrorType => vec!["\"error_type\" attribute can only annotate enums."],
845 Error => vec!["\"error\" attribute can only annotate enum variants of enums annotated with the \"error_type\" attribute."],
846 Trace => vec!["\"trace\" attribute can only annotate functions."],
847 AbiName => vec![
848 "\"abi_name\" attribute can only annotate structs and enums.",
849 ],
850 Event => vec![
851 "\"event\" attribute can only annotate structs or enums.",
852 ],
853 Indexed => vec![
854 "\"indexed\" attribute can only annotate struct fields.",
855 ],
856 };
857
858 if help.is_empty() && target_friendly_name.starts_with("module kind") {
859 vec!["Annotating module kinds (contract, script, predicate, or library) is currently not implemented."]
860 } else {
861 help
862 }
863 }
864}
865
866#[derive(Default, Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
878pub struct Attributes {
879 attributes: Arc<Vec<Attribute>>,
889 deprecated_attr_index: Option<usize>,
893}
894
895impl Attributes {
896 pub fn new(attribute_decls: &[AttributeDecl]) -> Attributes {
897 let mut attributes: Vec<Attribute> = vec![];
898 for attr_decl in attribute_decls {
899 let attrs = attr_decl.attribute.get().into_iter();
900 for attr in attrs {
901 let name = attr.name.as_str();
902 let args = attr
903 .args
904 .as_ref()
905 .map(|parens| {
906 parens
907 .get()
908 .into_iter()
909 .cloned()
910 .map(|arg| AttributeArg {
911 name: arg.name.clone(),
912 value: arg.value.clone(),
913 span: arg.span(),
914 })
915 .collect()
916 })
917 .unwrap_or_default();
918
919 let attribute = Attribute {
920 direction: (&attr_decl.hash_kind).into(),
921 name: attr.name.clone(),
922 args,
923 span: attr_decl.span(),
924 kind: AttributeKind::from_attribute_name(name),
925 };
926
927 attributes.push(attribute);
928 }
929 }
930
931 Attributes {
932 deprecated_attr_index: attributes
933 .iter()
934 .rposition(|attr| attr.kind == AttributeKind::Deprecated),
935 attributes: Arc::new(attributes),
936 }
937 }
938
939 pub fn is_empty(&self) -> bool {
940 self.attributes.is_empty()
941 }
942
943 pub fn first(&self) -> Option<&Attribute> {
945 self.attributes.first()
946 }
947
948 pub fn known_attribute_names(&self) -> &'static [&'static str] {
949 KNOWN_ATTRIBUTE_NAMES
950 }
951
952 pub fn all(&self) -> impl Iterator<Item = &Attribute> {
953 self.attributes.iter()
954 }
955
956 pub fn all_as_slice(&self) -> &[Attribute] {
957 self.attributes.as_slice()
958 }
959
960 pub fn all_by_kind<F>(&self, predicate: F) -> IndexMap<AttributeKind, Vec<&Attribute>>
961 where
962 F: Fn(&&Attribute) -> bool,
963 {
964 let mut result = IndexMap::<_, Vec<&Attribute>>::new();
965 for attr in self.attributes.iter().filter(predicate) {
966 result.entry(attr.kind).or_default().push(attr);
967 }
968 result
969 }
970
971 pub fn of_kind(&self, kind: AttributeKind) -> impl Iterator<Item = &Attribute> {
972 self.attributes.iter().filter(move |attr| attr.kind == kind)
973 }
974
975 pub fn has_any_of_kind(&self, kind: AttributeKind) -> bool {
976 self.of_kind(kind).any(|_| true)
977 }
978
979 pub fn unknown(&self) -> impl Iterator<Item = &Attribute> {
980 self.attributes
981 .iter()
982 .filter(|attr| attr.kind == AttributeKind::Unknown)
983 }
984
985 pub fn has_allow_dead_code(&self) -> bool {
986 self.has_allow(|arg| arg.is_allow_dead_code())
987 }
988
989 pub fn has_allow_deprecated(&self) -> bool {
990 self.has_allow(|arg| arg.is_allow_deprecated())
991 }
992
993 fn has_allow(&self, arg_filter: impl Fn(&AttributeArg) -> bool) -> bool {
994 self.of_kind(AttributeKind::Allow)
995 .flat_map(|attribute| &attribute.args)
996 .any(arg_filter)
997 }
998
999 pub fn has_error_type(&self) -> bool {
1000 self.of_kind(AttributeKind::ErrorType).any(|_| true)
1001 }
1002
1003 pub fn has_error(&self) -> bool {
1004 self.of_kind(AttributeKind::Error).any(|_| true)
1005 }
1006
1007 pub fn inline(&self) -> Option<Inline> {
1010 match self
1014 .of_kind(AttributeKind::Inline)
1015 .last()?
1016 .args
1017 .last()?
1018 .name
1019 .as_str()
1020 {
1021 INLINE_NEVER_ARG_NAME => Some(Inline::Never),
1022 INLINE_ALWAYS_ARG_NAME => Some(Inline::Always),
1023 _ => None,
1024 }
1025 }
1026
1027 pub fn trace(&self) -> Option<Trace> {
1030 match self
1034 .of_kind(AttributeKind::Trace)
1035 .last()?
1036 .args
1037 .last()?
1038 .name
1039 .as_str()
1040 {
1041 TRACE_NEVER_ARG_NAME => Some(Trace::Never),
1042 TRACE_ALWAYS_ARG_NAME => Some(Trace::Always),
1043 _ => None,
1044 }
1045 }
1046
1047 pub fn purity(&self) -> Purity {
1050 let Some(storage_attr) = self.of_kind(AttributeKind::Storage).last() else {
1053 return Purity::Pure;
1054 };
1055
1056 let mut purity = Purity::Pure;
1057
1058 let mut add_impurity = |new_impurity, counter_impurity| {
1059 if purity == Purity::Pure {
1060 purity = new_impurity;
1061 } else if purity == counter_impurity {
1062 purity = Purity::ReadsWrites;
1063 }
1064 };
1065
1066 for arg in storage_attr.args.iter() {
1067 match arg.name.as_str() {
1068 STORAGE_READ_ARG_NAME => add_impurity(Purity::Reads, Purity::Writes),
1069 STORAGE_WRITE_ARG_NAME => add_impurity(Purity::Writes, Purity::Reads),
1070 _ => {}
1071 }
1072 }
1073
1074 purity
1075 }
1076
1077 pub fn deprecated(&self) -> Option<&Attribute> {
1080 self.deprecated_attr_index
1081 .map(|index| &self.attributes[index])
1082 }
1083
1084 pub fn abi_name(&self) -> Option<&Attribute> {
1087 self.of_kind(AttributeKind::AbiName).last()
1088 }
1089
1090 pub fn test(&self) -> Option<&Attribute> {
1093 self.of_kind(AttributeKind::Test).last()
1095 }
1096
1097 pub fn error(&self) -> Option<&Attribute> {
1100 self.of_kind(AttributeKind::Error).last()
1102 }
1103
1104 pub fn error_message(&self) -> Option<&String> {
1110 self.error().and_then(|error_attr| {
1112 error_attr
1113 .args
1114 .iter()
1115 .filter(|arg| arg.is_error_message())
1116 .next_back()
1117 .and_then(|arg| arg.get_string_opt(&Handler::default()).ok().flatten())
1118 })
1119 }
1120
1121 pub fn event(&self) -> Option<&Attribute> {
1124 self.of_kind(AttributeKind::Event).last()
1125 }
1126
1127 pub fn indexed(&self) -> Option<&Attribute> {
1130 self.of_kind(AttributeKind::Indexed).last()
1131 }
1132}
1133
1134pub struct AllowDeprecatedEnterToken {
1135 diff: i32,
1136}
1137
1138#[derive(Default)]
1139pub struct AllowDeprecatedState {
1140 allowed: u32,
1141}
1142impl AllowDeprecatedState {
1143 pub(crate) fn enter(&mut self, attributes: Attributes) -> AllowDeprecatedEnterToken {
1144 if attributes.has_allow_deprecated() {
1145 self.allowed += 1;
1146
1147 AllowDeprecatedEnterToken { diff: -1 }
1148 } else {
1149 AllowDeprecatedEnterToken { diff: 0 }
1150 }
1151 }
1152
1153 pub(crate) fn exit(&mut self, token: AllowDeprecatedEnterToken) {
1154 self.allowed = self.allowed.saturating_add_signed(token.diff);
1155 }
1156
1157 pub(crate) fn is_allowed(&self) -> bool {
1158 self.allowed > 0
1159 }
1160}