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