Skip to content

Commit 53ab05e

Browse files
Merge pull request #560 from protofire/gc-strict-inequalities
GC: Gas Strict Inequalities
2 parents c28552f + bb10605 commit 53ab05e

File tree

8 files changed

+198
-4
lines changed

8 files changed

+198
-4
lines changed

conf/rulesets/solhint-all.js

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,13 +31,14 @@ module.exports = Object.freeze({
3131
'gas-multitoken1155': 'warn',
3232
'gas-named-return-values': 'warn',
3333
'gas-small-strings': 'warn',
34+
'gas-strict-inequalities': 'warn',
3435
'gas-struct-packing': 'warn',
3536
'comprehensive-interface': 'warn',
3637
quotes: ['error', 'double'],
3738
'const-name-snakecase': 'warn',
3839
'contract-name-camelcase': 'warn',
3940
'event-name-camelcase': 'warn',
40-
'foundry-test-functions': ['off', ['setUp']],
41+
'foundry-test-functions': ['warn', ['setUp']],
4142
'func-name-mixedcase': 'warn',
4243
'func-named-parameters': ['warn', 4],
4344
'func-param-name-mixedcase': 'warn',
@@ -48,7 +49,7 @@ module.exports = Object.freeze({
4849
},
4950
],
5051
'modifier-name-mixedcase': 'warn',
51-
'named-parameters-mapping': 'off',
52+
'named-parameters-mapping': 'warn',
5253
'private-vars-leading-underscore': [
5354
'warn',
5455
{

docs/rules.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ title: "Rule Index of Solhint"
3535
| [gas-multitoken1155](./rules/gas-consumption/gas-multitoken1155.md) | ERC1155 is a cheaper non-fungible token than ERC721 | | |
3636
| [gas-named-return-values](./rules/gas-consumption/gas-named-return-values.md) | Enforce the return values of a function to be named | | |
3737
| [gas-small-strings](./rules/gas-consumption/gas-small-strings.md) | Keep strings smaller than 32 bytes | | |
38+
| [gas-strict-inequalities](./rules/gas-consumption/gas-strict-inequalities.md) | Suggest Strict Inequalities over non Strict ones | | |
3839
| [gas-struct-packing](./rules/gas-consumption/gas-struct-packing.md) | Suggest to re-arrange struct packing order when it is inefficient | | |
3940
4041

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
---
2+
warning: "This is a dynamically generated file. Do not edit manually."
3+
layout: "default"
4+
title: "gas-strict-inequalities | Solhint"
5+
---
6+
7+
# gas-strict-inequalities
8+
![Category Badge](https://img.shields.io/badge/-Gas%20Consumption%20Rules-informational)
9+
![Default Severity Badge warn](https://img.shields.io/badge/Default%20Severity-warn-yellow)
10+
11+
## Description
12+
Suggest Strict Inequalities over non Strict ones
13+
14+
## Options
15+
This rule accepts a string option of rule severity. Must be one of "error", "warn", "off". Default to warn.
16+
17+
### Example Config
18+
```json
19+
{
20+
"rules": {
21+
"gas-strict-inequalities": "warn"
22+
}
23+
}
24+
```
25+
26+
### Notes
27+
- Strict inequality does not always saves gas. It is dependent on the context of the surrounding opcodes
28+
- [source 1](https://coinsbench.com/comprehensive-guide-tips-and-tricks-for-gas-optimization-in-solidity-5380db734404) of the rule initiative (see Less/Greater Than vs Less/Greater Than or Equal To)
29+
- [source 2](https://www.rareskills.io/post/gas-optimization?postId=c9db474a-ff97-4fa3-a51d-fe13ccb8fe3b#viewer-7b77t) of the rule initiative
30+
31+
## Examples
32+
This rule does not have examples.
33+
34+
## Version
35+
This rule is introduced in the latest version.
36+
37+
## Resources
38+
- [Rule source](https://github.com/protofire/solhint/tree/master/lib/rules/gas-consumption/gas-strict-inequalities.js)
39+
- [Document source](https://github.com/protofire/solhint/tree/master/docs/rules/gas-consumption/gas-strict-inequalities.md)
40+
- [Test cases](https://github.com/protofire/solhint/tree/master/test/rules/gas-consumption/gas-strict-inequalities.js)
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
const BaseChecker = require('../base-checker')
2+
3+
const ruleId = 'gas-strict-inequalities'
4+
const meta = {
5+
type: 'gas-consumption',
6+
7+
docs: {
8+
description: 'Suggest Strict Inequalities over non Strict ones',
9+
category: 'Gas Consumption Rules',
10+
notes: [
11+
{
12+
note: 'Strict inequality does not always saves gas. It is dependent on the context of the surrounding opcodes',
13+
},
14+
{
15+
note: '[source 1](https://coinsbench.com/comprehensive-guide-tips-and-tricks-for-gas-optimization-in-solidity-5380db734404) of the rule initiative (see Less/Greater Than vs Less/Greater Than or Equal To)',
16+
},
17+
{
18+
note: '[source 2](https://www.rareskills.io/post/gas-optimization?postId=c9db474a-ff97-4fa3-a51d-fe13ccb8fe3b#viewer-7b77t) of the rule initiative',
19+
},
20+
],
21+
},
22+
23+
isDefault: false,
24+
recommended: false,
25+
defaultSetup: 'warn',
26+
27+
schema: null,
28+
}
29+
30+
class GasStrictInequalities extends BaseChecker {
31+
constructor(reporter) {
32+
super(reporter, ruleId, meta)
33+
}
34+
35+
BinaryOperation(node) {
36+
if (node.operator === '>=' || node.operator === '<=') {
37+
this.reportError(node)
38+
}
39+
}
40+
41+
reportError(node) {
42+
this.error(node, `GC: Non strict inequality found. Try converting to a strict one`)
43+
}
44+
}
45+
46+
module.exports = GasStrictInequalities

lib/rules/gas-consumption/index.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ const GasIndexedEvents = require('./gas-indexed-events')
44
const GasCalldataParameters = require('./gas-calldata-parameters')
55
const GasIncrementByOne = require('./gas-increment-by-one')
66
const GasStructPacking = require('./gas-struct-packing')
7+
const GasStrictInequalities = require('./gas-strict-inequalities')
78
const GasNamedReturnValuesChecker = require('./gas-named-return-values')
89
const GasCustomErrorsChecker = require('./gas-custom-errors')
910

@@ -15,6 +16,7 @@ module.exports = function checkers(reporter, config) {
1516
new GasCalldataParameters(reporter, config),
1617
new GasIncrementByOne(reporter, config),
1718
new GasStructPacking(reporter, config),
19+
new GasStrictInequalities(reporter, config),
1820
new GasNamedReturnValuesChecker(reporter),
1921
new GasCustomErrorsChecker(reporter),
2022
]

test/rules/gas-consumption/gas-named-return-values.js

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,12 @@ describe('Linter - gas-named-return-values', () => {
8484

8585
const report = linter.processStr(code, {
8686
extends: 'solhint:all',
87-
rules: { 'compiler-version': 'off' },
87+
rules: {
88+
'compiler-version': 'off',
89+
'comprehensive-interface': 'off',
90+
'foundry-test-functions': 'off',
91+
'non-state-vars-leading-underscore': 'off',
92+
},
8893
})
8994

9095
assertWarnsCount(report, 2)
Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
const assert = require('assert')
2+
const linter = require('../../../lib/index')
3+
const { funcWith } = require('../../common/contract-builder')
4+
5+
const ERROR_MSG = 'GC: Non strict inequality found. Try converting to a strict one'
6+
7+
describe('Linter - gas-strict-inequalities', () => {
8+
it('should raise error on non strict equalities 1', () => {
9+
const code = funcWith(`
10+
uint256 a;
11+
uint256 b;
12+
uint256 c;
13+
uint256 d;
14+
15+
if (a >= b) { }
16+
if (c <= d) { }
17+
if (c < d) { }
18+
if (c > d) { }`)
19+
20+
const report = linter.processStr(code, {
21+
rules: { 'gas-strict-inequalities': 'error' },
22+
})
23+
24+
assert.equal(report.errorCount, 2)
25+
assert.equal(report.messages[0].message, ERROR_MSG)
26+
assert.equal(report.messages[1].message, ERROR_MSG)
27+
})
28+
29+
it('should raise error on non strict equalities 2', () => {
30+
const code = funcWith(`
31+
uint256 a;
32+
uint256 b;
33+
uint256 c;
34+
uint256 d;
35+
36+
while (a >= b) {
37+
38+
}
39+
40+
if (c < d) { }`)
41+
42+
const report = linter.processStr(code, {
43+
rules: { 'gas-strict-inequalities': 'error' },
44+
})
45+
46+
assert.equal(report.errorCount, 1)
47+
assert.equal(report.messages[0].message, ERROR_MSG)
48+
// assert.equal(report.errorCount, 0)
49+
})
50+
51+
it('should raise error on non strict equalities 3', () => {
52+
const code = funcWith(`
53+
uint256 a;
54+
uint256 b;
55+
uint256 c;
56+
uint256 d;
57+
58+
while (a >= b) {
59+
60+
}
61+
62+
if ((c < d) && (a <= b) && (d >= a)) { }`)
63+
64+
const report = linter.processStr(code, {
65+
rules: { 'gas-strict-inequalities': 'error' },
66+
})
67+
68+
assert.equal(report.errorCount, 3)
69+
assert.equal(report.messages[0].message, ERROR_MSG)
70+
assert.equal(report.messages[1].message, ERROR_MSG)
71+
assert.equal(report.messages[2].message, ERROR_MSG)
72+
// assert.equal(report.errorCount, 0)
73+
})
74+
75+
it('should NOT raise error on strict equalities', () => {
76+
const code = funcWith(`
77+
uint256 a;
78+
uint256 b;
79+
uint256 c;
80+
uint256 d;
81+
82+
while (a > b) {
83+
84+
}
85+
86+
if ((c < d) && (a < b) && (d > a)) { }`)
87+
88+
const report = linter.processStr(code, {
89+
rules: { 'gas-strict-inequalities': 'error' },
90+
})
91+
92+
assert.equal(report.errorCount, 0)
93+
})
94+
})

test/rules/naming/func-named-parameters.js

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,12 @@ describe('Linter - func-named-parameters', () => {
7979

8080
const report = linter.processStr(code, {
8181
extends: 'solhint:all',
82-
rules: { 'compiler-version': 'off', 'comprehensive-interface': 'off' },
82+
rules: {
83+
'compiler-version': 'off',
84+
'comprehensive-interface': 'off',
85+
'foundry-test-functions': 'off',
86+
'non-state-vars-leading-underscore': 'off',
87+
},
8388
})
8489

8590
assertWarnsCount(report, 1)

0 commit comments

Comments
 (0)