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