forge_lint/sol/info/
modifier_used_only_once.rs1use super::ModifierUsedOnlyOnce;
2use crate::{
3 linter::{Lint, ProjectLintEmitter, ProjectLintPass, ProjectSource},
4 sol::{Severity, SolLint},
5};
6use solar::{ast::FunctionKind, interface::source_map::FileName, sema::hir};
7use std::collections::HashMap;
8
9declare_forge_lint!(
10 MODIFIER_USED_ONLY_ONCE,
11 Severity::Info,
12 "modifier-used-only-once",
13 "this modifier is used only once; consider inlining its checks into the function"
14);
15
16impl<'ast> ProjectLintPass<'ast> for ModifierUsedOnlyOnce {
17 fn check_project(&mut self, ctx: &ProjectLintEmitter<'_, '_>, sources: &[ProjectSource<'ast>]) {
18 if !ctx.is_lint_enabled(MODIFIER_USED_ONLY_ONCE.id()) {
19 return;
20 }
21 let gcx = ctx.gcx();
22 let hir = &gcx.hir;
23
24 let input_source_idx: HashMap<hir::SourceId, usize> = hir
28 .sources_enumerated()
29 .filter_map(|(sid, src)| {
30 let path = match &src.file.name {
31 FileName::Real(p) => p,
32 _ => return None,
33 };
34 let idx = sources.iter().position(|s| &s.path == path)?;
35 Some((sid, idx))
36 })
37 .collect();
38
39 if input_source_idx.is_empty() {
40 return;
41 }
42
43 let counts = count_modifier_invocations(hir);
44
45 for function_id in hir.function_ids() {
46 let function = hir.function(function_id);
47 let Some(&src_idx) = input_source_idx.get(&function.source) else { continue };
48 if function.kind != FunctionKind::Modifier
52 || function.body.is_none()
53 || function.virtual_
54 || function.override_
55 {
56 continue;
57 }
58 if counts.get(&function_id).copied().unwrap_or(0) == 1 {
60 ctx.emit(&sources[src_idx], &MODIFIER_USED_ONLY_ONCE, function.keyword_span());
61 }
62 }
63 }
64}
65
66fn count_modifier_invocations(hir: &hir::Hir<'_>) -> HashMap<hir::FunctionId, usize> {
70 let mut counts = HashMap::new();
71 for function_id in hir.function_ids() {
73 for invocation in hir.function(function_id).modifiers {
74 if let hir::ItemId::Function(modifier_id) = invocation.id {
75 *counts.entry(modifier_id).or_insert(0) += 1;
76 }
77 }
78 }
79 counts
80}