Skip to content

Commit c3d3ed7

Browse files
Merge pull request #718 from protofire/rename-foundry-test-functions
Rename foundry-test-functions rule
2 parents fc302c3 + e13fe10 commit c3d3ed7

File tree

13 files changed

+483
-39
lines changed

13 files changed

+483
-39
lines changed

CHANGELOG.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,10 @@
1+
## [6.0.2] - 2025-09-18
2+
3+
🧹 `Chore`: `foundry-test-functions` is deprecated and will be removed in v7.0.0. Please rename to `foundry-test-function-naming`.
4+
WILL BE REPLACED IN v7
5+
6+
<br><br>
7+
18
## [6.0.1] - 2025-08-22
29

310
🛠️ `Fix`: `no-unused-vars` for modifiers

conf/rulesets/solhint-all.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,7 @@ module.exports = Object.freeze({
7272
'const-name-snakecase': 'warn',
7373
'contract-name-capwords': 'warn',
7474
'event-name-capwords': 'warn',
75+
'foundry-test-function-naming': ['warn', ['setUp']],
7576
'foundry-test-functions': ['warn', ['setUp']],
7677
'func-name-mixedcase': 'warn',
7778
'func-named-parameters': ['warn', 4],

docs/rules.md

Lines changed: 22 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -27,27 +27,28 @@ title: "Rule Index of Solhint"
2727

2828
## Style Guide Rules
2929

30-
| Rule Id | Error | Recommended | Deprecated |
31-
| ------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------- | ------------ | ---------- |
32-
| [interface-starts-with-i](./rules/naming/interface-starts-with-i.md) | Solidity Interfaces names should start with an `I` | $~~~~~~~~$✔️ | |
33-
| [duplicated-imports](./rules/miscellaneous/duplicated-imports.md) | Check if an import is done twice in the same file and there is no alias | $~~~~~~~~$✔️ | |
34-
| [const-name-snakecase](./rules/naming/const-name-snakecase.md) | Constant name must be in capitalized SNAKE_CASE. (Does not check IMMUTABLES, use immutable-vars-naming) | $~~~~~~~~$✔️ | |
35-
| [contract-name-capwords](./rules/naming/contract-name-capwords.md) | Contract, Structs and Enums should be in CapWords. | $~~~~~~~~$✔️ | |
36-
| [event-name-capwords](./rules/naming/event-name-capwords.md) | Event name must be in CapWords. | $~~~~~~~~$✔️ | |
37-
| [foundry-test-functions](./rules/naming/foundry-test-functions.md) | Enforce naming convention on functions for Foundry test cases | | |
38-
| [func-name-mixedcase](./rules/naming/func-name-mixedcase.md) | Function name must be in mixedCase. | $~~~~~~~~$✔️ | |
39-
| [func-named-parameters](./rules/naming/func-named-parameters.md) | Enforce named parameters for function calls with 4 or more arguments. This rule may have some false positives | | |
40-
| [func-param-name-mixedcase](./rules/naming/func-param-name-mixedcase.md) | Function param name must be in mixedCase. | | |
41-
| [immutable-vars-naming](./rules/naming/immutable-vars-naming.md) | Check Immutable variables. Capitalized SNAKE_CASE or mixedCase depending on configuration. | $~~~~~~~~$✔️ | |
42-
| [modifier-name-mixedcase](./rules/naming/modifier-name-mixedcase.md) | Modifier name must be in mixedCase. | | |
43-
| [named-parameters-mapping](./rules/naming/named-parameters-mapping.md) | Solidity v0.8.18 introduced named parameters on the mappings definition. | | |
44-
| [private-vars-leading-underscore](./rules/naming/private-vars-leading-underscore.md) | Non-external functions and state variables should start with a single underscore. Others, shouldn't | | |
45-
| [use-forbidden-name](./rules/naming/use-forbidden-name.md) | Avoid to use letters 'I', 'l', 'O' as identifiers. | $~~~~~~~~$✔️ | |
46-
| [var-name-mixedcase](./rules/naming/var-name-mixedcase.md) | Variable names must be in mixedCase. (Does not check IMMUTABLES nor CONSTANTS (use inherent rules for that) | $~~~~~~~~$✔️ | |
47-
| [imports-on-top](./rules/order/imports-on-top.md) | Import statements must be on top. | $~~~~~~~~$✔️ | |
48-
| [imports-order](./rules/order/imports-order.md) | Order the imports of the contract to follow a certain hierarchy (read "Notes section") | | |
49-
| [ordering](./rules/order/ordering.md) | Check order of elements in file and inside each contract, according to the style guide | | |
50-
| [visibility-modifier-order](./rules/order/visibility-modifier-order.md) | Visibility modifier must be first in list of modifiers. | $~~~~~~~~$✔️ | |
30+
| Rule Id | Error | Recommended | Deprecated |
31+
| ------------------------------------------------------------------------------------ | --------------------------------------------------------------------------------------------------------------- | ------------ | ---------- |
32+
| [interface-starts-with-i](./rules/naming/interface-starts-with-i.md) | Solidity Interfaces names should start with an `I` | $~~~~~~~~$✔️ | |
33+
| [duplicated-imports](./rules/miscellaneous/duplicated-imports.md) | Check if an import is done twice in the same file and there is no alias | $~~~~~~~~$✔️ | |
34+
| [const-name-snakecase](./rules/naming/const-name-snakecase.md) | Constant name must be in capitalized SNAKE_CASE. (Does not check IMMUTABLES, use immutable-vars-naming) | $~~~~~~~~$✔️ | |
35+
| [contract-name-capwords](./rules/naming/contract-name-capwords.md) | Contract, Structs and Enums should be in CapWords. | $~~~~~~~~$✔️ | |
36+
| [event-name-capwords](./rules/naming/event-name-capwords.md) | Event name must be in CapWords. | $~~~~~~~~$✔️ | |
37+
| [foundry-test-function-naming](./rules/naming/foundry-test-function-naming.md) | Enforce naming convention on functions for Foundry test cases | | |
38+
| [foundry-test-functions](./rules/naming/foundry-test-functions.md) | Enforce naming convention on functions for Foundry test cases (DEPRECATED, use `foundry-test-functions-naming`) | | |
39+
| [func-name-mixedcase](./rules/naming/func-name-mixedcase.md) | Function name must be in mixedCase. | $~~~~~~~~$✔️ | |
40+
| [func-named-parameters](./rules/naming/func-named-parameters.md) | Enforce named parameters for function calls with 4 or more arguments. This rule may have some false positives | | |
41+
| [func-param-name-mixedcase](./rules/naming/func-param-name-mixedcase.md) | Function param name must be in mixedCase. | | |
42+
| [immutable-vars-naming](./rules/naming/immutable-vars-naming.md) | Check Immutable variables. Capitalized SNAKE_CASE or mixedCase depending on configuration. | $~~~~~~~~$✔️ | |
43+
| [modifier-name-mixedcase](./rules/naming/modifier-name-mixedcase.md) | Modifier name must be in mixedCase. | | |
44+
| [named-parameters-mapping](./rules/naming/named-parameters-mapping.md) | Solidity v0.8.18 introduced named parameters on the mappings definition. | | |
45+
| [private-vars-leading-underscore](./rules/naming/private-vars-leading-underscore.md) | Non-external functions and state variables should start with a single underscore. Others, shouldn't | | |
46+
| [use-forbidden-name](./rules/naming/use-forbidden-name.md) | Avoid to use letters 'I', 'l', 'O' as identifiers. | $~~~~~~~~$✔️ | |
47+
| [var-name-mixedcase](./rules/naming/var-name-mixedcase.md) | Variable names must be in mixedCase. (Does not check IMMUTABLES nor CONSTANTS (use inherent rules for that) | $~~~~~~~~$✔️ | |
48+
| [imports-on-top](./rules/order/imports-on-top.md) | Import statements must be on top. | $~~~~~~~~$✔️ | |
49+
| [imports-order](./rules/order/imports-order.md) | Order the imports of the contract to follow a certain hierarchy (read "Notes section") | | |
50+
| [ordering](./rules/order/ordering.md) | Check order of elements in file and inside each contract, according to the style guide | | |
51+
| [visibility-modifier-order](./rules/order/visibility-modifier-order.md) | Visibility modifier must be first in list of modifiers. | $~~~~~~~~$✔️ | |
5152
5253

5354
## Gas Consumption Rules
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
---
2+
warning: "This is a dynamically generated file. Do not edit manually."
3+
layout: "default"
4+
title: "foundry-test-function-naming | Solhint"
5+
---
6+
7+
# foundry-test-function-naming
8+
![Category Badge](https://img.shields.io/badge/-Style%20Guide%20Rules-informational)
9+
![Default Severity Badge warn](https://img.shields.io/badge/Default%20Severity-warn-yellow)
10+
11+
## Description
12+
Enforce naming convention on functions for Foundry test cases
13+
14+
## Options
15+
This rule accepts an array of options:
16+
17+
| Index | Description | Default Value |
18+
| ----- | ----------------------------------------------------- | ------------- |
19+
| 0 | Rule severity. Must be one of "error", "warn", "off". | warn |
20+
| 1 | Array of required Foundry test hook function names. | setUp |
21+
22+
23+
### Example Config
24+
```json
25+
{
26+
"rules": {
27+
"foundry-test-function-naming": [
28+
"warn",
29+
[
30+
"setUp"
31+
]
32+
]
33+
}
34+
}
35+
```
36+
37+
### Notes
38+
- This rule can be configured to skip certain function names in the SKIP array. In Example Config. ```setUp``` function will be skipped
39+
- Supported Regex: ```test(Fork)?(Fuzz)?(Fail)?_(Revert(If_|When_){1})?\w{1,}```
40+
- This rule should be executed in a separate folder with a separate .solhint.json => ```solhint --config .solhint.json testFolder/**/*.sol```
41+
- This rule applies only to `external` and `public` functions
42+
- This rule skips the `setUp()` function by default
43+
44+
## Examples
45+
### 👍 Examples of **correct** code for this rule
46+
47+
#### Foundry test case with correct Function declaration
48+
49+
```solidity
50+
function test_NumberIs42() public {}
51+
```
52+
53+
#### Foundry test case with correct Function declaration
54+
55+
```solidity
56+
function testFail_Subtract43() public {}
57+
```
58+
59+
#### Foundry test case with correct Function declaration
60+
61+
```solidity
62+
function testFuzz_FuzzyTest() public {}
63+
```
64+
65+
### 👎 Examples of **incorrect** code for this rule
66+
67+
#### Foundry test case with incorrect Function declaration
68+
69+
```solidity
70+
function numberIs42() public {}
71+
```
72+
73+
## Version
74+
This rule was introduced in the latest version.
75+
76+
## Resources
77+
- [Rule source](https://github.com/protofire/solhint/blob/master/lib/rules/naming/foundry-test-function-naming.js)
78+
- [Document source](https://github.com/protofire/solhint/blob/master/docs/rules/naming/foundry-test-function-naming.md)
79+
- [Test cases](https://github.com/protofire/solhint/blob/master/test/rules/naming/foundry-test-function-naming.js)

docs/rules/naming/foundry-test-functions.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ title: "foundry-test-functions | Solhint"
99
![Default Severity Badge warn](https://img.shields.io/badge/Default%20Severity-warn-yellow)
1010

1111
## Description
12-
Enforce naming convention on functions for Foundry test cases
12+
Enforce naming convention on functions for Foundry test cases (DEPRECATED, use `foundry-test-functions-naming`)
1313

1414
## Options
1515
This rule accepts an array of options:

lib/common/identifier-naming.js

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -32,14 +32,20 @@ module.exports = {
3232
},
3333

3434
isFoundryTestCase(text) {
35-
// this one checks CamelCase after test keyword
35+
// PREVIOUS VERSION
36+
// → strict version: requires that after `test...` the first character must be uppercase (CamelCase enforced)
3637
// const regexTest = /^test(Fork)?(Fuzz)?(Fail)?(_)?[A-Z](Revert(If_|When_){1})?\w{1,}$/
38+
// PREVIOUS VERSION
39+
// → strict version: requires that after `invariant` or `statefulFuzz` the first character must be uppercase (CamelCase enforced)
40+
// const regexInvariant = /^(invariant|statefulFuzz)(_)?[A-Z]\w{1,}$/
3741

42+
// this one checks test functions with optional suffixes (Fork, Fuzz, Fail, RevertIf_, RevertWhen_, etc.)
43+
// now it only requires that after those, at least one word character exists (no CamelCase enforcement)
3844
const regexTest = /^test(Fork)?(Fuzz)?(Fail)?(_)?(Revert(If_|When_){1})?\w{1,}$/
3945
const matchRegexTest = match(text, regexTest)
4046

41-
// this one checks CamelCase after test keyword
42-
// const regexInvariant = /^(invariant|statefulFuzz)(_)?[A-Z]\w{1,}$/
47+
// this one checks invariant or statefulFuzz functions, with optional underscore,
48+
// followed by at least one word character (no CamelCase enforcement)
4349
const regexInvariant = /^(invariant|statefulFuzz)(_)?\w{1,}$/
4450
const matchRegexInvariant = match(text, regexInvariant)
4551

Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
const BaseChecker = require('../base-checker')
2+
const naming = require('../../common/identifier-naming')
3+
const { severityDescription } = require('../../doc/utils')
4+
5+
const DEFAULT_SEVERITY = 'warn'
6+
const DEFAULT_SKIP_FUNCTIONS = ['setUp']
7+
8+
const ruleId = 'foundry-test-function-naming'
9+
const meta = {
10+
type: 'naming',
11+
12+
docs: {
13+
description: `Enforce naming convention on functions for Foundry test cases`,
14+
category: 'Style Guide Rules',
15+
options: [
16+
{
17+
description: severityDescription,
18+
default: DEFAULT_SEVERITY,
19+
},
20+
{
21+
description: 'Array of required Foundry test hook function names.',
22+
default: DEFAULT_SKIP_FUNCTIONS,
23+
},
24+
],
25+
examples: {
26+
good: [
27+
{
28+
description: 'Foundry test case with correct Function declaration',
29+
code: 'function test_NumberIs42() public {}',
30+
},
31+
{
32+
description: 'Foundry test case with correct Function declaration',
33+
code: 'function testFail_Subtract43() public {}',
34+
},
35+
{
36+
description: 'Foundry test case with correct Function declaration',
37+
code: 'function testFuzz_FuzzyTest() public {}',
38+
},
39+
],
40+
bad: [
41+
{
42+
description: 'Foundry test case with incorrect Function declaration',
43+
code: 'function numberIs42() public {}',
44+
},
45+
],
46+
},
47+
notes: [
48+
{
49+
note: 'This rule can be configured to skip certain function names in the SKIP array. In Example Config. ```setUp``` function will be skipped',
50+
},
51+
{ note: 'Supported Regex: ```test(Fork)?(Fuzz)?(Fail)?_(Revert(If_|When_){1})?\\w{1,}```' },
52+
{
53+
note: 'This rule should be executed in a separate folder with a separate .solhint.json => ```solhint --config .solhint.json testFolder/**/*.sol```',
54+
},
55+
{
56+
note: 'This rule applies only to `external` and `public` functions',
57+
},
58+
{
59+
note: 'This rule skips the `setUp()` function by default',
60+
},
61+
],
62+
},
63+
64+
recommended: false,
65+
defaultSetup: [DEFAULT_SEVERITY, DEFAULT_SKIP_FUNCTIONS],
66+
67+
schema: {
68+
type: 'array',
69+
description: 'Array of function names to skip from the check',
70+
items: { type: 'string', errorMessage: 'Each item must be a string' },
71+
},
72+
}
73+
74+
class FoundryTestFunctionNaming extends BaseChecker {
75+
constructor(reporter, config) {
76+
super(reporter, ruleId, meta)
77+
this.skippedFunctions = config
78+
? config.getArray(ruleId, DEFAULT_SKIP_FUNCTIONS)
79+
: DEFAULT_SKIP_FUNCTIONS
80+
}
81+
82+
FunctionDefinition(node) {
83+
// function name should not be in skipped functions array
84+
// should be external or public
85+
if (
86+
!this.searchInArray(this.skippedFunctions, node.name) &&
87+
(node.visibility === 'public' || node.visibility === 'external')
88+
) {
89+
if (!naming.isFoundryTestCase(node.name)) {
90+
this.error(node, `Function ${node.name}() must match Foundry test naming convention`)
91+
}
92+
}
93+
}
94+
95+
searchInArray(array, searchString) {
96+
return array.indexOf(searchString) !== -1
97+
}
98+
}
99+
100+
module.exports = FoundryTestFunctionNaming

lib/rules/naming/foundry-test-functions.js

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,8 @@ const meta = {
1010
type: 'naming',
1111

1212
docs: {
13-
description: `Enforce naming convention on functions for Foundry test cases`,
13+
description:
14+
'Enforce naming convention on functions for Foundry test cases (DEPRECATED, use `foundry-test-functions-naming`)',
1415
category: 'Style Guide Rules',
1516
options: [
1617
{
@@ -77,6 +78,9 @@ class FoundryTestFunctionsChecker extends BaseChecker {
7778
this.skippedFunctions = config
7879
? config.getArray(ruleId, DEFAULT_SKIP_FUNCTIONS)
7980
: DEFAULT_SKIP_FUNCTIONS
81+
82+
// avoid spamming the deprecation warning
83+
this._deprecationWarned = false
8084
}
8185

8286
FunctionDefinition(node) {
@@ -87,7 +91,10 @@ class FoundryTestFunctionsChecker extends BaseChecker {
8791
(node.visibility === 'public' || node.visibility === 'external')
8892
) {
8993
if (!naming.isFoundryTestCase(node.name)) {
90-
this.error(node, `Function ${node.name}() must match Foundry test naming convention`)
94+
this.error(
95+
node,
96+
`[DEPRECATED] rule. Use "foundry-test-function-naming". Function ${node.name}() must match Foundry test naming convention`
97+
)
9198
}
9299
}
93100
}

0 commit comments

Comments
 (0)