Skip to content

Commit 2d5161d

Browse files
authored
fix(forge lint): gas lints (#10667)
* fix: gas lints * fix: use project paths to identify tests/scripts
1 parent 197586f commit 2d5161d

File tree

5 files changed

+46
-17
lines changed

5 files changed

+46
-17
lines changed

crates/forge/src/cmd/lint.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ impl LintArgs {
4545
pub fn run(self) -> Result<()> {
4646
let config = self.load_config()?;
4747
let project = config.project()?;
48+
let path_config = config.project_paths();
4849

4950
// Expand ignore globs and canonicalize from the get go
5051
let ignored = expand_globs(&config.root, config.lint.ignore.iter())?
@@ -105,7 +106,7 @@ impl LintArgs {
105106
return Err(eyre!("Linting not supported for this language"));
106107
}
107108

108-
let linter = SolidityLinter::new()
109+
let linter = SolidityLinter::new(path_config)
109110
.with_json_emitter(self.json)
110111
.with_description(true)
111112
.with_lints(include)

crates/lint/src/sol/gas/keccak.rs

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ use crate::{
44
linter::EarlyLintPass,
55
sol::{Severity, SolLint},
66
};
7-
use solar_ast::{Expr, ExprKind};
7+
use solar_ast::{CallArgsKind, Expr, ExprKind};
88
use solar_interface::kw;
99

1010
declare_forge_lint!(
@@ -16,9 +16,17 @@ declare_forge_lint!(
1616

1717
impl<'ast> EarlyLintPass<'ast> for AsmKeccak256 {
1818
fn check_expr(&mut self, ctx: &crate::linter::LintContext<'_>, expr: &'ast Expr<'ast>) {
19-
if let ExprKind::Call(expr, _) = &expr.kind {
19+
if let ExprKind::Call(expr, args) = &expr.kind {
2020
if let ExprKind::Ident(ident) = &expr.kind {
2121
if ident.name == kw::Keccak256 {
22+
// Do not flag when hashing a single literal, as the compiler should optimize it
23+
if let CallArgsKind::Unnamed(ref exprs) = args.kind {
24+
if exprs.len() == 1 {
25+
if let ExprKind::Lit(_, _) = exprs[0].kind {
26+
return;
27+
}
28+
}
29+
}
2230
ctx.emit(&ASM_KECCAK256, expr.span);
2331
}
2432
}

crates/lint/src/sol/mod.rs

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
use crate::linter::{EarlyLintPass, EarlyLintVisitor, Lint, LintContext, Linter};
2-
use foundry_compilers::solc::SolcLanguage;
2+
use foundry_compilers::{solc::SolcLanguage, ProjectPathsConfig};
33
use foundry_config::lint::Severity;
44
use rayon::iter::{IntoParallelIterator, ParallelIterator};
55
use solar_ast::{visit::Visit, Arena};
@@ -22,8 +22,9 @@ pub mod med;
2222

2323
/// Linter implementation to analyze Solidity source code responsible for identifying
2424
/// vulnerabilities gas optimizations, and best practices.
25-
#[derive(Debug, Clone, Default)]
25+
#[derive(Debug, Clone)]
2626
pub struct SolidityLinter {
27+
path_config: ProjectPathsConfig,
2728
severity: Option<Vec<Severity>>,
2829
lints_included: Option<Vec<SolLint>>,
2930
lints_excluded: Option<Vec<SolLint>>,
@@ -32,8 +33,9 @@ pub struct SolidityLinter {
3233
}
3334

3435
impl SolidityLinter {
35-
pub fn new() -> Self {
36+
pub fn new(path_config: ProjectPathsConfig) -> Self {
3637
Self {
38+
path_config,
3739
severity: None,
3840
lints_included: None,
3941
lints_excluded: None,
@@ -73,11 +75,15 @@ impl SolidityLinter {
7375
let _ = sess.enter(|| -> Result<(), diagnostics::ErrorGuaranteed> {
7476
// Declare all available passes and lints
7577
let mut passes_and_lints = Vec::new();
76-
passes_and_lints.extend(gas::create_lint_passes());
7778
passes_and_lints.extend(high::create_lint_passes());
7879
passes_and_lints.extend(med::create_lint_passes());
7980
passes_and_lints.extend(info::create_lint_passes());
8081

82+
// Do not apply gas-severity rules on tests and scripts
83+
if !self.path_config.is_test_or_script(file) {
84+
passes_and_lints.extend(gas::create_lint_passes());
85+
}
86+
8187
// Filter based on linter config
8288
let mut passes: Vec<Box<dyn EarlyLintPass<'_>>> = passes_and_lints
8389
.into_iter()

crates/lint/testdata/Keccak256.sol

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,19 @@
11
contract AsmKeccak256 {
2+
3+
// constants are optimized by the compiler
4+
bytes32 constant HASH = keccak256("hello");
5+
bytes32 constant OTHER_HASH = keccak256(1234);
6+
27
constructor(uint256 a, uint256 b) {
38
keccak256(abi.encodePacked(a, b)); //~NOTE: hash using inline assembly to save gas
49
}
510

6-
function solidityHash(uint256 a, uint256 b) public view {
7-
keccak256(abi.encodePacked(a, b)); //~NOTE: hash using inline assembly to save gas
11+
function solidityHash(uint256 a, uint256 b) public view returns (bytes32) {
12+
bytes32 hash = keccak256(a); //~NOTE: hash using inline assembly to save gas
13+
return keccak256(abi.encodePacked(a, b)); //~NOTE: hash using inline assembly to save gas
814
}
915

10-
function assemblyHash(uint256 a, uint256 b) public view {
16+
function assemblyHash(uint256 a, uint256 b) public view returns (bytes32){
1117
//optimized
1218
assembly {
1319
mstore(0x00, a)
Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,24 @@
11
note[asm-keccak256]: hash using inline assembly to save gas
22
--> ROOT/testdata/Keccak256.sol:LL:CC
33
|
4-
3 | keccak256(abi.encodePacked(a, b));
4+
8 | keccak256(abi.encodePacked(a, b));
55
| ---------
66
|
77
= help: https://book.getfoundry.sh/reference/forge/forge-lint#asm-keccak256
88

99
note[asm-keccak256]: hash using inline assembly to save gas
10-
--> ROOT/testdata/Keccak256.sol:LL:CC
11-
|
12-
7 | keccak256(abi.encodePacked(a, b));
13-
| ---------
14-
|
15-
= help: https://book.getfoundry.sh/reference/forge/forge-lint#asm-keccak256
10+
--> ROOT/testdata/Keccak256.sol:LL:CC
11+
|
12+
12 | bytes32 hash = keccak256(a);
13+
| ---------
14+
|
15+
= help: https://book.getfoundry.sh/reference/forge/forge-lint#asm-keccak256
16+
17+
note[asm-keccak256]: hash using inline assembly to save gas
18+
--> ROOT/testdata/Keccak256.sol:LL:CC
19+
|
20+
13 | return keccak256(abi.encodePacked(a, b));
21+
| ---------
22+
|
23+
= help: https://book.getfoundry.sh/reference/forge/forge-lint#asm-keccak256
1624

0 commit comments

Comments
 (0)