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_abi(&self) -> bool {
586 use AttributeKind::*;
587 match self.kind {
588 Unknown => true,
589 DocComment => self.direction == AttributeDirection::Outer,
590 Storage => false,
591 Inline => false,
592 Test => false,
593 Payable => false,
594 Allow => true,
595 Cfg => true,
596 Deprecated => false,
598 Fallback => false,
599 ErrorType => false,
600 Error => false,
601 Trace => false,
602 AbiName => false,
603 Event => false,
604 Indexed => false,
605 }
606 }
607
608 pub(crate) fn can_annotate_item_kind(&self, item_kind: &ItemKind) -> bool {
609 if matches!(item_kind, ItemKind::Error(..)) {
620 return true;
621 }
622
623 use AttributeKind::*;
624 match self.kind {
625 Unknown => !matches!(item_kind, ItemKind::Submodule(_)),
626 DocComment => {
628 self.direction == AttributeDirection::Outer
629 && !matches!(item_kind, ItemKind::Submodule(_))
630 }
631 Storage => matches!(item_kind, ItemKind::Fn(_)),
632 Inline => matches!(item_kind, ItemKind::Fn(_)),
633 Test => matches!(item_kind, ItemKind::Fn(_)),
634 Payable => false,
635 Allow => !matches!(item_kind, ItemKind::Submodule(_)),
636 Cfg => !matches!(item_kind, ItemKind::Submodule(_)),
637 Deprecated => match item_kind {
639 ItemKind::Submodule(_) => false,
640 ItemKind::Use(_) => false,
641 ItemKind::Struct(_) => true,
642 ItemKind::Enum(_) => true,
643 ItemKind::Fn(_) => true,
644 ItemKind::Trait(_) => false,
645 ItemKind::Impl(_) => false,
646 ItemKind::Abi(_) => false,
647 ItemKind::Const(_) => true,
648 ItemKind::Storage(_) => false,
649 ItemKind::Configurable(_) => false,
652 ItemKind::TypeAlias(_) => false,
653 ItemKind::Error(_, _) => true,
654 },
655 Fallback => matches!(item_kind, ItemKind::Fn(_)),
656 ErrorType => matches!(item_kind, ItemKind::Enum(_)),
657 Error => false,
658 Trace => matches!(item_kind, ItemKind::Fn(_)),
659 AbiName => matches!(item_kind, ItemKind::Struct(_) | ItemKind::Enum(_)),
660 Event => matches!(item_kind, ItemKind::Struct(_) | ItemKind::Enum(_)),
661 Indexed => false,
662 }
663 }
664
665 pub(crate) fn can_annotate_struct_or_enum_field(
670 &self,
671 struct_or_enum_field: StructOrEnumField,
672 ) -> bool {
673 use AttributeKind::*;
674 match self.kind {
675 Unknown => true,
676 DocComment => self.direction == AttributeDirection::Outer,
677 Storage => false,
678 Inline => false,
679 Test => false,
680 Payable => false,
681 Allow => true,
682 Cfg => true,
683 Deprecated => true,
684 Fallback => false,
685 ErrorType => false,
686 Error => struct_or_enum_field == StructOrEnumField::EnumField,
687 Trace => false,
688 AbiName => false,
689 Event => false,
690 Indexed => matches!(struct_or_enum_field, StructOrEnumField::StructField),
691 }
692 }
693
694 pub(crate) fn can_annotate_abi_or_trait_interface_item(
705 &self,
706 item: &ItemTraitItem,
707 parent: TraitItemParent,
708 ) -> bool {
709 use AttributeKind::*;
710 match self.kind {
711 Unknown => true,
712 DocComment => self.direction == AttributeDirection::Outer,
713 Storage => matches!(item, ItemTraitItem::Fn(..)),
714 Inline => false,
717 Test => false,
718 Payable => parent == TraitItemParent::Abi && matches!(item, ItemTraitItem::Fn(..)),
719 Allow => true,
720 Cfg => true,
721 Deprecated => false,
723 Fallback => false,
724 ErrorType => false,
725 Error => false,
726 Trace => false,
729 AbiName => false,
730 Event => false,
731 Indexed => false,
732 }
733 }
734
735 pub(crate) fn can_annotate_abi_or_trait_interface_fn(&self, parent: TraitItemParent) -> bool {
744 use AttributeKind::*;
745 match self.kind {
746 Unknown => true,
747 DocComment => self.direction == AttributeDirection::Outer,
748 Storage => true,
749 Inline => false,
752 Test => false,
753 Payable => parent == TraitItemParent::Abi,
754 Allow => true,
755 Cfg => true,
756 Deprecated => false,
758 Fallback => false,
759 ErrorType => false,
760 Error => false,
761 Trace => false,
764 AbiName => false,
765 Event => false,
766 Indexed => false,
767 }
768 }
769
770 pub(crate) fn can_annotate_abi_or_trait_interface_const(
779 &self,
780 _parent: TraitItemParent,
781 ) -> bool {
782 use AttributeKind::*;
783 match self.kind {
784 Unknown => true,
785 DocComment => self.direction == AttributeDirection::Outer,
786 Storage => false,
787 Inline => false,
788 Test => false,
789 Payable => false,
790 Allow => true,
791 Cfg => true,
792 Deprecated => false,
794 Fallback => false,
795 ErrorType => false,
796 Error => false,
797 Trace => false,
798 AbiName => false,
799 Event => false,
800 Indexed => false,
801 }
802 }
803
804 pub(crate) fn can_annotate_impl_item(
815 &self,
816 item: &ItemImplItem,
817 parent: ImplItemParent,
818 ) -> bool {
819 use AttributeKind::*;
820 match self.kind {
821 Unknown => true,
822 DocComment => self.direction == AttributeDirection::Outer,
823 Storage => matches!(item, ItemImplItem::Fn(..)),
824 Inline => matches!(item, ItemImplItem::Fn(..)),
825 Test => false,
826 Payable => matches!(item, ItemImplItem::Fn(..)) && parent == ImplItemParent::Contract,
827 Allow => true,
828 Cfg => true,
829 Deprecated => !matches!(item, ItemImplItem::Type(_)),
830 Fallback => false,
831 ErrorType => false,
832 Error => false,
833 Trace => matches!(item, ItemImplItem::Fn(..)),
834 AbiName => false,
835 Event => false,
836 Indexed => false,
837 }
838 }
839
840 pub(crate) fn can_annotate_abi_or_trait_provided_fn(&self, parent: TraitItemParent) -> bool {
851 use AttributeKind::*;
852 match self.kind {
853 Unknown => true,
854 DocComment => self.direction == AttributeDirection::Outer,
855 Storage => true,
856 Inline => true,
857 Test => false,
858 Payable => parent == TraitItemParent::Abi,
859 Allow => true,
860 Cfg => true,
861 Deprecated => true,
862 Fallback => false,
863 ErrorType => false,
864 Error => false,
865 Trace => true,
866 AbiName => false,
867 Event => false,
868 Indexed => false,
869 }
870 }
871
872 pub(crate) fn can_annotate_storage_entry(&self) -> bool {
873 use AttributeKind::*;
874 match self.kind {
875 Unknown => true,
876 DocComment => self.direction == AttributeDirection::Outer,
877 Storage => false,
878 Inline => false,
879 Test => false,
880 Payable => false,
881 Allow => true,
882 Cfg => true,
883 Deprecated => false,
885 Fallback => false,
886 ErrorType => false,
887 Error => false,
888 Trace => false,
889 AbiName => false,
890 Event => false,
891 Indexed => false,
892 }
893 }
894
895 pub(crate) fn can_annotate_configurable_field(&self) -> bool {
896 use AttributeKind::*;
897 match self.kind {
898 Unknown => true,
899 DocComment => self.direction == AttributeDirection::Outer,
900 Storage => false,
901 Inline => false,
902 Test => false,
903 Payable => false,
904 Allow => true,
905 Cfg => true,
906 Deprecated => true,
907 Fallback => false,
908 ErrorType => false,
909 Error => false,
910 Trace => false,
911 AbiName => false,
912 Event => false,
913 Indexed => false,
914 }
915 }
916
917 pub(crate) fn can_only_annotate_help(&self, target_friendly_name: &str) -> Vec<&'static str> {
918 use AttributeKind::*;
921 let help = match self.kind {
922 Unknown => vec![],
923 DocComment => match self.direction {
924 AttributeDirection::Inner => vec![
925 "Inner doc comments (`//!`) can only document modules and must be",
926 "at the beginning of the module file, before the module kind.",
927 ],
928 AttributeDirection::Outer => if target_friendly_name.starts_with("module kind") {
929 vec![
930 "To document modules, use inner doc comments (`//!`). E.g.:",
931 "//! This doc comment documents a module.",
932 ]
933 } else {
934 vec![]
935 },
936 },
937 Storage => {
938 if target_friendly_name == "function signature" {
939 vec![
940 "\"storage\" attribute can only annotate functions that have an implementation.",
941 "Function signatures in ABI and trait declarations do not have implementations.",
942 ]
943 } else {
944 vec![
945 "\"storage\" attribute can only annotate functions.",
946 ]
947 }
948 },
949 Inline => vec!["\"inline\" attribute can only annotate functions."],
950 Test => vec!["\"test\" attribute can only annotate module functions."],
951 Payable => vec![
952 "\"payable\" attribute can only annotate:",
953 " - ABI function signatures and their implementations in contracts,",
954 " - provided ABI functions.",
955 ],
956 Allow => vec![],
957 Cfg => vec![],
958 Deprecated => vec![
960 "\"deprecated\" attribute is currently not implemented for all elements that could be deprecated.",
961 ],
962 Fallback => vec!["\"fallback\" attribute can only annotate module functions in a contract module."],
963 ErrorType => vec!["\"error_type\" attribute can only annotate enums."],
964 Error => vec!["\"error\" attribute can only annotate enum variants of enums annotated with the \"error_type\" attribute."],
965 Trace => vec!["\"trace\" attribute can only annotate functions."],
966 AbiName => vec![
967 "\"abi_name\" attribute can only annotate structs and enums.",
968 ],
969 Event => vec![
970 "\"event\" attribute can only annotate structs or enums.",
971 ],
972 Indexed => vec![
973 "\"indexed\" attribute can only annotate struct fields.",
974 ],
975 };
976
977 if help.is_empty() && target_friendly_name.starts_with("module kind") {
978 vec!["Annotating module kinds (contract, script, predicate, or library) is currently not implemented."]
979 } else {
980 help
981 }
982 }
983}
984
985#[derive(Default, Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
997pub struct Attributes {
998 attributes: Arc<Vec<Attribute>>,
1008 deprecated_attr_index: Option<usize>,
1012}
1013
1014impl Attributes {
1015 pub fn new(attribute_decls: &[AttributeDecl]) -> Attributes {
1016 let mut attributes: Vec<Attribute> = vec![];
1017 for attr_decl in attribute_decls {
1018 let attrs = attr_decl.attribute.get().into_iter();
1019 for attr in attrs {
1020 let name = attr.name.as_str();
1021 let args = attr
1022 .args
1023 .as_ref()
1024 .map(|parens| {
1025 parens
1026 .get()
1027 .into_iter()
1028 .map(|arg| AttributeArg {
1029 name: arg.name.clone(),
1030 value: arg.value.clone(),
1031 span: arg.span(),
1032 })
1033 .collect()
1034 })
1035 .unwrap_or_default();
1036
1037 let attribute = Attribute {
1038 direction: (&attr_decl.hash_kind).into(),
1039 name: attr.name.clone(),
1040 args,
1041 span: attr_decl.span(),
1042 kind: AttributeKind::from_attribute_name(name),
1043 };
1044
1045 attributes.push(attribute);
1046 }
1047 }
1048
1049 Attributes {
1050 deprecated_attr_index: attributes
1051 .iter()
1052 .rposition(|attr| attr.kind == AttributeKind::Deprecated),
1053 attributes: Arc::new(attributes),
1054 }
1055 }
1056
1057 pub fn retain_from(other: &Attributes, f: impl Fn(&Attribute) -> bool) -> Self {
1060 let attributes = other
1061 .attributes
1062 .iter()
1063 .filter(|attr| f(attr))
1064 .cloned()
1065 .collect_vec();
1066 Self {
1067 deprecated_attr_index: attributes
1068 .iter()
1069 .rposition(|attr| attr.kind == AttributeKind::Deprecated),
1070 attributes: Arc::new(attributes),
1071 }
1072 }
1073
1074 pub fn is_empty(&self) -> bool {
1075 self.attributes.is_empty()
1076 }
1077
1078 pub fn first(&self) -> Option<&Attribute> {
1080 self.attributes.first()
1081 }
1082
1083 pub fn known_attribute_names(&self) -> &'static [&'static str] {
1084 KNOWN_ATTRIBUTE_NAMES
1085 }
1086
1087 pub fn all(&self) -> impl Iterator<Item = &Attribute> {
1088 self.attributes.iter()
1089 }
1090
1091 pub fn all_as_slice(&self) -> &[Attribute] {
1092 self.attributes.as_slice()
1093 }
1094
1095 pub fn all_by_kind<F>(&self, predicate: F) -> IndexMap<AttributeKind, Vec<&Attribute>>
1096 where
1097 F: Fn(&&Attribute) -> bool,
1098 {
1099 let mut result = IndexMap::<_, Vec<&Attribute>>::new();
1100 for attr in self.attributes.iter().filter(predicate) {
1101 result.entry(attr.kind).or_default().push(attr);
1102 }
1103 result
1104 }
1105
1106 pub fn of_kind(&self, kind: AttributeKind) -> impl Iterator<Item = &Attribute> {
1107 self.attributes.iter().filter(move |attr| attr.kind == kind)
1108 }
1109
1110 pub fn has_any_of_kind(&self, kind: AttributeKind) -> bool {
1111 self.of_kind(kind).any(|_| true)
1112 }
1113
1114 pub fn unknown(&self) -> impl Iterator<Item = &Attribute> {
1115 self.attributes
1116 .iter()
1117 .filter(|attr| attr.kind == AttributeKind::Unknown)
1118 }
1119
1120 pub fn has_allow_dead_code(&self) -> bool {
1121 self.has_allow(|arg| arg.is_allow_dead_code())
1122 }
1123
1124 pub fn has_allow_deprecated(&self) -> bool {
1125 self.has_allow(|arg| arg.is_allow_deprecated())
1126 }
1127
1128 fn has_allow(&self, arg_filter: impl Fn(&AttributeArg) -> bool) -> bool {
1129 self.of_kind(AttributeKind::Allow)
1130 .flat_map(|attribute| &attribute.args)
1131 .any(arg_filter)
1132 }
1133
1134 pub fn has_error_type(&self) -> bool {
1135 self.of_kind(AttributeKind::ErrorType).any(|_| true)
1136 }
1137
1138 pub fn has_error(&self) -> bool {
1139 self.of_kind(AttributeKind::Error).any(|_| true)
1140 }
1141
1142 pub fn inline(&self) -> Option<Inline> {
1145 match self
1149 .of_kind(AttributeKind::Inline)
1150 .last()?
1151 .args
1152 .last()?
1153 .name
1154 .as_str()
1155 {
1156 INLINE_NEVER_ARG_NAME => Some(Inline::Never),
1157 INLINE_ALWAYS_ARG_NAME => Some(Inline::Always),
1158 _ => None,
1159 }
1160 }
1161
1162 pub fn trace(&self) -> Option<Trace> {
1165 match self
1169 .of_kind(AttributeKind::Trace)
1170 .last()?
1171 .args
1172 .last()?
1173 .name
1174 .as_str()
1175 {
1176 TRACE_NEVER_ARG_NAME => Some(Trace::Never),
1177 TRACE_ALWAYS_ARG_NAME => Some(Trace::Always),
1178 _ => None,
1179 }
1180 }
1181
1182 pub fn purity(&self) -> Purity {
1185 let Some(storage_attr) = self.of_kind(AttributeKind::Storage).last() else {
1188 return Purity::Pure;
1189 };
1190
1191 let mut purity = Purity::Pure;
1192
1193 let mut add_impurity = |new_impurity, counter_impurity| {
1194 if purity == Purity::Pure {
1195 purity = new_impurity;
1196 } else if purity == counter_impurity {
1197 purity = Purity::ReadsWrites;
1198 }
1199 };
1200
1201 for arg in storage_attr.args.iter() {
1202 match arg.name.as_str() {
1203 STORAGE_READ_ARG_NAME => add_impurity(Purity::Reads, Purity::Writes),
1204 STORAGE_WRITE_ARG_NAME => add_impurity(Purity::Writes, Purity::Reads),
1205 _ => {}
1206 }
1207 }
1208
1209 purity
1210 }
1211
1212 pub fn deprecated(&self) -> Option<&Attribute> {
1215 self.deprecated_attr_index
1216 .map(|index| &self.attributes[index])
1217 }
1218
1219 pub fn abi_name(&self) -> Option<&Attribute> {
1222 self.of_kind(AttributeKind::AbiName).last()
1223 }
1224
1225 pub fn test(&self) -> Option<&Attribute> {
1228 self.of_kind(AttributeKind::Test).last()
1230 }
1231
1232 pub fn error(&self) -> Option<&Attribute> {
1235 self.of_kind(AttributeKind::Error).last()
1237 }
1238
1239 pub fn error_message(&self) -> Option<&String> {
1245 self.error().and_then(|error_attr| {
1247 error_attr
1248 .args
1249 .iter()
1250 .rfind(|arg| arg.is_error_message())
1251 .and_then(|arg| arg.get_string_opt(&Handler::default()).ok().flatten())
1252 })
1253 }
1254
1255 pub fn event(&self) -> Option<&Attribute> {
1258 self.of_kind(AttributeKind::Event).last()
1259 }
1260
1261 pub fn indexed(&self) -> Option<&Attribute> {
1264 self.of_kind(AttributeKind::Indexed).last()
1265 }
1266}
1267
1268pub struct AllowDeprecatedEnterToken {
1269 diff: i32,
1270}
1271
1272#[derive(Default)]
1273pub struct AllowDeprecatedState {
1274 allowed: u32,
1275}
1276impl AllowDeprecatedState {
1277 pub(crate) fn enter(&mut self, attributes: Attributes) -> AllowDeprecatedEnterToken {
1278 if attributes.has_allow_deprecated() {
1279 self.allowed += 1;
1280
1281 AllowDeprecatedEnterToken { diff: -1 }
1282 } else {
1283 AllowDeprecatedEnterToken { diff: 0 }
1284 }
1285 }
1286
1287 pub(crate) fn exit(&mut self, token: AllowDeprecatedEnterToken) {
1288 self.allowed = self.allowed.saturating_add_signed(token.diff);
1289 }
1290
1291 pub(crate) fn is_allowed(&self) -> bool {
1292 self.allowed > 0
1293 }
1294}