forge_lint/sol/info/
event_fields.rs1use super::EventFields;
2use crate::{
3 linter::{EarlyLintPass, LintContext},
4 sol::{Severity, SolLint},
5};
6use solar::ast::{ElementaryType, Item, ItemKind, TypeKind};
7
8declare_forge_lint!(
9 EVENT_FIELDS,
10 Severity::Info,
11 "event-fields",
12 "`address` event parameter is not `indexed`"
13);
14
15impl<'ast> EarlyLintPass<'ast> for EventFields {
16 fn check_item(&mut self, ctx: &LintContext, item: &'ast Item<'ast>) {
17 let ItemKind::Event(event) = &item.kind else { return };
18 if event.parameters.iter().any(|p| p.indexed) {
19 return;
20 }
21 let slots_available = if event.anonymous { 4 } else { 3 };
23 let names: Vec<String> = event
25 .parameters
26 .iter()
27 .enumerate()
28 .filter_map(|(idx, param)| {
29 let TypeKind::Elementary(ElementaryType::Address(payable)) = ¶m.ty.kind else {
30 return None;
31 };
32 let name =
33 param.name.map_or_else(|| format!("parameter #{}", idx + 1), |n| n.to_string());
34 let ty = if *payable { "address payable" } else { "address" };
35 Some(format!("`{name}` (`{ty}`)"))
36 })
37 .take(slots_available)
38 .collect();
39 if !names.is_empty() {
40 let msg = format!(
41 "event has unindexed fields that may benefit from being indexed: {}",
42 names.join(", ")
43 );
44 ctx.emit_with_msg(&EVENT_FIELDS, event.name.span, msg);
45 }
46 }
47}