forge_lint/sol/info/
mixed_case.rs

1use super::{MixedCaseFunction, MixedCaseVariable};
2use crate::{
3    declare_forge_lint,
4    linter::{EarlyLintPass, LintContext},
5    sol::{Severity, SolLint},
6};
7use solar_ast::{ItemFunction, VariableDefinition};
8
9declare_forge_lint!(
10    MIXED_CASE_FUNCTION,
11    Severity::Info,
12    "mixed-case-function",
13    "function names should use mixedCase"
14);
15
16impl<'ast> EarlyLintPass<'ast> for MixedCaseFunction {
17    fn check_item_function(&mut self, ctx: &LintContext<'_>, func: &'ast ItemFunction<'ast>) {
18        if let Some(name) = func.header.name {
19            if !is_mixed_case(name.as_str(), true) {
20                ctx.emit(&MIXED_CASE_FUNCTION, name.span);
21            }
22        }
23    }
24}
25
26declare_forge_lint!(
27    MIXED_CASE_VARIABLE,
28    Severity::Info,
29    "mixed-case-variable",
30    "mutable variables should use mixedCase"
31);
32
33impl<'ast> EarlyLintPass<'ast> for MixedCaseVariable {
34    fn check_variable_definition(
35        &mut self,
36        ctx: &LintContext<'_>,
37        var: &'ast VariableDefinition<'ast>,
38    ) {
39        if var.mutability.is_none() {
40            if let Some(name) = var.name {
41                if !is_mixed_case(name.as_str(), false) {
42                    ctx.emit(&MIXED_CASE_VARIABLE, name.span);
43                }
44            }
45        }
46    }
47}
48
49/// Check if a string is mixedCase
50///
51/// To avoid false positives like `fn increment()` or `uint256 counter`,
52/// lowercase strings are treated as mixedCase.
53pub fn is_mixed_case(s: &str, is_fn: bool) -> bool {
54    if s.len() <= 1 {
55        return true;
56    }
57
58    // Remove leading/trailing underscores like `heck` does
59    if s.trim_matches('_') == format!("{}", heck::AsLowerCamelCase(s)).as_str() {
60        return true
61    }
62
63    // Ignore `fn test*`, `fn invariant_*`, and `fn statefulFuzz*` patterns, as they usually contain
64    // (allowed) underscores.
65    is_fn && (s.starts_with("test") || s.starts_with("invariant_") || s.starts_with("statefulFuzz"))
66}