Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,10 @@
## [6.0.2] - 2025-09-18

🧹 `Chore`: `foundry-test-functions` is deprecated and will be removed in v7.0.0. Please rename to `foundry-test-function-naming`.
WILL BE REPLACED IN v7

<br><br>

## [6.0.1] - 2025-08-22

🛠️ `Fix`: `no-unused-vars` for modifiers
Expand Down
1 change: 1 addition & 0 deletions conf/rulesets/solhint-all.js
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@ module.exports = Object.freeze({
'const-name-snakecase': 'warn',
'contract-name-capwords': 'warn',
'event-name-capwords': 'warn',
'foundry-test-function-naming': ['warn', ['setUp']],
'foundry-test-functions': ['warn', ['setUp']],
'func-name-mixedcase': 'warn',
'func-named-parameters': ['warn', 4],
Expand Down
43 changes: 22 additions & 21 deletions docs/rules.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,27 +27,28 @@ title: "Rule Index of Solhint"

## Style Guide Rules

| Rule Id | Error | Recommended | Deprecated |
| ------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------- | ------------ | ---------- |
| [interface-starts-with-i](./rules/naming/interface-starts-with-i.md) | Solidity Interfaces names should start with an `I` | $~~~~~~~~$✔️ | |
| [duplicated-imports](./rules/miscellaneous/duplicated-imports.md) | Check if an import is done twice in the same file and there is no alias | $~~~~~~~~$✔️ | |
| [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) | $~~~~~~~~$✔️ | |
| [contract-name-capwords](./rules/naming/contract-name-capwords.md) | Contract, Structs and Enums should be in CapWords. | $~~~~~~~~$✔️ | |
| [event-name-capwords](./rules/naming/event-name-capwords.md) | Event name must be in CapWords. | $~~~~~~~~$✔️ | |
| [foundry-test-functions](./rules/naming/foundry-test-functions.md) | Enforce naming convention on functions for Foundry test cases | | |
| [func-name-mixedcase](./rules/naming/func-name-mixedcase.md) | Function name must be in mixedCase. | $~~~~~~~~$✔️ | |
| [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 | | |
| [func-param-name-mixedcase](./rules/naming/func-param-name-mixedcase.md) | Function param name must be in mixedCase. | | |
| [immutable-vars-naming](./rules/naming/immutable-vars-naming.md) | Check Immutable variables. Capitalized SNAKE_CASE or mixedCase depending on configuration. | $~~~~~~~~$✔️ | |
| [modifier-name-mixedcase](./rules/naming/modifier-name-mixedcase.md) | Modifier name must be in mixedCase. | | |
| [named-parameters-mapping](./rules/naming/named-parameters-mapping.md) | Solidity v0.8.18 introduced named parameters on the mappings definition. | | |
| [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 | | |
| [use-forbidden-name](./rules/naming/use-forbidden-name.md) | Avoid to use letters 'I', 'l', 'O' as identifiers. | $~~~~~~~~$✔️ | |
| [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) | $~~~~~~~~$✔️ | |
| [imports-on-top](./rules/order/imports-on-top.md) | Import statements must be on top. | $~~~~~~~~$✔️ | |
| [imports-order](./rules/order/imports-order.md) | Order the imports of the contract to follow a certain hierarchy (read "Notes section") | | |
| [ordering](./rules/order/ordering.md) | Check order of elements in file and inside each contract, according to the style guide | | |
| [visibility-modifier-order](./rules/order/visibility-modifier-order.md) | Visibility modifier must be first in list of modifiers. | $~~~~~~~~$✔️ | |
| Rule Id | Error | Recommended | Deprecated |
| ------------------------------------------------------------------------------------ | --------------------------------------------------------------------------------------------------------------- | ------------ | ---------- |
| [interface-starts-with-i](./rules/naming/interface-starts-with-i.md) | Solidity Interfaces names should start with an `I` | $~~~~~~~~$✔️ | |
| [duplicated-imports](./rules/miscellaneous/duplicated-imports.md) | Check if an import is done twice in the same file and there is no alias | $~~~~~~~~$✔️ | |
| [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) | $~~~~~~~~$✔️ | |
| [contract-name-capwords](./rules/naming/contract-name-capwords.md) | Contract, Structs and Enums should be in CapWords. | $~~~~~~~~$✔️ | |
| [event-name-capwords](./rules/naming/event-name-capwords.md) | Event name must be in CapWords. | $~~~~~~~~$✔️ | |
| [foundry-test-function-naming](./rules/naming/foundry-test-function-naming.md) | Enforce naming convention on functions for Foundry test cases | | |
| [foundry-test-functions](./rules/naming/foundry-test-functions.md) | Enforce naming convention on functions for Foundry test cases (DEPRECATED, use `foundry-test-functions-naming`) | | |
| [func-name-mixedcase](./rules/naming/func-name-mixedcase.md) | Function name must be in mixedCase. | $~~~~~~~~$✔️ | |
| [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 | | |
| [func-param-name-mixedcase](./rules/naming/func-param-name-mixedcase.md) | Function param name must be in mixedCase. | | |
| [immutable-vars-naming](./rules/naming/immutable-vars-naming.md) | Check Immutable variables. Capitalized SNAKE_CASE or mixedCase depending on configuration. | $~~~~~~~~$✔️ | |
| [modifier-name-mixedcase](./rules/naming/modifier-name-mixedcase.md) | Modifier name must be in mixedCase. | | |
| [named-parameters-mapping](./rules/naming/named-parameters-mapping.md) | Solidity v0.8.18 introduced named parameters on the mappings definition. | | |
| [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 | | |
| [use-forbidden-name](./rules/naming/use-forbidden-name.md) | Avoid to use letters 'I', 'l', 'O' as identifiers. | $~~~~~~~~$✔️ | |
| [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) | $~~~~~~~~$✔️ | |
| [imports-on-top](./rules/order/imports-on-top.md) | Import statements must be on top. | $~~~~~~~~$✔️ | |
| [imports-order](./rules/order/imports-order.md) | Order the imports of the contract to follow a certain hierarchy (read "Notes section") | | |
| [ordering](./rules/order/ordering.md) | Check order of elements in file and inside each contract, according to the style guide | | |
| [visibility-modifier-order](./rules/order/visibility-modifier-order.md) | Visibility modifier must be first in list of modifiers. | $~~~~~~~~$✔️ | |


## Gas Consumption Rules
Expand Down
79 changes: 79 additions & 0 deletions docs/rules/naming/foundry-test-function-naming.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
---
warning: "This is a dynamically generated file. Do not edit manually."
layout: "default"
title: "foundry-test-function-naming | Solhint"
---

# foundry-test-function-naming
![Category Badge](https://img.shields.io/badge/-Style%20Guide%20Rules-informational)
![Default Severity Badge warn](https://img.shields.io/badge/Default%20Severity-warn-yellow)

## Description
Enforce naming convention on functions for Foundry test cases

## Options
This rule accepts an array of options:

| Index | Description | Default Value |
| ----- | ----------------------------------------------------- | ------------- |
| 0 | Rule severity. Must be one of "error", "warn", "off". | warn |
| 1 | Array of required Foundry test hook function names. | setUp |


### Example Config
```json
{
"rules": {
"foundry-test-function-naming": [
"warn",
[
"setUp"
]
]
}
}
```

### Notes
- This rule can be configured to skip certain function names in the SKIP array. In Example Config. ```setUp``` function will be skipped
- Supported Regex: ```test(Fork)?(Fuzz)?(Fail)?_(Revert(If_|When_){1})?\w{1,}```
- This rule should be executed in a separate folder with a separate .solhint.json => ```solhint --config .solhint.json testFolder/**/*.sol```
- This rule applies only to `external` and `public` functions
- This rule skips the `setUp()` function by default

## Examples
### 👍 Examples of **correct** code for this rule

#### Foundry test case with correct Function declaration

```solidity
function test_NumberIs42() public {}
```

#### Foundry test case with correct Function declaration

```solidity
function testFail_Subtract43() public {}
```

#### Foundry test case with correct Function declaration

```solidity
function testFuzz_FuzzyTest() public {}
```

### 👎 Examples of **incorrect** code for this rule

#### Foundry test case with incorrect Function declaration

```solidity
function numberIs42() public {}
```

## Version
This rule was introduced in the latest version.

## Resources
- [Rule source](https://github.com/protofire/solhint/blob/master/lib/rules/naming/foundry-test-function-naming.js)
- [Document source](https://github.com/protofire/solhint/blob/master/docs/rules/naming/foundry-test-function-naming.md)
- [Test cases](https://github.com/protofire/solhint/blob/master/test/rules/naming/foundry-test-function-naming.js)
2 changes: 1 addition & 1 deletion docs/rules/naming/foundry-test-functions.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ title: "foundry-test-functions | Solhint"
![Default Severity Badge warn](https://img.shields.io/badge/Default%20Severity-warn-yellow)

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

## Options
This rule accepts an array of options:
Expand Down
12 changes: 9 additions & 3 deletions lib/common/identifier-naming.js
Original file line number Diff line number Diff line change
Expand Up @@ -32,14 +32,20 @@ module.exports = {
},

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

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

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

Expand Down
100 changes: 100 additions & 0 deletions lib/rules/naming/foundry-test-function-naming.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
const BaseChecker = require('../base-checker')
const naming = require('../../common/identifier-naming')
const { severityDescription } = require('../../doc/utils')

const DEFAULT_SEVERITY = 'warn'
const DEFAULT_SKIP_FUNCTIONS = ['setUp']

const ruleId = 'foundry-test-function-naming'
const meta = {
type: 'naming',

docs: {
description: `Enforce naming convention on functions for Foundry test cases`,
category: 'Style Guide Rules',
options: [
{
description: severityDescription,
default: DEFAULT_SEVERITY,
},
{
description: 'Array of required Foundry test hook function names.',
default: DEFAULT_SKIP_FUNCTIONS,
},
],
examples: {
good: [
{
description: 'Foundry test case with correct Function declaration',
code: 'function test_NumberIs42() public {}',
},
{
description: 'Foundry test case with correct Function declaration',
code: 'function testFail_Subtract43() public {}',
},
{
description: 'Foundry test case with correct Function declaration',
code: 'function testFuzz_FuzzyTest() public {}',
},
],
bad: [
{
description: 'Foundry test case with incorrect Function declaration',
code: 'function numberIs42() public {}',
},
],
},
notes: [
{
note: 'This rule can be configured to skip certain function names in the SKIP array. In Example Config. ```setUp``` function will be skipped',
},
{ note: 'Supported Regex: ```test(Fork)?(Fuzz)?(Fail)?_(Revert(If_|When_){1})?\\w{1,}```' },
{
note: 'This rule should be executed in a separate folder with a separate .solhint.json => ```solhint --config .solhint.json testFolder/**/*.sol```',
},
{
note: 'This rule applies only to `external` and `public` functions',
},
{
note: 'This rule skips the `setUp()` function by default',
},
],
},

recommended: false,
defaultSetup: [DEFAULT_SEVERITY, DEFAULT_SKIP_FUNCTIONS],

schema: {
type: 'array',
description: 'Array of function names to skip from the check',
items: { type: 'string', errorMessage: 'Each item must be a string' },
},
}

class FoundryTestFunctionNaming extends BaseChecker {
constructor(reporter, config) {
super(reporter, ruleId, meta)
this.skippedFunctions = config
? config.getArray(ruleId, DEFAULT_SKIP_FUNCTIONS)
: DEFAULT_SKIP_FUNCTIONS
}

FunctionDefinition(node) {
// function name should not be in skipped functions array
// should be external or public
if (
!this.searchInArray(this.skippedFunctions, node.name) &&
(node.visibility === 'public' || node.visibility === 'external')
) {
if (!naming.isFoundryTestCase(node.name)) {
this.error(node, `Function ${node.name}() must match Foundry test naming convention`)
}
}
}

searchInArray(array, searchString) {
return array.indexOf(searchString) !== -1
}
}

module.exports = FoundryTestFunctionNaming
11 changes: 9 additions & 2 deletions lib/rules/naming/foundry-test-functions.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@ const meta = {
type: 'naming',

docs: {
description: `Enforce naming convention on functions for Foundry test cases`,
description:
'Enforce naming convention on functions for Foundry test cases (DEPRECATED, use `foundry-test-functions-naming`)',
category: 'Style Guide Rules',
options: [
{
Expand Down Expand Up @@ -77,6 +78,9 @@ class FoundryTestFunctionsChecker extends BaseChecker {
this.skippedFunctions = config
? config.getArray(ruleId, DEFAULT_SKIP_FUNCTIONS)
: DEFAULT_SKIP_FUNCTIONS

// avoid spamming the deprecation warning
this._deprecationWarned = false
}

FunctionDefinition(node) {
Expand All @@ -87,7 +91,10 @@ class FoundryTestFunctionsChecker extends BaseChecker {
(node.visibility === 'public' || node.visibility === 'external')
) {
if (!naming.isFoundryTestCase(node.name)) {
this.error(node, `Function ${node.name}() must match Foundry test naming convention`)
this.error(
node,
`[DEPRECATED] rule. Use "foundry-test-function-naming". Function ${node.name}() must match Foundry test naming convention`
)
}
}
}
Expand Down
Loading
Loading