forge_lint/sol/low/empty_block.rs
1use super::EmptyBlock;
2use crate::{
3 linter::{EarlyLintPass, LintContext},
4 sol::{Severity, SolLint},
5};
6use solar::ast::{FunctionKind, ItemFunction, StateMutability};
7
8declare_forge_lint!(EMPTY_BLOCK, Severity::Low, "empty-block", "empty function body");
9
10impl<'ast> EarlyLintPass<'ast> for EmptyBlock {
11 fn check_item_function(&mut self, ctx: &LintContext, func: &'ast ItemFunction<'ast>) {
12 // An empty body on a regular function is dead or unfinished code. Bodies whose emptiness
13 // is the behavior are exempt: constructors, receive/fallback (the empty body accepts
14 // calls or ether), `virtual` functions (an empty default meant to be overridden),
15 // functions with modifiers (`initialize() external initializer {}`,
16 // `_authorizeUpgrade(address) internal override onlyOwner {}`: the modifier carries the
17 // behavior) and `payable` functions without return values (an empty ether sink; with a
18 // return value the body silently returns the default, an unfinished stub). Functions
19 // without a body (interfaces, abstract declarations) have nothing to flag, and an empty
20 // modifier body is a solc compile error (2883), so it never reaches the linter.
21 if let Some(body) = &func.body
22 && body.is_empty()
23 && matches!(func.kind, FunctionKind::Function)
24 && func.header.virtual_.is_none()
25 && func.header.modifiers.is_empty()
26 && (func.header.state_mutability() != StateMutability::Payable
27 || func.header.returns.is_some())
28 {
29 ctx.emit(&EMPTY_BLOCK, body.span);
30 }
31 }
32}