forge_lint/sol/info/
screaming_snake_case.rs

1use super::ScreamingSnakeCase;
2use crate::{
3    declare_forge_lint,
4    linter::{EarlyLintPass, LintContext},
5    sol::{Severity, SolLint},
6};
7use solar_ast::{VarMut, VariableDefinition};
8
9declare_forge_lint!(
10    SCREAMING_SNAKE_CASE_CONSTANT,
11    Severity::Info,
12    "screaming-snake-case-const",
13    "constants should use SCREAMING_SNAKE_CASE"
14);
15
16declare_forge_lint!(
17    SCREAMING_SNAKE_CASE_IMMUTABLE,
18    Severity::Info,
19    "screaming-snake-case-immutable",
20    "immutables should use SCREAMING_SNAKE_CASE"
21);
22
23impl<'ast> EarlyLintPass<'ast> for ScreamingSnakeCase {
24    fn check_variable_definition(
25        &mut self,
26        ctx: &LintContext<'_>,
27        var: &'ast VariableDefinition<'ast>,
28    ) {
29        if let (Some(name), Some(mutability)) = (var.name, var.mutability) {
30            let name_str = name.as_str();
31            if name_str.len() < 2 || is_screaming_snake_case(name_str) {
32                return;
33            }
34
35            match mutability {
36                VarMut::Constant => ctx.emit(&SCREAMING_SNAKE_CASE_CONSTANT, name.span),
37                VarMut::Immutable => ctx.emit(&SCREAMING_SNAKE_CASE_IMMUTABLE, name.span),
38            }
39        }
40    }
41}
42
43/// Check if a string is SCREAMING_SNAKE_CASE. Numbers don't need to be preceded by an underscore.
44pub fn is_screaming_snake_case(s: &str) -> bool {
45    if s.len() <= 1 {
46        return true;
47    }
48
49    // Remove leading/trailing underscores like `heck` does
50    s.trim_matches('_') == format!("{}", heck::AsShoutySnakeCase(s)).as_str()
51}