Repository: protofire/solhint
Pull request #484 (2023-08-16 13:34:37)

Fix: I402 foundry test functions
Removed mandatory underscore after fix
Added `invariant` and `statefulFuzz` keywords 


Patch:
diff --git a/lib/common/identifier-naming.js b/lib/common/identifier-naming.js
index efc7e3f..970bcc7 100644
--- a/lib/common/identifier-naming.js
+++ b/lib/common/identifier-naming.js
@@ -31,8 +31,18 @@ module.exports = {
     return text && text[0] === '_'
   },
 
-  isNotFoundryTestCase(text) {
-    const regex = /^test(Fork)?(Fuzz)?(Fail)?_(Revert(If_|When_){1})?\w{1,}$/
-    return !match(text, regex)
+  isFoundryTestCase(text) {
+    // this one checks CamelCase after test keyword
+    // const regexTest = /^test(Fork)?(Fuzz)?(Fail)?(_)?[A-Z](Revert(If_|When_){1})?\w{1,}$/
+
+    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,}$/
+    const regexInvariant = /^(invariant|statefulFuzz)(_)?\w{1,}$/
+    const matchRegexInvariant = match(text, regexInvariant)
+
+    return matchRegexTest || matchRegexInvariant
   },
 }


Test patch:
diff --git a/docs/rules/naming/foundry-test-functions.md b/docs/rules/naming/foundry-test-functions.md
index 534ad6e..0f20475 100644
--- a/docs/rules/naming/foundry-test-functions.md
+++ b/docs/rules/naming/foundry-test-functions.md
@@ -33,7 +33,7 @@ This rule accepts an array of options:
 - 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
+- This rule skips the `setUp()` function by default
 
 ## Examples
 ### 👍 Examples of **correct** code for this rule
diff --git a/lib/rules/naming/foundry-test-functions.js b/lib/rules/naming/foundry-test-functions.js
index 1cc367a..4a7b364 100644
--- a/lib/rules/naming/foundry-test-functions.js
+++ b/lib/rules/naming/foundry-test-functions.js
@@ -52,7 +52,7 @@ const meta = {
         note: 'This rule applies only to `external` and `public` functions',
       },
       {
-        note: 'This rule skips the `setUp()` function',
+        note: 'This rule skips the `setUp()` function by default',
       },
     ],
   },
@@ -79,7 +79,7 @@ class FoundryTestFunctionsChecker extends BaseChecker {
       !this.searchInArray(this.skippedFunctions, node.name) &&
       (node.visibility === 'public' || node.visibility === 'external')
     ) {
-      if (naming.isNotFoundryTestCase(node.name)) {
+      if (!naming.isFoundryTestCase(node.name)) {
         this.error(node, `Function ${node.name}() must match Foundry test naming convention`)
       }
     }
diff --git a/test/rules/naming/foundry-test-functions.js b/test/rules/naming/foundry-test-functions.js
index 9a0feeb..53c70ca 100644
--- a/test/rules/naming/foundry-test-functions.js
+++ b/test/rules/naming/foundry-test-functions.js
@@ -4,6 +4,7 @@ const contractWith = require('../../common/contract-builder').contractWith
 const { assertErrorCount, assertNoErrors, assertErrorMessage } = require('../../common/asserts')
 
 const ALLOWED_FUNCTION_NAMES = [
+  'test',
   'test_',
   'testFork_',
   'testFuzz_',
@@ -20,9 +21,14 @@ const ALLOWED_FUNCTION_NAMES = [
   'testFuzz_Revert_',
   'testFuzz_If_',
   'testFuzz_When_',
+  'invariant',
+  'invariant_',
+  'invariantA',
+  'statefulFuzz',
+  'statefulFuzz_',
 ]
 
-const DISALLOWED_FUNCTION_NAMES = ['Test_', 'Test', 'test', 'testo_', '', 'any', 'setUp', 'other']
+const DISALLOWED_FUNCTION_NAMES = ['Test_', 'Test', '', 'any', 'setUp', 'other', '_']
 
 const composeFunctionName = (prefix, name, visibility) =>
   'function ' + prefix + name + ' ' + visibility + ' { testNumber = 42; }'
@@ -33,6 +39,7 @@ describe('Linter - foundry-test-functions', () => {
       const functionDefinition = composeFunctionName(prefix, 'FunctionName()', 'public')
       const code = contractWith(functionDefinition)
 
+      console.log('`code` :>> ', code)
       const report = linter.processStr(code, {
         rules: { 'foundry-test-functions': ['error', ['setUp', 'finish']] },
       })
@@ -171,7 +178,7 @@ describe('Linter - foundry-test-functions', () => {
     )
   })
 
-  it(`should NOT raise error only for setUp when rule is just on 'error'`, () => {
+  it(`should NOT raise error only for setUp when rule is just on 'error' (setUp is default)`, () => {
     const code = contractWith(`
       function setUp() public { testNumber = 42; }
       function finish() public { testNumber = 43; }