forge_lint/sol/med/
non_reentrant_not_first.rs1use 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}