Skip to main content

forge_lint/sol/med/
non_reentrant_not_first.rs

1use super::NonReentrantNotFirst;
2use crate::{
3    linter::{LateLintPass, LintContext},
4    sol::{Severity, SolLint},
5};
6use solar::sema::{
7    Gcx,
8    hir::{self, FunctionKind},
9};
10
11declare_forge_lint!(
12    NON_REENTRANT_NOT_FIRST,
13    Severity::Med,
14    "non-reentrant-not-first",
15    "`nonReentrant` should be the first modifier"
16);
17
18impl<'hir> LateLintPass<'hir> for NonReentrantNotFirst {
19    fn check_function(
20        &mut self,
21        ctx: &LintContext,
22        _gcx: Gcx<'hir>,
23        hir: &'hir hir::Hir<'hir>,
24        func: &'hir hir::Function<'hir>,
25    ) {
26        if !matches!(
27            func.kind,
28            FunctionKind::Function | FunctionKind::Fallback | FunctionKind::Receive
29        ) {
30            return;
31        }
32
33        func.modifiers
34            .iter()
35            .enumerate()
36            .filter(|(index, modifier)| {
37                *index != 0 && modifier_is_named(hir, modifier, "nonReentrant")
38            })
39            .for_each(|(_, modifier)| ctx.emit(&NON_REENTRANT_NOT_FIRST, modifier.span));
40    }
41}
42
43fn modifier_is_named(hir: &hir::Hir<'_>, modifier: &hir::Modifier<'_>, name: &str) -> bool {
44    modifier.id.as_function().is_some_and(|function_id| {
45        let modifier_fn = hir.function(function_id);
46        matches!(modifier_fn.kind, FunctionKind::Modifier)
47            && modifier_fn.name.is_some_and(|ident| ident.as_str() == name)
48    })
49}