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}
360
361#[derive(Debug, Clone, Copy, PartialEq, Eq)]
363pub enum TraitItemParent {
364 Abi,
365 Trait,
366}
367
368#[derive(Debug, Clone, Copy, PartialEq, Eq)]
370pub enum StructOrEnumField {
371 StructField,
372 EnumField,
373}
374
375impl AttributeKind {
376 pub fn from_attribute_name(name: &str) -> Self {
377 match name {
378 DOC_COMMENT_ATTRIBUTE_NAME => AttributeKind::DocComment,
379 STORAGE_ATTRIBUTE_NAME => AttributeKind::Storage,
380 INLINE_ATTRIBUTE_NAME => AttributeKind::Inline,
381 TEST_ATTRIBUTE_NAME => AttributeKind::Test,
382 PAYABLE_ATTRIBUTE_NAME => AttributeKind::Payable,
383 ALLOW_ATTRIBUTE_NAME => AttributeKind::Allow,
384 CFG_ATTRIBUTE_NAME => AttributeKind::Cfg,
385 DEPRECATED_ATTRIBUTE_NAME => AttributeKind::Deprecated,
386 FALLBACK_ATTRIBUTE_NAME => AttributeKind::Fallback,
387 ERROR_TYPE_ATTRIBUTE_NAME => AttributeKind::ErrorType,
388 ERROR_ATTRIBUTE_NAME => AttributeKind::Error,
389 TRACE_ATTRIBUTE_NAME => AttributeKind::Trace,
390 ABI_NAME_ATTRIBUTE_NAME => AttributeKind::AbiName,
391 _ => AttributeKind::Unknown,
392 }
393 }
394
395 pub fn allows_multiple(&self) -> bool {
405 use AttributeKind::*;
406 match self {
407 Unknown => true,
408 DocComment => true,
409 Storage => false,
410 Inline => false,
411 Test => false,
412 Payable => false,
413 Allow => true,
414 Cfg => true,
415 Deprecated => false,
416 Fallback => false,
417 ErrorType => false,
418 Error => false,
419 Trace => false,
420 AbiName => false,
421 }
422 }
423}
424
425impl Attribute {
426 pub fn is_doc_comment(&self) -> bool {
427 self.kind == AttributeKind::DocComment
428 }
429
430 pub fn is_inner(&self) -> bool {
431 self.direction == AttributeDirection::Inner
432 }
433
434 pub fn is_outer(&self) -> bool {
435 self.direction == AttributeDirection::Outer
436 }
437
438 pub(crate) fn args_multiplicity(&self) -> ArgsMultiplicity {
439 use ArgsMultiplicity as Multiplicity;
440 use AttributeKind::*;
441 match self.kind {
442 Unknown => Multiplicity::arbitrary(),
443 DocComment => Multiplicity::exactly(1),
447 Storage => Multiplicity::between(1, 2),
449 Inline => Multiplicity::exactly(1),
451 Test => Multiplicity::at_most(1),
453 Payable => Multiplicity::zero(),
454 Allow => Multiplicity::at_least(1),
455 Cfg => Multiplicity::exactly(1),
456 Deprecated => Multiplicity::at_most(1),
458 Fallback => Multiplicity::zero(),
459 ErrorType => Multiplicity::zero(),
460 Error => Multiplicity::exactly(1),
461 Trace => Multiplicity::exactly(1),
463 AbiName => Multiplicity::exactly(1),
464 }
465 }
466
467 pub(crate) fn check_args_multiplicity(&self, handler: &Handler) -> Result<(), ErrorEmitted> {
468 if !self.args_multiplicity().contains(self.args.len()) {
469 Err(handler.emit_err(
470 ConvertParseTreeError::InvalidAttributeArgsMultiplicity {
471 span: if self.args.is_empty() {
472 self.name.span()
473 } else {
474 Span::join(
475 self.args.first().unwrap().span(),
476 &self.args.last().unwrap().span,
477 )
478 },
479 attribute: self.name.clone(),
480 args_multiplicity: (&self.args_multiplicity()).into(),
481 num_of_args: self.args.len(),
482 }
483 .into(),
484 ))
485 } else {
486 Ok(())
487 }
488 }
489
490 pub(crate) fn can_have_arguments(&self) -> bool {
491 let args_multiplicity = self.args_multiplicity();
492 args_multiplicity.min != 0 || args_multiplicity.max != 0
493 }
494
495 pub(crate) fn expected_args(&self) -> ExpectedArgs {
496 use AttributeKind::*;
497 use ExpectedArgs::*;
498 match self.kind {
499 Unknown => Any,
500 DocComment => Any,
501 Storage => MustBeIn(vec![STORAGE_READ_ARG_NAME, STORAGE_WRITE_ARG_NAME]),
502 Inline => MustBeIn(vec![INLINE_ALWAYS_ARG_NAME, INLINE_NEVER_ARG_NAME]),
503 Test => MustBeIn(vec![TEST_SHOULD_REVERT_ARG_NAME]),
504 Payable => None,
505 Allow => ShouldBeIn(vec![ALLOW_DEAD_CODE_ARG_NAME, ALLOW_DEPRECATED_ARG_NAME]),
506 Cfg => {
507 let mut args = vec![
508 CFG_PROGRAM_TYPE_ARG_NAME,
510 CFG_TARGET_ARG_NAME,
511 ];
512 args.extend(Feature::CFG.iter().sorted());
513 MustBeIn(args)
514 }
515 Deprecated => MustBeIn(vec![DEPRECATED_NOTE_ARG_NAME]),
516 Fallback => None,
517 ErrorType => None,
518 Error => MustBeIn(vec![ERROR_M_ARG_NAME]),
519 Trace => MustBeIn(vec![TRACE_ALWAYS_ARG_NAME, TRACE_NEVER_ARG_NAME]),
520 AbiName => MustBeIn(vec![ABI_NAME_NAME_ARG_NAME]),
521 }
522 }
523
524 pub(crate) fn args_expect_values(&self) -> ArgsExpectValues {
525 use ArgsExpectValues::*;
526 use AttributeKind::*;
527 match self.kind {
528 Unknown => Maybe,
529 DocComment => No,
531 Storage => No,
532 Inline => No,
533 Test => Maybe,
535 Payable => No,
536 Allow => No,
537 Cfg => Yes,
538 Deprecated => Yes,
540 Fallback => No,
541 ErrorType => No,
542 Error => Yes,
544 Trace => No,
545 AbiName => Yes,
546 }
547 }
548
549 pub(crate) fn can_annotate_module_kind(&self) -> bool {
550 use AttributeKind::*;
551 match self.kind {
552 Unknown => false,
553 DocComment => self.direction == AttributeDirection::Inner,
554 Storage => false,
555 Inline => false,
556 Test => false,
557 Payable => false,
558 Allow => false,
559 Cfg => false,
560 Deprecated => false,
563 Fallback => false,
564 ErrorType => false,
565 Error => false,
566 Trace => false,
567 AbiName => false,
568 }
569 }
570
571 pub(crate) fn can_annotate_item_kind(&self, item_kind: &ItemKind) -> bool {
572 if matches!(item_kind, ItemKind::Error(..)) {
583 return true;
584 }
585
586 use AttributeKind::*;
587 match self.kind {
588 Unknown => !matches!(item_kind, ItemKind::Submodule(_)),
589 DocComment => {
591 self.direction == AttributeDirection::Outer
592 && !matches!(item_kind, ItemKind::Submodule(_))
593 }
594 Storage => matches!(item_kind, ItemKind::Fn(_)),
595 Inline => matches!(item_kind, ItemKind::Fn(_)),
596 Test => matches!(item_kind, ItemKind::Fn(_)),
597 Payable => false,
598 Allow => !matches!(item_kind, ItemKind::Submodule(_)),
599 Cfg => !matches!(item_kind, ItemKind::Submodule(_)),
600 Deprecated => match item_kind {
602 ItemKind::Submodule(_) => false,
603 ItemKind::Use(_) => false,
604 ItemKind::Struct(_) => true,
605 ItemKind::Enum(_) => true,
606 ItemKind::Fn(_) => true,
607 ItemKind::Trait(_) => false,
608 ItemKind::Impl(_) => false,
609 ItemKind::Abi(_) => false,
610 ItemKind::Const(_) => true,
611 ItemKind::Storage(_) => false,
612 ItemKind::Configurable(_) => false,
615 ItemKind::TypeAlias(_) => false,
616 ItemKind::Error(_, _) => true,
617 },
618 Fallback => matches!(item_kind, ItemKind::Fn(_)),
619 ErrorType => matches!(item_kind, ItemKind::Enum(_)),
620 Error => false,
621 Trace => matches!(item_kind, ItemKind::Fn(_)),
622 AbiName => matches!(item_kind, ItemKind::Struct(_) | ItemKind::Enum(_)),
623 }
624 }
625
626 pub(crate) fn can_annotate_struct_or_enum_field(
631 &self,
632 struct_or_enum_field: StructOrEnumField,
633 ) -> bool {
634 use AttributeKind::*;
635 match self.kind {
636 Unknown => true,
637 DocComment => self.direction == AttributeDirection::Outer,
638 Storage => false,
639 Inline => false,
640 Test => false,
641 Payable => false,
642 Allow => true,
643 Cfg => true,
644 Deprecated => true,
645 Fallback => false,
646 ErrorType => false,
647 Error => struct_or_enum_field == StructOrEnumField::EnumField,
648 Trace => false,
649 AbiName => false,
650 }
651 }
652
653 pub(crate) fn can_annotate_abi_or_trait_item(
654 &self,
655 item: &ItemTraitItem,
656 parent: TraitItemParent,
657 ) -> bool {
658 use AttributeKind::*;
659 match self.kind {
660 Unknown => true,
661 DocComment => self.direction == AttributeDirection::Outer,
662 Storage => matches!(item, ItemTraitItem::Fn(..)),
663 Inline => false,
666 Test => false,
667 Payable => parent == TraitItemParent::Abi && matches!(item, ItemTraitItem::Fn(..)),
668 Allow => true,
669 Cfg => true,
670 Deprecated => false,
672 Fallback => false,
673 ErrorType => false,
674 Error => false,
675 Trace => false,
678 AbiName => false,
679 }
680 }
681
682 pub(crate) fn can_annotate_impl_item(
683 &self,
684 item: &ItemImplItem,
685 parent: ImplItemParent,
686 ) -> bool {
687 use AttributeKind::*;
688 match self.kind {
689 Unknown => true,
690 DocComment => self.direction == AttributeDirection::Outer,
691 Storage => matches!(item, ItemImplItem::Fn(..)),
692 Inline => matches!(item, ItemImplItem::Fn(..)),
693 Test => false,
694 Payable => parent == ImplItemParent::Contract,
695 Allow => true,
696 Cfg => true,
697 Deprecated => !matches!(item, ItemImplItem::Type(_)),
698 Fallback => false,
699 ErrorType => false,
700 Error => false,
701 Trace => matches!(item, ItemImplItem::Fn(..)),
702 AbiName => false,
703 }
704 }
705
706 pub(crate) fn can_annotate_abi_or_trait_item_fn(
707 &self,
708 abi_or_trait_item: TraitItemParent,
709 ) -> bool {
710 use AttributeKind::*;
711 match self.kind {
712 Unknown => true,
713 DocComment => self.direction == AttributeDirection::Outer,
714 Storage => true,
715 Inline => true,
716 Test => false,
717 Payable => abi_or_trait_item == TraitItemParent::Abi,
718 Allow => true,
719 Cfg => true,
720 Deprecated => true,
721 Fallback => false,
722 ErrorType => false,
723 Error => false,
724 Trace => true,
725 AbiName => false,
726 }
727 }
728
729 pub(crate) fn can_annotate_storage_entry(&self) -> bool {
730 use AttributeKind::*;
731 match self.kind {
732 Unknown => true,
733 DocComment => self.direction == AttributeDirection::Outer,
734 Storage => false,
735 Inline => false,
736 Test => false,
737 Payable => false,
738 Allow => true,
739 Cfg => true,
740 Deprecated => false,
742 Fallback => false,
743 ErrorType => false,
744 Error => false,
745 Trace => false,
746 AbiName => false,
747 }
748 }
749
750 pub(crate) fn can_annotate_configurable_field(&self) -> bool {
751 use AttributeKind::*;
752 match self.kind {
753 Unknown => true,
754 DocComment => self.direction == AttributeDirection::Outer,
755 Storage => false,
756 Inline => false,
757 Test => false,
758 Payable => false,
759 Allow => true,
760 Cfg => true,
761 Deprecated => true,
762 Fallback => false,
763 ErrorType => false,
764 Error => false,
765 Trace => false,
766 AbiName => false,
767 }
768 }
769
770 pub(crate) fn can_only_annotate_help(&self, target_friendly_name: &str) -> Vec<&'static str> {
771 use AttributeKind::*;
774 let help = match self.kind {
775 Unknown => vec![],
776 DocComment => match self.direction {
777 AttributeDirection::Inner => vec![
778 "Inner doc comments (`//!`) can only document modules and must be",
779 "at the beginning of the module file, before the module kind.",
780 ],
781 AttributeDirection::Outer => if target_friendly_name.starts_with("module kind") {
782 vec![
783 "To document modules, use inner doc comments (`//!`). E.g.:",
784 "//! This doc comment documents a module.",
785 ]
786 } else {
787 vec![]
788 },
789 },
790 Storage => {
791 if target_friendly_name == "function signature" {
792 vec![
793 "\"storage\" attribute can only annotate functions that have an implementation.",
794 "Function signatures in ABI and trait declarations do not have implementations.",
795 ]
796 } else {
797 vec![
798 "\"storage\" attribute can only annotate functions.",
799 ]
800 }
801 },
802 Inline => vec!["\"inline\" attribute can only annotate functions."],
803 Test => vec!["\"test\" attribute can only annotate module functions."],
804 Payable => vec![
805 "\"payable\" attribute can only annotate:",
806 " - ABI function signatures and their implementations in contracts,",
807 " - provided ABI functions.",
808 ],
809 Allow => vec![],
810 Cfg => vec![],
811 Deprecated => vec![
813 "\"deprecated\" attribute is currently not implemented for all elements that could be deprecated.",
814 ],
815 Fallback => vec!["\"fallback\" attribute can only annotate module functions in a contract module."],
816 ErrorType => vec!["\"error_type\" attribute can only annotate enums."],
817 Error => vec!["\"error\" attribute can only annotate enum variants of enums annotated with the \"error_type\" attribute."],
818 Trace => vec!["\"trace\" attribute can only annotate functions that can panic."],
819 AbiName => vec![
820 "\"abi_name\" attribute can only annotate structs and enums.",
821 ],
822 };
823
824 if help.is_empty() && target_friendly_name.starts_with("module kind") {
825 vec!["Annotating module kinds (contract, script, predicate, or library) is currently not implemented."]
826 } else {
827 help
828 }
829 }
830}
831
832#[derive(Default, Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
844pub struct Attributes {
845 attributes: Arc<Vec<Attribute>>,
855 deprecated_attr_index: Option<usize>,
859}
860
861impl Attributes {
862 pub fn new(attribute_decls: &[AttributeDecl]) -> Attributes {
863 let mut attributes: Vec<Attribute> = vec![];
864 for attr_decl in attribute_decls {
865 let attrs = attr_decl.attribute.get().into_iter();
866 for attr in attrs {
867 let name = attr.name.as_str();
868 let args = attr
869 .args
870 .as_ref()
871 .map(|parens| {
872 parens
873 .get()
874 .into_iter()
875 .cloned()
876 .map(|arg| AttributeArg {
877 name: arg.name.clone(),
878 value: arg.value.clone(),
879 span: arg.span(),
880 })
881 .collect()
882 })
883 .unwrap_or_default();
884
885 let attribute = Attribute {
886 direction: (&attr_decl.hash_kind).into(),
887 name: attr.name.clone(),
888 args,
889 span: attr_decl.span(),
890 kind: AttributeKind::from_attribute_name(name),
891 };
892
893 attributes.push(attribute);
894 }
895 }
896
897 Attributes {
898 deprecated_attr_index: attributes
899 .iter()
900 .rposition(|attr| attr.kind == AttributeKind::Deprecated),
901 attributes: Arc::new(attributes),
902 }
903 }
904
905 pub fn is_empty(&self) -> bool {
906 self.attributes.is_empty()
907 }
908
909 pub fn first(&self) -> Option<&Attribute> {
911 self.attributes.first()
912 }
913
914 pub fn known_attribute_names(&self) -> &'static [&'static str] {
915 KNOWN_ATTRIBUTE_NAMES
916 }
917
918 pub fn all(&self) -> impl Iterator<Item = &Attribute> {
919 self.attributes.iter()
920 }
921
922 pub fn all_as_slice(&self) -> &[Attribute] {
923 self.attributes.as_slice()
924 }
925
926 pub fn all_by_kind<F>(&self, predicate: F) -> IndexMap<AttributeKind, Vec<&Attribute>>
927 where
928 F: Fn(&&Attribute) -> bool,
929 {
930 let mut result = IndexMap::<_, Vec<&Attribute>>::new();
931 for attr in self.attributes.iter().filter(predicate) {
932 result.entry(attr.kind).or_default().push(attr);
933 }
934 result
935 }
936
937 pub fn of_kind(&self, kind: AttributeKind) -> impl Iterator<Item = &Attribute> {
938 self.attributes.iter().filter(move |attr| attr.kind == kind)
939 }
940
941 pub fn has_any_of_kind(&self, kind: AttributeKind) -> bool {
942 self.of_kind(kind).any(|_| true)
943 }
944
945 pub fn unknown(&self) -> impl Iterator<Item = &Attribute> {
946 self.attributes
947 .iter()
948 .filter(|attr| attr.kind == AttributeKind::Unknown)
949 }
950
951 pub fn has_allow_dead_code(&self) -> bool {
952 self.has_allow(|arg| arg.is_allow_dead_code())
953 }
954
955 pub fn has_allow_deprecated(&self) -> bool {
956 self.has_allow(|arg| arg.is_allow_deprecated())
957 }
958
959 fn has_allow(&self, arg_filter: impl Fn(&AttributeArg) -> bool) -> bool {
960 self.of_kind(AttributeKind::Allow)
961 .flat_map(|attribute| &attribute.args)
962 .any(arg_filter)
963 }
964
965 pub fn has_error_type(&self) -> bool {
966 self.of_kind(AttributeKind::ErrorType).any(|_| true)
967 }
968
969 pub fn has_error(&self) -> bool {
970 self.of_kind(AttributeKind::Error).any(|_| true)
971 }
972
973 pub fn inline(&self) -> Option<Inline> {
976 match self
980 .of_kind(AttributeKind::Inline)
981 .last()?
982 .args
983 .last()?
984 .name
985 .as_str()
986 {
987 INLINE_NEVER_ARG_NAME => Some(Inline::Never),
988 INLINE_ALWAYS_ARG_NAME => Some(Inline::Always),
989 _ => None,
990 }
991 }
992
993 pub fn trace(&self) -> Option<Trace> {
996 match self
1000 .of_kind(AttributeKind::Trace)
1001 .last()?
1002 .args
1003 .last()?
1004 .name
1005 .as_str()
1006 {
1007 TRACE_NEVER_ARG_NAME => Some(Trace::Never),
1008 TRACE_ALWAYS_ARG_NAME => Some(Trace::Always),
1009 _ => None,
1010 }
1011 }
1012
1013 pub fn purity(&self) -> Purity {
1016 let Some(storage_attr) = self.of_kind(AttributeKind::Storage).last() else {
1019 return Purity::Pure;
1020 };
1021
1022 let mut purity = Purity::Pure;
1023
1024 let mut add_impurity = |new_impurity, counter_impurity| {
1025 if purity == Purity::Pure {
1026 purity = new_impurity;
1027 } else if purity == counter_impurity {
1028 purity = Purity::ReadsWrites;
1029 }
1030 };
1031
1032 for arg in storage_attr.args.iter() {
1033 match arg.name.as_str() {
1034 STORAGE_READ_ARG_NAME => add_impurity(Purity::Reads, Purity::Writes),
1035 STORAGE_WRITE_ARG_NAME => add_impurity(Purity::Writes, Purity::Reads),
1036 _ => {}
1037 }
1038 }
1039
1040 purity
1041 }
1042
1043 pub fn deprecated(&self) -> Option<&Attribute> {
1046 self.deprecated_attr_index
1047 .map(|index| &self.attributes[index])
1048 }
1049
1050 pub fn abi_name(&self) -> Option<&Attribute> {
1053 self.of_kind(AttributeKind::AbiName).last()
1054 }
1055
1056 pub fn test(&self) -> Option<&Attribute> {
1059 self.of_kind(AttributeKind::Test).last()
1061 }
1062
1063 pub fn error(&self) -> Option<&Attribute> {
1066 self.of_kind(AttributeKind::Error).last()
1068 }
1069
1070 pub fn error_message(&self) -> Option<&String> {
1076 self.error().and_then(|error_attr| {
1078 error_attr
1079 .args
1080 .iter()
1081 .filter(|arg| arg.is_error_message())
1082 .next_back()
1083 .and_then(|arg| arg.get_string_opt(&Handler::default()).ok().flatten())
1084 })
1085 }
1086}
1087
1088pub struct AllowDeprecatedEnterToken {
1089 diff: i32,
1090}
1091
1092#[derive(Default)]
1093pub struct AllowDeprecatedState {
1094 allowed: u32,
1095}
1096impl AllowDeprecatedState {
1097 pub(crate) fn enter(&mut self, attributes: Attributes) -> AllowDeprecatedEnterToken {
1098 if attributes.has_allow_deprecated() {
1099 self.allowed += 1;
1100
1101 AllowDeprecatedEnterToken { diff: -1 }
1102 } else {
1103 AllowDeprecatedEnterToken { diff: 0 }
1104 }
1105 }
1106
1107 pub(crate) fn exit(&mut self, token: AllowDeprecatedEnterToken) {
1108 self.allowed = self.allowed.saturating_add_signed(token.diff);
1109 }
1110
1111 pub(crate) fn is_allowed(&self) -> bool {
1112 self.allowed > 0
1113 }
1114}