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