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 Require,
362}
363
364#[derive(Debug, Clone, Copy, PartialEq, Eq)]
366pub enum TraitItemParent {
367 Abi,
368 Trait,
369}
370
371#[derive(Debug, Clone, Copy, PartialEq, Eq)]
373pub enum StructOrEnumField {
374 StructField,
375 EnumField,
376}
377
378impl AttributeKind {
379 pub fn from_attribute_name(name: &str) -> Self {
380 match name {
381 DOC_COMMENT_ATTRIBUTE_NAME => AttributeKind::DocComment,
382 STORAGE_ATTRIBUTE_NAME => AttributeKind::Storage,
383 INLINE_ATTRIBUTE_NAME => AttributeKind::Inline,
384 TEST_ATTRIBUTE_NAME => AttributeKind::Test,
385 PAYABLE_ATTRIBUTE_NAME => AttributeKind::Payable,
386 ALLOW_ATTRIBUTE_NAME => AttributeKind::Allow,
387 CFG_ATTRIBUTE_NAME => AttributeKind::Cfg,
388 DEPRECATED_ATTRIBUTE_NAME => AttributeKind::Deprecated,
389 FALLBACK_ATTRIBUTE_NAME => AttributeKind::Fallback,
390 ERROR_TYPE_ATTRIBUTE_NAME => AttributeKind::ErrorType,
391 ERROR_ATTRIBUTE_NAME => AttributeKind::Error,
392 TRACE_ATTRIBUTE_NAME => AttributeKind::Trace,
393 ABI_NAME_ATTRIBUTE_NAME => AttributeKind::AbiName,
394 EVENT_ATTRIBUTE_NAME => AttributeKind::Event,
395 INDEXED_ATTRIBUTE_NAME => AttributeKind::Indexed,
396 REQUIRE_ATTRIBUTE_NAME => AttributeKind::Require,
397 _ => AttributeKind::Unknown,
398 }
399 }
400
401 pub fn allows_multiple(&self) -> bool {
411 use AttributeKind::*;
412 match self {
413 Unknown => true,
414 DocComment => true,
415 Storage => false,
416 Inline => false,
417 Test => false,
418 Payable => false,
419 Allow => true,
420 Cfg => true,
421 Deprecated => false,
422 Fallback => false,
423 ErrorType => false,
424 Error => false,
425 Trace => false,
426 AbiName => false,
427 Event => false,
428 Indexed => false,
429 Require => false,
430 }
431 }
432}
433
434impl Attribute {
435 pub fn is_doc_comment(&self) -> bool {
436 self.kind == AttributeKind::DocComment
437 }
438
439 pub fn is_inner(&self) -> bool {
440 self.direction == AttributeDirection::Inner
441 }
442
443 pub fn is_outer(&self) -> bool {
444 self.direction == AttributeDirection::Outer
445 }
446
447 pub(crate) fn args_multiplicity(&self) -> ArgsMultiplicity {
448 use ArgsMultiplicity as Multiplicity;
449 use AttributeKind::*;
450 match self.kind {
451 Unknown => Multiplicity::arbitrary(),
452 DocComment => Multiplicity::exactly(1),
456 Storage => Multiplicity::between(1, 2),
458 Inline => Multiplicity::exactly(1),
460 Test => Multiplicity::at_most(1),
462 Payable => Multiplicity::zero(),
463 Allow => Multiplicity::at_least(1),
464 Cfg => Multiplicity::exactly(1),
465 Deprecated => Multiplicity::at_most(1),
467 Fallback => Multiplicity::zero(),
468 ErrorType => Multiplicity::zero(),
469 Error => Multiplicity::exactly(1),
470 Trace => Multiplicity::exactly(1),
472 AbiName => Multiplicity::exactly(1),
473 Event => Multiplicity::zero(),
474 Indexed => Multiplicity::zero(),
475 Require => Multiplicity::between(1, 2),
476 }
477 }
478
479 pub(crate) fn check_args_multiplicity(&self, handler: &Handler) -> Result<(), ErrorEmitted> {
480 if !self.args_multiplicity().contains(self.args.len()) {
481 Err(handler.emit_err(
482 ConvertParseTreeError::InvalidAttributeArgsMultiplicity {
483 span: if self.args.is_empty() {
484 self.name.span()
485 } else {
486 Span::join(
487 self.args.first().unwrap().span(),
488 &self.args.last().unwrap().span,
489 )
490 },
491 attribute: self.name.clone(),
492 args_multiplicity: (&self.args_multiplicity()).into(),
493 num_of_args: self.args.len(),
494 }
495 .into(),
496 ))
497 } else {
498 Ok(())
499 }
500 }
501
502 pub(crate) fn can_have_arguments(&self) -> bool {
503 let args_multiplicity = self.args_multiplicity();
504 args_multiplicity.min != 0 || args_multiplicity.max != 0
505 }
506
507 pub(crate) fn expected_args(&self) -> ExpectedArgs {
508 use AttributeKind::*;
509 use ExpectedArgs::*;
510 match self.kind {
511 Unknown => Any,
512 DocComment => Any,
513 Storage => MustBeIn(vec![STORAGE_READ_ARG_NAME, STORAGE_WRITE_ARG_NAME]),
514 Inline => MustBeIn(vec![INLINE_ALWAYS_ARG_NAME, INLINE_NEVER_ARG_NAME]),
515 Test => MustBeIn(vec![TEST_SHOULD_REVERT_ARG_NAME]),
516 Payable => None,
517 Allow => ShouldBeIn(vec![ALLOW_DEAD_CODE_ARG_NAME, ALLOW_DEPRECATED_ARG_NAME]),
518 Cfg => {
519 let mut args = vec![
520 CFG_PROGRAM_TYPE_ARG_NAME,
522 CFG_TARGET_ARG_NAME,
523 ];
524 args.extend(Feature::CFG.iter().sorted());
525 MustBeIn(args)
526 }
527 Deprecated => MustBeIn(vec![DEPRECATED_NOTE_ARG_NAME]),
528 Fallback => None,
529 ErrorType => None,
530 Error => MustBeIn(vec![ERROR_M_ARG_NAME]),
531 Trace => MustBeIn(vec![TRACE_ALWAYS_ARG_NAME, TRACE_NEVER_ARG_NAME]),
532 AbiName => MustBeIn(vec![ABI_NAME_NAME_ARG_NAME]),
533 Event => None,
534 Indexed => None,
535 Require => MustBeIn(vec![REQUIRE_ARG_NAME_TRIVIALLY_DECODABLE]),
536 }
537 }
538
539 pub(crate) fn args_expect_values(&self) -> ArgsExpectValues {
540 use ArgsExpectValues::*;
541 use AttributeKind::*;
542 match self.kind {
543 Unknown => Maybe,
544 DocComment => No,
546 Storage => No,
547 Inline => No,
548 Test => Maybe,
550 Payable => No,
551 Allow => No,
552 Cfg => Yes,
553 Deprecated => Yes,
555 Fallback => No,
556 ErrorType => No,
557 Error => Yes,
559 Trace => No,
560 AbiName => Yes,
561 Event => No,
562 Indexed => No,
563 Require => Yes,
565 }
566 }
567
568 pub(crate) fn can_annotate_module_kind(&self) -> bool {
569 use AttributeKind::*;
570 match self.kind {
571 Unknown => false,
572 DocComment => self.direction == AttributeDirection::Inner,
573 Storage => false,
574 Inline => false,
575 Test => false,
576 Payable => false,
577 Allow => false,
578 Cfg => false,
579 Deprecated => false,
582 Fallback => false,
583 ErrorType => false,
584 Error => false,
585 Trace => false,
586 AbiName => false,
587 Event => false,
588 Indexed => false,
589 Require => false,
590 }
591 }
592
593 pub(crate) fn can_annotate_abi(&self) -> bool {
594 use AttributeKind::*;
595 match self.kind {
596 Unknown => true,
597 DocComment => self.direction == AttributeDirection::Outer,
598 Storage => false,
599 Inline => false,
600 Test => false,
601 Payable => false,
602 Allow => true,
603 Cfg => true,
604 Deprecated => false,
606 Fallback => false,
607 ErrorType => false,
608 Error => false,
609 Trace => false,
610 AbiName => false,
611 Event => false,
612 Indexed => false,
613 Require => false,
614 }
615 }
616
617 pub(crate) fn can_annotate_item_kind(&self, item_kind: &ItemKind) -> bool {
618 if matches!(item_kind, ItemKind::Error(..)) {
629 return true;
630 }
631
632 use AttributeKind::*;
633 match self.kind {
634 Unknown => !matches!(item_kind, ItemKind::Submodule(_)),
635 DocComment => {
637 self.direction == AttributeDirection::Outer
638 && !matches!(item_kind, ItemKind::Submodule(_))
639 }
640 Storage => matches!(item_kind, ItemKind::Fn(_)),
641 Inline => matches!(item_kind, ItemKind::Fn(_)),
642 Test => matches!(item_kind, ItemKind::Fn(_)),
643 Payable => false,
644 Allow => !matches!(item_kind, ItemKind::Submodule(_)),
645 Cfg => !matches!(item_kind, ItemKind::Submodule(_)),
646 Deprecated => match item_kind {
648 ItemKind::Submodule(_) => false,
649 ItemKind::Use(_) => false,
650 ItemKind::Struct(_) => true,
651 ItemKind::Enum(_) => true,
652 ItemKind::Fn(_) => true,
653 ItemKind::Trait(_) => false,
654 ItemKind::Impl(_) => false,
655 ItemKind::Abi(_) => false,
656 ItemKind::Const(_) => true,
657 ItemKind::Storage(_) => false,
658 ItemKind::Configurable(_) => false,
661 ItemKind::TypeAlias(_) => false,
662 ItemKind::Error(_, _) => true,
663 },
664 Fallback => matches!(item_kind, ItemKind::Fn(_)),
665 ErrorType => matches!(item_kind, ItemKind::Enum(_)),
666 Error => false,
667 Trace => matches!(item_kind, ItemKind::Fn(_)),
668 AbiName => matches!(item_kind, ItemKind::Struct(_) | ItemKind::Enum(_)),
669 Event => matches!(item_kind, ItemKind::Struct(_) | ItemKind::Enum(_)),
670 Indexed => false,
671 Require => matches!(item_kind, ItemKind::Struct(_) | ItemKind::Enum(_)),
672 }
673 }
674
675 pub(crate) fn can_annotate_struct_or_enum_field(
680 &self,
681 struct_or_enum_field: StructOrEnumField,
682 ) -> bool {
683 use AttributeKind::*;
684 match self.kind {
685 Unknown => true,
686 DocComment => self.direction == AttributeDirection::Outer,
687 Storage => false,
688 Inline => false,
689 Test => false,
690 Payable => false,
691 Allow => true,
692 Cfg => true,
693 Deprecated => true,
694 Fallback => false,
695 ErrorType => false,
696 Error => struct_or_enum_field == StructOrEnumField::EnumField,
697 Trace => false,
698 AbiName => false,
699 Event => false,
700 Indexed => matches!(struct_or_enum_field, StructOrEnumField::StructField),
701 Require => false,
702 }
703 }
704
705 pub(crate) fn can_annotate_abi_or_trait_interface_item(
716 &self,
717 item: &ItemTraitItem,
718 parent: TraitItemParent,
719 ) -> bool {
720 use AttributeKind::*;
721 match self.kind {
722 Unknown => true,
723 DocComment => self.direction == AttributeDirection::Outer,
724 Storage => matches!(item, ItemTraitItem::Fn(..)),
725 Inline => false,
728 Test => false,
729 Payable => parent == TraitItemParent::Abi && matches!(item, ItemTraitItem::Fn(..)),
730 Allow => true,
731 Cfg => true,
732 Deprecated => false,
734 Fallback => false,
735 ErrorType => false,
736 Error => false,
737 Trace => false,
740 AbiName => false,
741 Event => false,
742 Indexed => false,
743 Require => parent == TraitItemParent::Abi && matches!(item, ItemTraitItem::Fn(..)),
744 }
745 }
746
747 pub(crate) fn can_annotate_abi_or_trait_interface_fn(&self, parent: TraitItemParent) -> bool {
756 use AttributeKind::*;
757 match self.kind {
758 Unknown => true,
759 DocComment => self.direction == AttributeDirection::Outer,
760 Storage => true,
761 Inline => false,
764 Test => false,
765 Payable => parent == TraitItemParent::Abi,
766 Allow => true,
767 Cfg => true,
768 Deprecated => false,
770 Fallback => false,
771 ErrorType => false,
772 Error => false,
773 Trace => false,
776 AbiName => false,
777 Event => false,
778 Indexed => false,
779 Require => false,
780 }
781 }
782
783 pub(crate) fn can_annotate_abi_or_trait_interface_const(
792 &self,
793 _parent: TraitItemParent,
794 ) -> bool {
795 use AttributeKind::*;
796 match self.kind {
797 Unknown => true,
798 DocComment => self.direction == AttributeDirection::Outer,
799 Storage => false,
800 Inline => false,
801 Test => false,
802 Payable => false,
803 Allow => true,
804 Cfg => true,
805 Deprecated => false,
807 Fallback => false,
808 ErrorType => false,
809 Error => false,
810 Trace => false,
811 AbiName => false,
812 Event => false,
813 Indexed => false,
814 Require => false,
815 }
816 }
817
818 pub(crate) fn can_annotate_impl_item(
829 &self,
830 item: &ItemImplItem,
831 parent: ImplItemParent,
832 ) -> bool {
833 use AttributeKind::*;
834 match self.kind {
835 Unknown => true,
836 DocComment => self.direction == AttributeDirection::Outer,
837 Storage => matches!(item, ItemImplItem::Fn(..)),
838 Inline => matches!(item, ItemImplItem::Fn(..)),
839 Test => false,
840 Payable => matches!(item, ItemImplItem::Fn(..)) && parent == ImplItemParent::Contract,
841 Allow => true,
842 Cfg => true,
843 Deprecated => !matches!(item, ItemImplItem::Type(_)),
844 Fallback => false,
845 ErrorType => false,
846 Error => false,
847 Trace => matches!(item, ItemImplItem::Fn(..)),
848 AbiName => false,
849 Event => false,
850 Indexed => false,
851 Require => false,
852 }
853 }
854
855 pub(crate) fn can_annotate_abi_or_trait_provided_fn(&self, parent: TraitItemParent) -> bool {
866 use AttributeKind::*;
867 match self.kind {
868 Unknown => true,
869 DocComment => self.direction == AttributeDirection::Outer,
870 Storage => true,
871 Inline => true,
872 Test => false,
873 Payable => parent == TraitItemParent::Abi,
874 Allow => true,
875 Cfg => true,
876 Deprecated => true,
877 Fallback => false,
878 ErrorType => false,
879 Error => false,
880 Trace => true,
881 AbiName => false,
882 Event => false,
883 Indexed => false,
884 Require => false,
885 }
886 }
887
888 pub(crate) fn can_annotate_storage_entry(&self) -> bool {
889 use AttributeKind::*;
890 match self.kind {
891 Unknown => true,
892 DocComment => self.direction == AttributeDirection::Outer,
893 Storage => false,
894 Inline => false,
895 Test => false,
896 Payable => false,
897 Allow => true,
898 Cfg => true,
899 Deprecated => false,
901 Fallback => false,
902 ErrorType => false,
903 Error => false,
904 Trace => false,
905 AbiName => false,
906 Event => false,
907 Indexed => false,
908 Require => false,
909 }
910 }
911
912 pub(crate) fn can_annotate_configurable_field(&self) -> bool {
913 use AttributeKind::*;
914 match self.kind {
915 Unknown => true,
916 DocComment => self.direction == AttributeDirection::Outer,
917 Storage => false,
918 Inline => false,
919 Test => false,
920 Payable => false,
921 Allow => true,
922 Cfg => true,
923 Deprecated => true,
924 Fallback => false,
925 ErrorType => false,
926 Error => false,
927 Trace => false,
928 AbiName => false,
929 Event => false,
930 Indexed => false,
931 Require => false,
932 }
933 }
934
935 pub(crate) fn can_only_annotate_help(&self, target_friendly_name: &str) -> Vec<&'static str> {
936 use AttributeKind::*;
939 let help = match self.kind {
940 Unknown => vec![],
941 DocComment => match self.direction {
942 AttributeDirection::Inner => vec![
943 "Inner doc comments (`//!`) can only document modules and must be",
944 "at the beginning of the module file, before the module kind.",
945 ],
946 AttributeDirection::Outer => if target_friendly_name.starts_with("module kind") {
947 vec![
948 "To document modules, use inner doc comments (`//!`). E.g.:",
949 "//! This doc comment documents a module.",
950 ]
951 } else {
952 vec![]
953 },
954 },
955 Storage => {
956 if target_friendly_name == "function signature" {
957 vec![
958 "\"storage\" attribute can only annotate functions that have an implementation.",
959 "Function signatures in ABI and trait declarations do not have implementations.",
960 ]
961 } else {
962 vec![
963 "\"storage\" attribute can only annotate functions.",
964 ]
965 }
966 },
967 Inline => vec!["\"inline\" attribute can only annotate functions."],
968 Test => vec!["\"test\" attribute can only annotate module functions."],
969 Payable => vec![
970 "\"payable\" attribute can only annotate:",
971 " - ABI function signatures and their implementations in contracts,",
972 " - provided ABI functions.",
973 ],
974 Allow => vec![],
975 Cfg => vec![],
976 Deprecated => vec![
978 "\"deprecated\" attribute is currently not implemented for all elements that could be deprecated.",
979 ],
980 Fallback => vec!["\"fallback\" attribute can only annotate module functions in a contract module."],
981 ErrorType => vec!["\"error_type\" attribute can only annotate enums."],
982 Error => vec!["\"error\" attribute can only annotate enum variants of enums annotated with the \"error_type\" attribute."],
983 Trace => vec!["\"trace\" attribute can only annotate functions."],
984 AbiName => vec![
985 "\"abi_name\" attribute can only annotate structs and enums.",
986 ],
987 Event => vec![
988 "\"event\" attribute can only annotate structs or enums.",
989 ],
990 Indexed => vec![
991 "\"indexed\" attribute can only annotate struct fields.",
992 ],
993 Require => vec![
994 "\"require\" attribute can only annotate structs and enums.",
995 ],
996 };
997
998 if help.is_empty() && target_friendly_name.starts_with("module kind") {
999 vec!["Annotating module kinds (contract, script, predicate, or library) is currently not implemented."]
1000 } else {
1001 help
1002 }
1003 }
1004}
1005
1006#[derive(Default, Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
1018pub struct Attributes {
1019 attributes: Arc<Vec<Attribute>>,
1029 deprecated_attr_index: Option<usize>,
1033}
1034
1035impl Attributes {
1036 pub fn new(attribute_decls: &[AttributeDecl]) -> Attributes {
1037 let mut attributes: Vec<Attribute> = vec![];
1038 for attr_decl in attribute_decls {
1039 let attrs = attr_decl.attribute.get().into_iter();
1040 for attr in attrs {
1041 let name = attr.name.as_str();
1042 let args = attr
1043 .args
1044 .as_ref()
1045 .map(|parens| {
1046 parens
1047 .get()
1048 .into_iter()
1049 .map(|arg| AttributeArg {
1050 name: arg.name.clone(),
1051 value: arg.value.clone(),
1052 span: arg.span(),
1053 })
1054 .collect()
1055 })
1056 .unwrap_or_default();
1057
1058 let attribute = Attribute {
1059 direction: (&attr_decl.hash_kind).into(),
1060 name: attr.name.clone(),
1061 args,
1062 span: attr_decl.span(),
1063 kind: AttributeKind::from_attribute_name(name),
1064 };
1065
1066 attributes.push(attribute);
1067 }
1068 }
1069
1070 Attributes {
1071 deprecated_attr_index: attributes
1072 .iter()
1073 .rposition(|attr| attr.kind == AttributeKind::Deprecated),
1074 attributes: Arc::new(attributes),
1075 }
1076 }
1077
1078 pub fn retain_from(other: &Attributes, f: impl Fn(&Attribute) -> bool) -> Self {
1081 let attributes = other
1082 .attributes
1083 .iter()
1084 .filter(|attr| f(attr))
1085 .cloned()
1086 .collect_vec();
1087 Self {
1088 deprecated_attr_index: attributes
1089 .iter()
1090 .rposition(|attr| attr.kind == AttributeKind::Deprecated),
1091 attributes: Arc::new(attributes),
1092 }
1093 }
1094
1095 pub fn is_empty(&self) -> bool {
1096 self.attributes.is_empty()
1097 }
1098
1099 pub fn first(&self) -> Option<&Attribute> {
1101 self.attributes.first()
1102 }
1103
1104 pub fn known_attribute_names(&self) -> &'static [&'static str] {
1105 KNOWN_ATTRIBUTE_NAMES
1106 }
1107
1108 pub fn all(&self) -> impl Iterator<Item = &Attribute> {
1109 self.attributes.iter()
1110 }
1111
1112 pub fn all_as_slice(&self) -> &[Attribute] {
1113 self.attributes.as_slice()
1114 }
1115
1116 pub fn all_by_kind<F>(&self, predicate: F) -> IndexMap<AttributeKind, Vec<&Attribute>>
1117 where
1118 F: Fn(&&Attribute) -> bool,
1119 {
1120 let mut result = IndexMap::<_, Vec<&Attribute>>::new();
1121 for attr in self.attributes.iter().filter(predicate) {
1122 result.entry(attr.kind).or_default().push(attr);
1123 }
1124 result
1125 }
1126
1127 pub fn of_kind(&self, kind: AttributeKind) -> impl Iterator<Item = &Attribute> {
1128 self.attributes.iter().filter(move |attr| attr.kind == kind)
1129 }
1130
1131 pub fn has_any_of_kind(&self, kind: AttributeKind) -> bool {
1132 self.of_kind(kind).any(|_| true)
1133 }
1134
1135 pub fn unknown(&self) -> impl Iterator<Item = &Attribute> {
1136 self.attributes
1137 .iter()
1138 .filter(|attr| attr.kind == AttributeKind::Unknown)
1139 }
1140
1141 pub fn has_allow_dead_code(&self) -> bool {
1142 self.has_allow(|arg| arg.is_allow_dead_code())
1143 }
1144
1145 pub fn has_allow_deprecated(&self) -> bool {
1146 self.has_allow(|arg| arg.is_allow_deprecated())
1147 }
1148
1149 fn has_allow(&self, arg_filter: impl Fn(&AttributeArg) -> bool) -> bool {
1150 self.of_kind(AttributeKind::Allow)
1151 .flat_map(|attribute| &attribute.args)
1152 .any(arg_filter)
1153 }
1154
1155 pub fn has_error_type(&self) -> bool {
1156 self.of_kind(AttributeKind::ErrorType).any(|_| true)
1157 }
1158
1159 pub fn has_error(&self) -> bool {
1160 self.of_kind(AttributeKind::Error).any(|_| true)
1161 }
1162
1163 pub fn inline(&self) -> Option<Inline> {
1166 match self
1170 .of_kind(AttributeKind::Inline)
1171 .last()?
1172 .args
1173 .last()?
1174 .name
1175 .as_str()
1176 {
1177 INLINE_NEVER_ARG_NAME => Some(Inline::Never),
1178 INLINE_ALWAYS_ARG_NAME => Some(Inline::Always),
1179 _ => None,
1180 }
1181 }
1182
1183 pub fn trace(&self) -> Option<Trace> {
1186 match self
1190 .of_kind(AttributeKind::Trace)
1191 .last()?
1192 .args
1193 .last()?
1194 .name
1195 .as_str()
1196 {
1197 TRACE_NEVER_ARG_NAME => Some(Trace::Never),
1198 TRACE_ALWAYS_ARG_NAME => Some(Trace::Always),
1199 _ => None,
1200 }
1201 }
1202
1203 pub fn purity(&self) -> Purity {
1206 let Some(storage_attr) = self.of_kind(AttributeKind::Storage).last() else {
1209 return Purity::Pure;
1210 };
1211
1212 let mut purity = Purity::Pure;
1213
1214 let mut add_impurity = |new_impurity, counter_impurity| {
1215 if purity == Purity::Pure {
1216 purity = new_impurity;
1217 } else if purity == counter_impurity {
1218 purity = Purity::ReadsWrites;
1219 }
1220 };
1221
1222 for arg in storage_attr.args.iter() {
1223 match arg.name.as_str() {
1224 STORAGE_READ_ARG_NAME => add_impurity(Purity::Reads, Purity::Writes),
1225 STORAGE_WRITE_ARG_NAME => add_impurity(Purity::Writes, Purity::Reads),
1226 _ => {}
1227 }
1228 }
1229
1230 purity
1231 }
1232
1233 pub fn deprecated(&self) -> Option<&Attribute> {
1236 self.deprecated_attr_index
1237 .map(|index| &self.attributes[index])
1238 }
1239
1240 pub fn abi_name(&self) -> Option<&Attribute> {
1243 self.of_kind(AttributeKind::AbiName).last()
1244 }
1245
1246 pub fn test(&self) -> Option<&Attribute> {
1249 self.of_kind(AttributeKind::Test).last()
1251 }
1252
1253 pub fn error(&self) -> Option<&Attribute> {
1256 self.of_kind(AttributeKind::Error).last()
1258 }
1259
1260 pub fn error_message(&self) -> Option<&String> {
1266 self.error().and_then(|error_attr| {
1268 error_attr
1269 .args
1270 .iter()
1271 .rfind(|arg| arg.is_error_message())
1272 .and_then(|arg| arg.get_string_opt(&Handler::default()).ok().flatten())
1273 })
1274 }
1275
1276 pub fn event(&self) -> Option<&Attribute> {
1279 self.of_kind(AttributeKind::Event).last()
1280 }
1281
1282 pub fn indexed(&self) -> Option<&Attribute> {
1285 self.of_kind(AttributeKind::Indexed).last()
1286 }
1287}
1288
1289pub struct AllowDeprecatedEnterToken {
1290 diff: i32,
1291}
1292
1293#[derive(Default)]
1294pub struct AllowDeprecatedState {
1295 allowed: u32,
1296}
1297impl AllowDeprecatedState {
1298 pub(crate) fn enter(&mut self, attributes: Attributes) -> AllowDeprecatedEnterToken {
1299 if attributes.has_allow_deprecated() {
1300 self.allowed += 1;
1301
1302 AllowDeprecatedEnterToken { diff: -1 }
1303 } else {
1304 AllowDeprecatedEnterToken { diff: 0 }
1305 }
1306 }
1307
1308 pub(crate) fn exit(&mut self, token: AllowDeprecatedEnterToken) {
1309 self.allowed = self.allowed.saturating_add_signed(token.diff);
1310 }
1311
1312 pub(crate) fn is_allowed(&self) -> bool {
1313 self.allowed > 0
1314 }
1315}