Skip to main content

sway_error/
warning.rs

1use crate::{
2    diagnostic::{Code, Diagnostic, Hint, Issue, Reason, ToDiagnostic},
3    error::TrivialCheckFailedData,
4    formatting::{
5        did_you_mean_help, first_line, num_to_str, sequence_to_list, sequence_to_str, Enclosing,
6        Indent,
7    },
8};
9
10use core::fmt;
11
12use either::Either;
13
14use sway_types::{Ident, IdentUnique, SourceId, Span, Spanned};
15
16// TODO: since moving to using Idents instead of strings,
17// the warning_content will usually contain a duplicate of the span.
18#[derive(Debug, Clone, PartialEq, Eq, Hash)]
19pub struct CompileWarning {
20    pub span: Span,
21    pub warning_content: Warning,
22}
23
24impl Spanned for CompileWarning {
25    fn span(&self) -> Span {
26        self.span.clone()
27    }
28}
29
30impl CompileWarning {
31    pub fn to_friendly_warning_string(&self) -> String {
32        self.warning_content.to_string()
33    }
34
35    pub fn source_id(&self) -> Option<SourceId> {
36        self.span.source_id().cloned()
37    }
38}
39
40#[derive(Debug, Clone, PartialEq, Eq, Hash)]
41pub enum Warning {
42    NonClassCaseStructName {
43        struct_name: Ident,
44    },
45    NonClassCaseTypeParameter {
46        name: Ident,
47    },
48    NonClassCaseTraitName {
49        name: Ident,
50    },
51    NonClassCaseEnumName {
52        enum_name: Ident,
53    },
54    NonClassCaseEnumVariantName {
55        variant_name: Ident,
56    },
57    NonSnakeCaseStructFieldName {
58        field_name: Ident,
59    },
60    NonSnakeCaseFunctionName {
61        name: Ident,
62    },
63    NonScreamingSnakeCaseConstName {
64        name: Ident,
65    },
66    UnusedReturnValue {
67        r#type: String,
68    },
69    SimilarMethodFound {
70        lib: Ident,
71        module: Ident,
72        name: Ident,
73    },
74    ShadowsOtherSymbol {
75        name: Ident,
76    },
77    AsmBlockIsEmpty,
78    UninitializedAsmRegShadowsItem {
79        /// Text "Constant" or "Configurable" or "Variable".
80        /// Denotes the type of the `item` that shadows the uninitialized ASM register.
81        constant_or_configurable_or_variable: &'static str,
82        /// The name of the item that shadows the register, that points to the name in
83        /// the item declaration.
84        item: IdentUnique,
85    },
86    OverridingTraitImplementation,
87    DeadDeclaration,
88    DeadEnumDeclaration,
89    DeadFunctionDeclaration,
90    DeadStructDeclaration,
91    DeadTrait,
92    UnreachableCode,
93    DeadEnumVariant {
94        variant_name: Ident,
95    },
96    DeadMethod,
97    StructFieldNeverRead,
98    ShadowingReservedRegister {
99        reg_name: Ident,
100    },
101    DeadStorageDeclaration,
102    DeadStorageDeclarationForFunction {
103        unneeded_attrib: String,
104    },
105    MatchExpressionUnreachableArm {
106        match_value: Span,
107        match_type: String,
108        // Either preceding non catch-all arms or a single interior catch-all arm.
109        preceding_arms: Either<Vec<Span>, Span>,
110        unreachable_arm: Span,
111        is_last_arm: bool,
112        is_catch_all_arm: bool,
113    },
114    UnknownAttribute {
115        attribute: IdentUnique,
116        known_attributes: &'static [&'static str],
117    },
118    UnknownAttributeArg {
119        attribute: Ident,
120        arg: IdentUnique,
121        expected_args: Vec<&'static str>,
122    },
123    EffectAfterInteraction {
124        effect: String,
125        effect_in_suggestion: String,
126        block_name: Ident,
127    },
128    UsingDeprecated {
129        deprecated_element: DeprecatedElement,
130        deprecated_element_name: String,
131        help: Option<String>,
132    },
133    InherentImplForExternalType {
134        type_name: String,
135        type_definition_span: Option<Span>,
136    },
137    DuplicatedStorageKey {
138        first_field: IdentUnique,
139        first_field_full_name: String,
140        first_field_key_is_compiler_generated: bool,
141        second_field: IdentUnique,
142        second_field_full_name: String,
143        second_field_key_is_compiler_generated: bool,
144        key: String,
145    },
146    ErrorTypeEmptyEnum {
147        enum_name: IdentUnique,
148    },
149    ErrorEmptyErrorMessage {
150        enum_name: Ident,
151        enum_variant_name: Ident,
152    },
153    ErrorDuplicatedErrorMessage {
154        last_occurrence: Span,
155        previous_occurrences: Vec<Span>,
156    },
157    TrivialCheckFailed(TrivialCheckFailedData),
158}
159
160/// Elements that can be deprecated.
161#[derive(Debug, Clone, PartialEq, Eq, Hash)]
162pub enum DeprecatedElement {
163    Struct,
164    StructField,
165    Enum,
166    EnumVariant,
167    Function,
168    Const,
169    Configurable,
170}
171
172impl fmt::Display for DeprecatedElement {
173    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
174        match self {
175            Self::Struct => write!(f, "Struct"),
176            Self::StructField => write!(f, "Struct field"),
177            Self::Enum => write!(f, "Enum"),
178            Self::EnumVariant => write!(f, "Enum variant"),
179            Self::Function => write!(f, "Function"),
180            Self::Const => write!(f, "Constant"),
181            Self::Configurable => write!(f, "Configurable"),
182        }
183    }
184}
185
186impl fmt::Display for Warning {
187    // This trait requires `fmt` with this exact signature.
188    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
189        use sway_types::style::*;
190        use Warning::*;
191        match self {
192            NonClassCaseStructName { struct_name } => {
193                write!(f,
194                "Struct name \"{}\" is not idiomatic. Structs should have a ClassCase name, like \
195                 \"{}\".",
196                struct_name,
197                to_upper_camel_case(struct_name.as_str())
198            )
199            }
200            NonClassCaseTypeParameter { name } => {
201                write!(f,
202                "Type parameter \"{}\" is not idiomatic. TypeParameters should have a ClassCase name, like \
203                 \"{}\".",
204                name,
205                to_upper_camel_case(name.as_str())
206            )
207            }
208            NonClassCaseTraitName { name } => {
209                write!(f,
210                "Trait name \"{}\" is not idiomatic. Traits should have a ClassCase name, like \
211                 \"{}\".",
212                name,
213                to_upper_camel_case(name.as_str())
214            )
215            }
216            NonClassCaseEnumName { enum_name } => write!(
217                f,
218                "Enum \"{}\"'s capitalization is not idiomatic. Enums should have a ClassCase \
219                 name, like \"{}\".",
220                enum_name,
221                to_upper_camel_case(enum_name.as_str())
222            ),
223            NonSnakeCaseStructFieldName { field_name } => write!(
224                f,
225                "Struct field name \"{}\" is not idiomatic. Struct field names should have a \
226                 snake_case name, like \"{}\".",
227                field_name,
228                to_snake_case(field_name.as_str())
229            ),
230            NonClassCaseEnumVariantName { variant_name } => write!(
231                f,
232                "Enum variant name \"{}\" is not idiomatic. Enum variant names should be \
233                 ClassCase, like \"{}\".",
234                variant_name,
235                to_upper_camel_case(variant_name.as_str())
236            ),
237            NonSnakeCaseFunctionName { name } => {
238                write!(f,
239                "Function name \"{}\" is not idiomatic. Function names should be snake_case, like \
240                 \"{}\".",
241                name,
242                to_snake_case(name.as_str())
243            )
244            }
245            NonScreamingSnakeCaseConstName { name } => {
246                write!(
247                    f,
248                    "Constant name \"{name}\" is not idiomatic. Constant names should be SCREAMING_SNAKE_CASE, like \
249                    \"{}\".",
250                    to_screaming_snake_case(name.as_str()),
251                )
252            },
253            UnusedReturnValue { r#type } => write!(
254                f,
255                "This returns a value of type \"{type}\", which is not assigned to anything and is \
256                 ignored."
257            ),
258            SimilarMethodFound { lib, module, name } => write!(
259                f,
260                "A method with the same name was found for type {name} in dependency \"{lib}::{module}\". \
261                 Traits must be in scope in order to access their methods. "
262            ),
263            ShadowsOtherSymbol { name } => write!(
264                f,
265                "This shadows another symbol in this scope with the same name \"{name}\"."
266            ),
267            AsmBlockIsEmpty => write!(
268                f,
269                "This ASM block is empty."
270            ),
271            UninitializedAsmRegShadowsItem { constant_or_configurable_or_variable, item } => write!(
272                f,
273                "This uninitialized register is shadowing a {}. You probably meant to also initialize it, like \"{item}: {item}\".",
274                constant_or_configurable_or_variable.to_ascii_lowercase(),
275            ),
276            OverridingTraitImplementation => write!(
277                f,
278                "This trait implementation overrides another one that was previously defined."
279            ),
280            DeadDeclaration => write!(f, "This declaration is never used."),
281            DeadEnumDeclaration => write!(f, "This enum is never used."),
282            DeadStructDeclaration => write!(f, "This struct is never used."),
283            DeadFunctionDeclaration => write!(f, "This function is never called."),
284            UnreachableCode => write!(f, "This code is unreachable."),
285            DeadEnumVariant { variant_name } => {
286                write!(f, "Enum variant {variant_name} is never constructed.")
287            }
288            DeadTrait => write!(f, "This trait is never implemented."),
289            DeadMethod => write!(f, "This method is never called."),
290            StructFieldNeverRead => write!(f, "This struct field is never accessed."),
291            ShadowingReservedRegister { reg_name } => write!(
292                f,
293                "This register declaration shadows the reserved register, \"{reg_name}\"."
294            ),
295            DeadStorageDeclaration => write!(
296                f,
297                "This storage declaration is never accessed and can be removed."
298            ),
299            DeadStorageDeclarationForFunction { unneeded_attrib } => write!(
300                f,
301                "This function's storage attributes declaration does not match its \
302                 actual storage access pattern: '{unneeded_attrib}' attribute(s) can be removed."
303            ),
304            MatchExpressionUnreachableArm { .. } => write!(f, "This match arm is unreachable."),
305            UnknownAttribute { attribute, .. } => write!(f, "Unknown attribute \"{attribute}\"."),
306            UnknownAttributeArg { attribute, arg, expected_args } => write!(
307                f,
308                "\"{arg}\" is an unknown argument for attribute \"{attribute}\". Known arguments are: {}.", sequence_to_str(expected_args, Enclosing::DoubleQuote, usize::MAX)
309            ),
310            EffectAfterInteraction {effect, effect_in_suggestion, block_name} =>
311                write!(f, "{effect} after external contract interaction in function or method \"{block_name}\". \
312                          Consider {effect_in_suggestion} before calling another contract"),
313            UsingDeprecated { deprecated_element_name, deprecated_element, help } =>
314                write!(f, "{deprecated_element} \"{deprecated_element_name}\" is deprecated. {}", help.as_ref().unwrap_or(&"".into())),
315            InherentImplForExternalType { type_name, .. } =>
316                write!(
317                    f,
318                    "Inherent implementation for `{type_name}` must be defined in the package that defines the type."
319                ),
320            DuplicatedStorageKey { first_field_full_name, second_field_full_name, key, .. } =>
321                write!(f, "Two storage fields have the same storage key.\nFirst field: {first_field_full_name}\nSecond field: {second_field_full_name}\nKey: {key}"),
322            ErrorTypeEmptyEnum { enum_name } =>
323                write!(f, "Empty error type enum \"{enum_name}\" can never be instantiated and used in `panic` expressions."),
324            ErrorEmptyErrorMessage { enum_name, enum_variant_name } =>
325                write!(f, "Error enum variant \"{enum_name}::{enum_variant_name}\" has an empty error message. Consider adding a helpful error message."),
326            ErrorDuplicatedErrorMessage { previous_occurrences, .. } =>
327                write!(f, "This error message is duplicated{}. Consider using a unique error message for every error variant.",
328                    if previous_occurrences.len() == 1 {
329                        "".to_string()
330                    } else {
331                        format!(" {} times", num_to_str(previous_occurrences.len()))
332                    }
333                ),
334            TrivialCheckFailed(_) => {
335                write!(f, "Trivial Check failed")
336            },
337        }
338    }
339}
340
341#[allow(dead_code)]
342const FUTURE_HARD_ERROR_HELP: &str =
343    "In future versions of Sway this warning will become a hard error.";
344
345impl ToDiagnostic for CompileWarning {
346    fn to_diagnostic(&self, source_engine: &sway_types::SourceEngine) -> Diagnostic {
347        let code = Code::warnings;
348        use sway_types::style::*;
349        use Warning::*;
350        match &self.warning_content {
351            NonScreamingSnakeCaseConstName { name } => Diagnostic {
352                reason: Some(Reason::new(code(1), "Constant name is not idiomatic".to_string())),
353                issue: Issue::warning(
354                    source_engine,
355                    name.span(),
356                    format!("Constant \"{name}\" should be SCREAMING_SNAKE_CASE."),
357                ),
358                hints: vec![
359                    Hint::help(
360                        source_engine,
361                        name.span(),
362                        format!("Consider renaming it to, e.g., \"{}\".", to_screaming_snake_case(name.as_str())),
363                    ),
364                ],
365                help: vec![
366                    format!("In Sway, ABIs, structs, traits, and enums are CapitalCase."),
367                    format!("Modules, variables, and functions are snake_case, while constants are SCREAMING_SNAKE_CASE."),
368                ],
369            },
370            MatchExpressionUnreachableArm { match_value, match_type, preceding_arms, unreachable_arm, is_last_arm, is_catch_all_arm } => Diagnostic {
371                reason: Some(Reason::new(code(1), "Match arm is unreachable".to_string())),
372                issue: Issue::warning(
373                    source_engine,
374                    unreachable_arm.clone(),
375                    match (*is_last_arm, *is_catch_all_arm) {
376                        (true, true) => format!("Last catch-all match arm `{}` is unreachable.", unreachable_arm.as_str()),
377                        _ => format!("Match arm `{}` is unreachable.", unreachable_arm.as_str())
378                    }
379                ),
380                hints: vec![
381                    Hint::info(
382                        source_engine,
383                        match_value.clone(),
384                        format!("The expression to match on is of type \"{match_type}\".")
385                    ),
386                    if preceding_arms.is_right() {
387                        Hint::help(
388                            source_engine,
389                            preceding_arms.as_ref().unwrap_right().clone(),
390                            format!("Catch-all arm `{}` makes all match arms below it unreachable.", preceding_arms.as_ref().unwrap_right().as_str())
391                        )
392                    }
393                    else {
394                        Hint::info(
395                            source_engine,
396                            Span::join_all(preceding_arms.as_ref().unwrap_left().clone()),
397                            if *is_last_arm {
398                                format!("Preceding match arms already match all possible values of `{}`.", match_value.as_str())
399                            }
400                            else {
401                                format!("Preceding match arms already match all the values that `{}` can match.", unreachable_arm.as_str())
402                            }
403                        )
404                    }
405                ],
406                help: if preceding_arms.is_right() {
407                    let catch_all_arm = preceding_arms.as_ref().unwrap_right().as_str();
408                    vec![
409                        format!("Catch-all patterns make sense only in last match arms."),
410                        format!("Consider removing the catch-all arm `{catch_all_arm}` or making it the last arm."),
411                        format!("Consider removing the unreachable arms below the `{catch_all_arm}` arm."),
412                    ]
413                }
414                else if *is_last_arm && *is_catch_all_arm {
415                    vec![
416                        format!("Catch-all patterns are often used in last match arms."),
417                        format!("But in this case, the preceding arms already match all possible values of `{}`.", match_value.as_str()),
418                        format!("Consider removing the unreachable last catch-all arm."),
419                    ]
420                }
421                else {
422                    vec![
423                        format!("Consider removing the unreachable arm."),
424                    ]
425                }
426            },
427            UninitializedAsmRegShadowsItem { constant_or_configurable_or_variable, item } => Diagnostic {
428                reason: Some(Reason::new(code(1), format!("Uninitialized ASM register is shadowing a {}", constant_or_configurable_or_variable.to_ascii_lowercase()))),
429                issue: Issue::warning(
430                    source_engine,
431                    self.span(),
432                    format!("Uninitialized register \"{item}\" is shadowing a {} of the same name.", constant_or_configurable_or_variable.to_ascii_lowercase()),
433                ),
434                hints: {
435                    let mut hints = vec![
436                        Hint::info(
437                            source_engine,
438                            item.span(),
439                            format!("{constant_or_configurable_or_variable} \"{item}\" is declared here.")
440                        ),
441                    ];
442
443                    hints.append(&mut Hint::multi_help(
444                        source_engine,
445                        &self.span(),
446                        vec![
447                            format!("Are you trying to initialize the register to the value of the {}?", constant_or_configurable_or_variable.to_ascii_lowercase()),
448                            format!("In that case, you must do it explicitly: `{item}: {item}`."),
449                            format!("Otherwise, to avoid the confusion with the shadowed {}, consider renaming the register \"{item}\".", constant_or_configurable_or_variable.to_ascii_lowercase()),
450                        ]
451                    ));
452
453                    hints
454                },
455                help: vec![],
456            },
457            AsmBlockIsEmpty => Diagnostic {
458                reason: Some(Reason::new(code(1), "ASM block is empty".to_string())),
459                issue: Issue::warning(
460                    source_engine,
461                    self.span(),
462                    "This ASM block is empty.".to_string(),
463                ),
464                hints: vec![],
465                help: vec![
466                    "Consider adding assembly instructions or a return register to the ASM block, or removing the block altogether.".to_string(),
467                ],
468            },
469            DuplicatedStorageKey { first_field, first_field_full_name, first_field_key_is_compiler_generated, second_field, second_field_full_name, second_field_key_is_compiler_generated, key } => Diagnostic {
470                reason: Some(Reason::new(code(1), "Two storage fields have the same storage key".to_string())),
471                issue: Issue::warning(
472                    source_engine,
473                    first_field.span(),
474                    format!("\"{first_field_full_name}\" has the same storage key as \"{second_field_full_name}\"."),
475                ),
476                hints: vec![
477                    Hint::info(
478                        source_engine,
479                        second_field.span(),
480                        format!("\"{second_field_full_name}\" is declared here."),
481                    ),
482                ],
483                help: vec![
484                    if *first_field_key_is_compiler_generated || *second_field_key_is_compiler_generated {
485                        format!("The key of \"{}\" is generated by the compiler using the following formula:",
486                            if *first_field_key_is_compiler_generated {
487                                first_field_full_name
488                            } else {
489                                second_field_full_name
490                            }
491                        )
492                    } else {
493                        "Both keys are explicitly defined by using the `in` keyword.".to_string()
494                    },
495                    if *first_field_key_is_compiler_generated || *second_field_key_is_compiler_generated {
496                        format!("{}sha256((0u8, \"{}\"))",
497                            Indent::Single,
498                            if *first_field_key_is_compiler_generated {
499                                first_field_full_name
500                            } else {
501                                second_field_full_name
502                            }
503                        )
504                    } else {
505                        Diagnostic::help_none()
506                    },
507                    format!("The common key is: {key}.")
508                ],
509            },
510            UnknownAttribute { attribute, known_attributes } => Diagnostic {
511                reason: Some(Reason::new(code(1), "Attribute is unknown".to_string())),
512                issue: Issue::warning(
513                    source_engine,
514                    attribute.span(),
515                    format!("\"{attribute}\" attribute is unknown.")
516                ),
517                hints: vec![did_you_mean_help(source_engine, attribute.span(), known_attributes.iter(), 2, Enclosing::DoubleQuote)],
518                help: vec![
519                    "Unknown attributes are allowed and can be used by third-party tools,".to_string(),
520                    "but the compiler ignores them.".to_string(),
521                ],
522            },
523            UnknownAttributeArg { attribute, arg, expected_args } => Diagnostic {
524                reason: Some(Reason::new(code(1), "Attribute argument is unknown".to_string())),
525                issue: Issue::warning(
526                    source_engine,
527                    arg.span(),
528                    format!("\"{arg}\" is an unknown argument for attribute \"{attribute}\".")
529                ),
530                hints: {
531                    let mut hints = vec![did_you_mean_help(source_engine, arg.span(), expected_args, 2, Enclosing::DoubleQuote)];
532                    if expected_args.len() == 1 {
533                        hints.push(Hint::help(source_engine, arg.span(), format!("The only known argument is \"{}\".", expected_args[0])));
534                    } else if expected_args.len() <= 3 {
535                        hints.push(Hint::help(source_engine, arg.span(), format!("Known arguments are {}.", sequence_to_str(expected_args, Enclosing::DoubleQuote, usize::MAX))));
536                    } else {
537                        hints.push(Hint::help(source_engine, arg.span(), "Known arguments are:".to_string()));
538                        hints.append(&mut Hint::multi_help(source_engine, &arg.span(), sequence_to_list(expected_args, Indent::Single, usize::MAX)))
539                    }
540                    hints
541                },
542                help: vec![
543                    format!("Unknown attribute arguments are allowed for some attributes like \"{attribute}\"."),
544                    "They can be used by third-party tools, but the compiler ignores them.".to_string(),
545                ],
546            },
547            UsingDeprecated { deprecated_element, deprecated_element_name, help } => Diagnostic {
548                reason: Some(Reason::new(code(1), format!("{deprecated_element} is deprecated"))),
549                issue: Issue::warning(
550                    source_engine,
551                    self.span(),
552                    format!("{deprecated_element} \"{deprecated_element_name}\" is deprecated."),
553                ),
554                hints: help.as_ref().map_or(vec![], |help| vec![
555                    Hint::help(
556                        source_engine,
557                        self.span(),
558                        help.clone(),
559                    ),
560                ]),
561                help: vec![],
562            },
563            InherentImplForExternalType { type_name, type_definition_span } => Diagnostic {
564                reason: Some(Reason::new(
565                    code(1),
566                    "coherence violation: inherent implementations must be defined in the type's defining package".into()
567                )),
568                issue: Issue::warning(
569                    source_engine,
570                    self.span(),
571                    format!(
572                        "cannot define inherent implementation for `{type_name}`: type is defined in a different package"
573                    ),
574                ),
575                hints: match type_definition_span.clone() {
576                    Some(def_span) => vec![Hint::info(
577                        source_engine,
578                        def_span,
579                        format!("Type `{type_name}` is defined here."),
580                    )],
581                    None => vec![],
582                },
583                help: vec![
584                    FUTURE_HARD_ERROR_HELP.to_string(),
585                    Diagnostic::help_empty_line(),
586                    "move this impl into the package that defines the type".to_string(),
587                    "or define and use a local trait instead to avoid the orphan rule".to_string(),
588                ],
589            },
590            ErrorTypeEmptyEnum { enum_name } => Diagnostic {
591                reason: Some(Reason::new(code(1), "Empty error type enum cannot be used in `panic` expressions".to_string())),
592                issue: Issue::warning(
593                    source_engine,
594                    enum_name.span(),
595                    format!("Error type enum \"{enum_name}\" is empty and can never be used in `panic` expressions."),
596                ),
597                hints: vec![],
598                help: vec![
599                    "Empty enums with no enum variants can never be instantiated.".to_string(),
600                    "Thus, they cannot have instances to use as arguments in `panic` expressions.".to_string(),
601                    Diagnostic::help_empty_line(),
602                    format!("Consider adding enum variants to \"{enum_name}\" and attributing them"),
603                    "with the `#[error]` attribute.".to_string(),
604                ],
605            },
606            ErrorEmptyErrorMessage { enum_name, enum_variant_name } => Diagnostic {
607                reason: Some(Reason::new(code(1), "Error message is empty".to_string())),
608                issue: Issue::warning(
609                    source_engine,
610                    self.span(),
611                    format!("Error enum variant \"{enum_name}::{enum_variant_name}\" has an empty error message."),
612                ),
613                hints: vec![
614                    Hint::help(
615                        source_engine,
616                        self.span(),
617                        "Consider adding a helpful error message here.".to_string(),
618                    )
619                ],
620                help: vec![],
621            },
622            ErrorDuplicatedErrorMessage { last_occurrence, previous_occurrences } => Diagnostic {
623                reason: Some(Reason::new(code(1), "Error message is duplicated".to_string())),
624                issue: Issue::error(
625                    source_engine,
626                    last_occurrence.clone(),
627                    "This error message is duplicated.".to_string(),
628                ),
629                hints: {
630                    let (first_occurrence, other_occurrences) = previous_occurrences.split_first().expect("there is at least one previous occurrence in `previous_occurrences`");
631                    let mut hints = vec![Hint::info(source_engine, first_occurrence.clone(), "It is already used here.".to_string())];
632                    other_occurrences.iter().for_each(|occurrence| hints.push(Hint::info(source_engine, occurrence.clone(), "And here.".to_string())));
633                    hints.push(Hint::help(source_engine, last_occurrence.clone(), "Consider using a unique error message for every error variant.".to_string()));
634                    hints
635                },
636                help: vec![],
637            },
638            UnusedReturnValue { r#type } => Diagnostic {
639                reason: Some(Reason::new(code(1), "Returned value is ignored".to_string())),
640                issue: Issue::warning(
641                    source_engine,
642                    self.span(),
643                    "This returns a value which is not assigned to anything and is ignored.".to_string(),
644                ),
645                hints: vec![
646                    Hint::help(
647                        source_engine,
648                        self.span(),
649                        format!("The returned value has type \"{type}\"."),
650                    )
651                ],
652                help: vec![
653                    "If you want to intentionally ignore the returned value, use `let _ = ...`:".to_string(),
654                    format!("{}let _ = {};", Indent::Single, first_line(self.span.as_str(), true)),
655                ],
656            },
657            TrivialCheckFailed(data) => {
658                data.as_diagnostic(source_engine)
659            }
660           _ => Diagnostic {
661                    // TODO: Temporarily we use self here to achieve backward compatibility.
662                    //       In general, self must not be used and will not be used once we
663                    //       switch to our own #[error] macro. All the values for the formatting
664                    //       of a diagnostic must come from the enum variant parameters.
665                    issue: Issue::warning(source_engine, self.span(), format!("{}", self.warning_content)),
666                    ..Default::default()
667                }
668        }
669    }
670}
671
672#[derive(Debug, Clone, PartialEq, Eq, Hash)]
673pub struct CollectedTraitImpl {
674    pub impl_span: Span,
675    pub trait_name: String,
676}
677
678#[derive(Debug, Clone, PartialEq, Eq, Hash)]
679pub enum Info {
680    ImplTraitsForType { impls: Vec<CollectedTraitImpl> },
681}
682
683impl fmt::Display for Info {
684    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
685        use Info::*;
686        match self {
687            ImplTraitsForType { impls } => {
688                write!(
689                    f,
690                    "Implemented traits: \"{:?}\"",
691                    impls
692                        .iter()
693                        .map(|i| i.impl_span.as_str())
694                        .collect::<Vec<_>>()
695                )
696            }
697        }
698    }
699}
700
701#[derive(Debug, Clone, PartialEq, Eq, Hash)]
702pub struct CompileInfo {
703    pub span: Span,
704    pub content: Info,
705}
706
707impl Spanned for CompileInfo {
708    fn span(&self) -> Span {
709        self.span.clone()
710    }
711}
712
713impl CompileInfo {
714    pub fn source_id(&self) -> Option<SourceId> {
715        self.span.source_id().cloned()
716    }
717
718    pub fn to_friendly_string(&self) -> String {
719        self.content.to_string()
720    }
721}
722
723impl ToDiagnostic for CompileInfo {
724    fn to_diagnostic(&self, source_engine: &sway_types::SourceEngine) -> Diagnostic {
725        let code = Code::warnings;
726        use Info::*;
727        match &self.content {
728            ImplTraitsForType { impls } => Diagnostic {
729                reason: Some(Reason::new(
730                    code(1),
731                    "Trait impls dump was requested.".to_string(),
732                )),
733                issue: Issue::info(
734                    source_engine,
735                    self.span(),
736                    "Matching implemented traits for this type.".to_string(),
737                ),
738                hints: impls
739                    .iter()
740                    .map(|i| {
741                        Hint::help(
742                            source_engine,
743                            i.impl_span.clone(),
744                            format!("trait is {}", i.trait_name.clone()),
745                        )
746                    })
747                    .collect::<Vec<_>>(),
748                help: vec![],
749            },
750        }
751    }
752}
753
754#[cfg(test)]
755mod test {
756    use sway_types::style::*;
757
758    #[test]
759    fn detect_styles() {
760        let snake_cases = [
761            "hello",
762            "__hello",
763            "blah32",
764            "some_words_here",
765            "___some_words_here",
766        ];
767        let screaming_snake_cases = ["SOME_WORDS_HERE", "___SOME_WORDS_HERE"];
768        let upper_camel_cases = [
769            "Hello",
770            "__Hello",
771            "Blah32",
772            "SomeWordsHere",
773            "___SomeWordsHere",
774        ];
775        let screaming_snake_case_or_upper_camel_case_idents = ["HELLO", "__HELLO", "BLAH32"];
776        let styleless_idents = ["Mix_Of_Things", "__Mix_Of_Things", "FooBar_123"];
777        for name in &snake_cases {
778            assert!(is_snake_case(name));
779            assert!(!is_screaming_snake_case(name));
780            assert!(!is_upper_camel_case(name));
781        }
782        for name in &screaming_snake_cases {
783            assert!(!is_snake_case(name));
784            assert!(is_screaming_snake_case(name));
785            assert!(!is_upper_camel_case(name));
786        }
787        for name in &upper_camel_cases {
788            assert!(!is_snake_case(name));
789            assert!(!is_screaming_snake_case(name));
790            assert!(is_upper_camel_case(name));
791        }
792        for name in &screaming_snake_case_or_upper_camel_case_idents {
793            assert!(!is_snake_case(name));
794            assert!(is_screaming_snake_case(name));
795            assert!(is_upper_camel_case(name));
796        }
797        for name in &styleless_idents {
798            assert!(!is_snake_case(name));
799            assert!(!is_screaming_snake_case(name));
800            assert!(!is_upper_camel_case(name));
801        }
802    }
803
804    #[test]
805    fn convert_to_snake_case() {
806        assert_eq!("hello", to_snake_case("HELLO"));
807        assert_eq!("___hello", to_snake_case("___HELLO"));
808        assert_eq!("blah32", to_snake_case("BLAH32"));
809        assert_eq!("some_words_here", to_snake_case("SOME_WORDS_HERE"));
810        assert_eq!("___some_words_here", to_snake_case("___SOME_WORDS_HERE"));
811        assert_eq!("hello", to_snake_case("Hello"));
812        assert_eq!("___hello", to_snake_case("___Hello"));
813        assert_eq!("blah32", to_snake_case("Blah32"));
814        assert_eq!("some_words_here", to_snake_case("SomeWordsHere"));
815        assert_eq!("___some_words_here", to_snake_case("___SomeWordsHere"));
816        assert_eq!("some_words_here", to_snake_case("someWordsHere"));
817        assert_eq!("___some_words_here", to_snake_case("___someWordsHere"));
818        assert_eq!("mix_of_things", to_snake_case("Mix_Of_Things"));
819        assert_eq!("__mix_of_things", to_snake_case("__Mix_Of_Things"));
820        assert_eq!("foo_bar_123", to_snake_case("FooBar_123"));
821    }
822
823    #[test]
824    fn convert_to_screaming_snake_case() {
825        assert_eq!("HELLO", to_screaming_snake_case("hello"));
826        assert_eq!("___HELLO", to_screaming_snake_case("___hello"));
827        assert_eq!("BLAH32", to_screaming_snake_case("blah32"));
828        assert_eq!(
829            "SOME_WORDS_HERE",
830            to_screaming_snake_case("some_words_here")
831        );
832        assert_eq!(
833            "___SOME_WORDS_HERE",
834            to_screaming_snake_case("___some_words_here")
835        );
836        assert_eq!("HELLO", to_screaming_snake_case("Hello"));
837        assert_eq!("___HELLO", to_screaming_snake_case("___Hello"));
838        assert_eq!("BLAH32", to_screaming_snake_case("Blah32"));
839        assert_eq!("SOME_WORDS_HERE", to_screaming_snake_case("SomeWordsHere"));
840        assert_eq!(
841            "___SOME_WORDS_HERE",
842            to_screaming_snake_case("___SomeWordsHere")
843        );
844        assert_eq!("SOME_WORDS_HERE", to_screaming_snake_case("someWordsHere"));
845        assert_eq!(
846            "___SOME_WORDS_HERE",
847            to_screaming_snake_case("___someWordsHere")
848        );
849        assert_eq!("MIX_OF_THINGS", to_screaming_snake_case("Mix_Of_Things"));
850        assert_eq!(
851            "__MIX_OF_THINGS",
852            to_screaming_snake_case("__Mix_Of_Things")
853        );
854        assert_eq!("FOO_BAR_123", to_screaming_snake_case("FooBar_123"));
855    }
856
857    #[test]
858    fn convert_to_upper_camel_case() {
859        assert_eq!("Hello", to_upper_camel_case("hello"));
860        assert_eq!("___Hello", to_upper_camel_case("___hello"));
861        assert_eq!("Blah32", to_upper_camel_case("blah32"));
862        assert_eq!("SomeWordsHere", to_upper_camel_case("some_words_here"));
863        assert_eq!(
864            "___SomeWordsHere",
865            to_upper_camel_case("___some_words_here")
866        );
867        assert_eq!("Hello", to_upper_camel_case("HELLO"));
868        assert_eq!("___Hello", to_upper_camel_case("___HELLO"));
869        assert_eq!("Blah32", to_upper_camel_case("BLAH32"));
870        assert_eq!("SomeWordsHere", to_upper_camel_case("SOME_WORDS_HERE"));
871        assert_eq!(
872            "___SomeWordsHere",
873            to_upper_camel_case("___SOME_WORDS_HERE")
874        );
875        assert_eq!("SomeWordsHere", to_upper_camel_case("someWordsHere"));
876        assert_eq!("___SomeWordsHere", to_upper_camel_case("___someWordsHere"));
877        assert_eq!("MixOfThings", to_upper_camel_case("Mix_Of_Things"));
878        assert_eq!("__MixOfThings", to_upper_camel_case("__Mix_Of_Things"));
879        assert_eq!("FooBar123", to_upper_camel_case("FooBar_123"));
880    }
881}