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