Repository: protofire/solhint
Pull request #463 (2023-07-31 14:07:54)

Merge Develop into Master


Patch:
diff --git a/.eslintrc.js b/.eslintrc.js
index c953335..02e2d70 100644
--- a/.eslintrc.js
+++ b/.eslintrc.js
@@ -1,6 +1,6 @@
 module.exports = {
   parserOptions: {
-    ecmaVersion: 8,
+    ecmaVersion: 2020,
   },
   env: {
     browser: false,
@@ -18,6 +18,7 @@ module.exports = {
     'global-require': 'off',
     'no-bitwise': 'off',
     'no-console': 'off',
+    'func-names': 'off',
     'no-continue': 'off',
     'no-else-return': 'off',
     'no-param-reassign': 'off',
diff --git a/.gitignore b/.gitignore
index 3614b26..77dd2e5 100644
--- a/.gitignore
+++ b/.gitignore
@@ -9,3 +9,4 @@ convertLib.sol
 antlr4.jar
 /docs/.sass-cache/
 _temp/
+.solhint.json
diff --git a/.husky/pre-commit b/.husky/pre-commit
new file mode 100755
index 0000000..e09f21f
--- /dev/null
+++ b/.husky/pre-commit
@@ -0,0 +1,4 @@
+#!/usr/bin/env sh
+. "$(dirname -- "$0")/_/husky.sh"
+
+sh ./scripts/generate-changed.sh
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 9af36d1..d63bff5 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,29 @@
+## [3.4.2] - 2023-08-04
+### Updated
+- Support `ignoreConstructors` option for `no-empty-blocks` [#418](https://github.com/protofire/solhint/pull/418)
+- Bump json5 from 2.1.3 to 2.2.3 [#376](https://github.com/protofire/solhint/pull/376)
+- Bump json-schema and jsprim [#370](https://github.com/protofire/solhint/pull/370)
+- Bump semver from 6.3.0 to 7.5.2 [#438](https://github.com/protofire/solhint/pull/438)
+- Corrected "Category" of `quotes` rule, added default rules list on readme [#443](https://github.com/protofire/solhint/pull/443)
+- 'Deprecated' column on `rules.md`` [#444](https://github.com/protofire/solhint/pull/444)
+- Information about maxCharacters allowed on `reason-string` rule [#446](https://github.com/protofire/solhint/pull/446)
+- E2E tests for `max-warnings` [#455](https://github.com/protofire/solhint/pull/455)
+- Replaced blacklist and whitelist words [#459](https://github.com/protofire/solhint/pull/459)
+### Added
+- New Rule: No unused imports [#417](https://github.com/protofire/solhint/pull/417)
+- New Rule: To treat immutable as constants [#458](https://github.com/protofire/solhint/pull/458)
+- New Rule: Explicit-types. To forbid/enforce full type or alias for variables declaration [#467](https://github.com/protofire/solhint/pull/467)
+- JSON formatter support [#440](https://github.com/protofire/solhint/pull/440)
+- Rules List with `list-rules` command [#449](https://github.com/protofire/solhint/pull/449)
+- E2E tests for formatters and new `Compact formatter` [#457](https://github.com/protofire/solhint/pull/457)
+
+### Fixed
+- `maxWarnings` parameter waiting review [#439](https://github.com/protofire/solhint/pull/439)
+- `–fix` option not working in avoid-throw rule [#442](https://github.com/protofire/solhint/pull/442)
+- Formatter option fixed for `stdin` command [#450](https://github.com/protofire/solhint/pull/450)
+
+
+<br><br>
 ## [3.4.1] - 2023-03-06
 ### Updated
 - Updated solidity parser to 0.16.0 [#420](https://github.com/protofire/solhint/pull/420)
diff --git a/README.md b/README.md
index c734a92..0f010f6 100644
--- a/README.md
+++ b/README.md
@@ -55,7 +55,7 @@ Linter for Solidity programming language
 Options:
 
   -V, --version                           output the version number
-  -f, --formatter [name]                  report formatter name (stylish, table, tap, unix)
+  -f, --formatter [name]                  report formatter name (stylish, table, tap, unix, json, compact)
   -w, --max-warnings [maxWarningsNumber]  number of allowed warnings
   -c, --config [file_name]                file to use as your .solhint.json
   -q, --quiet                             report errors only - default: false
@@ -67,8 +67,11 @@ Options:
 Commands:
 
   stdin [options]                         linting of source code data provided to STDIN
+  list-rules                              display covered rules of current .solhint.json
 ```
-
+### Note
+The `--fix`  option currently works only on "avoid-throw" and "avoid-sha3" rules
+<br><br>
 ## Configuration
 
 You can use a `.solhint.json` file to configure Solhint for the whole project.
@@ -86,6 +89,10 @@ This file has the following format:
   "extends": "solhint:default"
 }
 ```
+### Note
+The `solhint:default` configuration contains only two rules: max-line-length & no-console
+<br><br>
+
 
 ### Sample
 ```json
diff --git a/ROADMAP.md b/ROADMAP.md
index eac74d5..4b90f8d 100644
--- a/ROADMAP.md
+++ b/ROADMAP.md
@@ -16,7 +16,7 @@ This is our next releases plan:
 - 1.4.0 [released on Oct 10, 2018]
   - Support prettier-solidity [#72]
 - 2.0.0.beta.1 [released beta version on Jan 31, 2019]
-  - Switch from Blacklisting to Rulesets + Whitelisting [#73]
+  - Switch from Blocklisting to Rulesets + Allowlisting [#73]
   - Plugins support [#70]
   - Support prettier-solidity as a plugin [#72]
 - 2.1.0
diff --git a/conf/rulesets/solhint-all.js b/conf/rulesets/solhint-all.js
index ff348be..6ceced1 100644
--- a/conf/rulesets/solhint-all.js
+++ b/conf/rulesets/solhint-all.js
@@ -1,12 +1,75 @@
-const { loadRules } = require('../../lib/load-rules')
+/*
+ * WARNING: This file is autogenerated using the scripts/generate-rulesets.js
+ * script. Do not edit manually.
+ */
 
-const rulesConstants = loadRules()
-const enabledRules = {}
-
-rulesConstants.forEach((rule) => {
-  if (!rule.meta.deprecated) {
-    enabledRules[rule.ruleId] = rule.meta.defaultSetup
-  }
+module.exports = Object.freeze({
+  rules: {
+    'code-complexity': ['warn', 7],
+    'explicit-types': ['warn', 'explicit'],
+    'function-max-lines': ['warn', 50],
+    'max-line-length': ['error', 120],
+    'max-states-count': ['warn', 15],
+    'no-console': 'error',
+    'no-empty-blocks': 'warn',
+    'no-global-import': 'warn',
+    'no-unused-import': 'warn',
+    'no-unused-vars': 'warn',
+    'payable-fallback': 'warn',
+    'reason-string': [
+      'warn',
+      {
+        maxLength: 32,
+      },
+    ],
+    'constructor-syntax': 'warn',
+    'comprehensive-interface': 'warn',
+    quotes: ['error', 'double'],
+    'const-name-snakecase': 'warn',
+    'contract-name-camelcase': 'warn',
+    'event-name-camelcase': 'warn',
+    'func-name-mixedcase': 'warn',
+    'func-named-parameters': ['warn', 2],
+    'func-param-name-mixedcase': 'warn',
+    'immutable-vars-naming': [
+      'warn',
+      {
+        immutablesAsConstants: true,
+      },
+    ],
+    'modifier-name-mixedcase': 'warn',
+    'named-parameters-mapping': 'off',
+    'private-vars-leading-underscore': [
+      'warn',
+      {
+        strict: false,
+      },
+    ],
+    'use-forbidden-name': 'warn',
+    'var-name-mixedcase': 'warn',
+    'imports-on-top': 'warn',
+    ordering: 'warn',
+    'visibility-modifier-order': 'warn',
+    'avoid-call-value': 'warn',
+    'avoid-low-level-calls': 'warn',
+    'avoid-sha3': 'warn',
+    'avoid-suicide': 'warn',
+    'avoid-throw': 'warn',
+    'avoid-tx-origin': 'warn',
+    'check-send-result': 'warn',
+    'compiler-version': ['error', '^0.5.8'],
+    'func-visibility': [
+      'warn',
+      {
+        ignoreConstructors: false,
+      },
+    ],
+    'multiple-sends': 'warn',
+    'no-complex-fallback': 'warn',
+    'no-inline-assembly': 'warn',
+    'not-rely-on-block-hash': 'warn',
+    'not-rely-on-time': 'warn',
+    reentrancy: 'warn',
+    'state-visibility': 'warn',
+  },
 })
-
-module.exports = { rules: enabledRules }
diff --git a/conf/rulesets/solhint-default.js b/conf/rulesets/solhint-default.js
index 2840f56..8f622ee 100644
--- a/conf/rulesets/solhint-default.js
+++ b/conf/rulesets/solhint-default.js
@@ -1,12 +1,11 @@
-const { loadRules } = require('../../lib/load-rules')
+/*
+ * WARNING: This file is autogenerated using the scripts/generate-rulesets.js
+ * script. Do not edit manually.
+ */
 
-const rulesConstants = loadRules()
-const enabledRules = {}
-
-rulesConstants.forEach((rule) => {
-  if (!rule.meta.deprecated && rule.meta.isDefault) {
-    enabledRules[rule.ruleId] = rule.meta.defaultSetup
-  }
+module.exports = Object.freeze({
+  rules: {
+    'max-line-length': ['error', 120],
+    'no-console': 'error',
+  },
 })
-
-module.exports = { rules: enabledRules }
diff --git a/conf/rulesets/solhint-recommended.js b/conf/rulesets/solhint-recommended.js
index 18361c9..dbd05e7 100644
--- a/conf/rulesets/solhint-recommended.js
+++ b/conf/rulesets/solhint-recommended.js
@@ -1,12 +1,60 @@
-const { loadRules } = require('../../lib/load-rules')
+/*
+ * WARNING: This file is autogenerated using the scripts/generate-rulesets.js
+ * script. Do not edit manually.
+ */
 
-const rulesConstants = loadRules()
-const enabledRules = {}
-
-rulesConstants.forEach((rule) => {
-  if (!rule.meta.deprecated && rule.meta.recommended) {
-    enabledRules[rule.ruleId] = rule.meta.defaultSetup
-  }
+module.exports = Object.freeze({
+  rules: {
+    'explicit-types': ['warn', 'explicit'],
+    'max-states-count': ['warn', 15],
+    'no-console': 'error',
+    'no-empty-blocks': 'warn',
+    'no-global-import': 'warn',
+    'no-unused-import': 'warn',
+    'no-unused-vars': 'warn',
+    'payable-fallback': 'warn',
+    'reason-string': [
+      'warn',
+      {
+        maxLength: 32,
+      },
+    ],
+    quotes: ['error', 'double'],
+    'const-name-snakecase': 'warn',
+    'contract-name-camelcase': 'warn',
+    'event-name-camelcase': 'warn',
+    'func-name-mixedcase': 'warn',
+    'func-named-parameters': ['warn', 2],
+    'immutable-vars-naming': [
+      'warn',
+      {
+        immutablesAsConstants: true,
+      },
+    ],
+    'use-forbidden-name': 'warn',
+    'var-name-mixedcase': 'warn',
+    'imports-on-top': 'warn',
+    'visibility-modifier-order': 'warn',
+    'avoid-call-value': 'warn',
+    'avoid-low-level-calls': 'warn',
+    'avoid-sha3': 'warn',
+    'avoid-suicide': 'warn',
+    'avoid-throw': 'warn',
+    'avoid-tx-origin': 'warn',
+    'check-send-result': 'warn',
+    'compiler-version': ['error', '^0.5.8'],
+    'func-visibility': [
+      'warn',
+      {
+        ignoreConstructors: false,
+      },
+    ],
+    'multiple-sends': 'warn',
+    'no-complex-fallback': 'warn',
+    'no-inline-assembly': 'warn',
+    'not-rely-on-block-hash': 'warn',
+    'not-rely-on-time': 'warn',
+    reentrancy: 'warn',
+    'state-visibility': 'warn',
+  },
 })
-
-module.exports = { rules: enabledRules }
diff --git a/docs/rules.md b/docs/rules.md
index 65aacdf..1a7d2c2 100644
--- a/docs/rules.md
+++ b/docs/rules.md
@@ -6,70 +6,74 @@ title:       "Rule Index of Solhint"
 
 ## Best Practise Rules
 
-| Rule Id                                                            | Error                                                                                                                 | Recommended |
-| ------------------------------------------------------------------ | --------------------------------------------------------------------------------------------------------------------- | ----------- |
-| [code-complexity](./rules/best-practises/code-complexity.md)       | Function has cyclomatic complexity "current" but allowed no more than maxcompl.                                       |             |
-| [function-max-lines](./rules/best-practises/function-max-lines.md) | Function body contains "count" lines but allowed no more than maxlines.                                               |             |
-| [max-line-length](./rules/best-practises/max-line-length.md)       | Line length must be no more than maxlen.                                                                              |             |
-| [max-states-count](./rules/best-practises/max-states-count.md)     | Contract has "some count" states declarations but allowed no more than maxstates.                                     | ✔️          |
-| [no-console](./rules/best-practises/no-console.md)                 | No console.log/logInt/logBytesX/logString/etc & No hardhat and forge-std console.sol import statements                | ✔️          |
-| [no-empty-blocks](./rules/best-practises/no-empty-blocks.md)       | Code contains empty block.                                                                                            | ✔️          |
-| [no-global-import](./rules/best-practises/no-global-import.md)     | Import statement includes an entire file instead of selected symbols                                                  | ✔️          |
-| [no-unused-vars](./rules/best-practises/no-unused-vars.md)         | Variable "name" is unused.                                                                                            | ✔️          |
-| [payable-fallback](./rules/best-practises/payable-fallback.md)     | When fallback is not payable you will not be able to receive ethers.                                                  | ✔️          |
-| [reason-string](./rules/best-practises/reason-string.md)           | Require or revert statement must have a reason string and check that each reason string is at most N characters long. | ✔️          |
-| [constructor-syntax](./rules/best-practises/constructor-syntax.md) | Constructors should use the new constructor keyword.                                                                  |             |
+| Rule Id                                                            | Error                                                                                                                 | Recommended  | Deprecated |
+| ------------------------------------------------------------------ | --------------------------------------------------------------------------------------------------------------------- | ------------ | ---------- |
+| [code-complexity](./rules/best-practises/code-complexity.md)       | Function has cyclomatic complexity "current" but allowed no more than maxcompl.                                       |              |            |
+| [explicit-types](./rules/best-practises/explicit-types.md)         | Forbid or enforce explicit types (like uint256) that have an alias (like uint).                                       | $~~~~~~~~$✔️ |            |
+| [function-max-lines](./rules/best-practises/function-max-lines.md) | Function body contains "count" lines but allowed no more than maxlines.                                               |              |            |
+| [max-line-length](./rules/best-practises/max-line-length.md)       | Line length must be no more than maxlen.                                                                              |              |            |
+| [max-states-count](./rules/best-practises/max-states-count.md)     | Contract has "some count" states declarations but allowed no more than maxstates.                                     | $~~~~~~~~$✔️ |            |
+| [no-console](./rules/best-practises/no-console.md)                 | No console.log/logInt/logBytesX/logString/etc & No hardhat and forge-std console.sol import statements.               | $~~~~~~~~$✔️ |            |
+| [no-empty-blocks](./rules/best-practises/no-empty-blocks.md)       | Code block has zero statements inside. Exceptions apply.                                                              | $~~~~~~~~$✔️ |            |
+| [no-global-import](./rules/best-practises/no-global-import.md)     | Import statement includes an entire file instead of selected symbols.                                                 | $~~~~~~~~$✔️ |            |
+| [no-unused-import](./rules/best-practises/no-unused-import.md)     | Imported name is not used.                                                                                            | $~~~~~~~~$✔️ |            |
+| [no-unused-vars](./rules/best-practises/no-unused-vars.md)         | Variable "name" is unused.                                                                                            | $~~~~~~~~$✔️ |            |
+| [payable-fallback](./rules/best-practises/payable-fallback.md)     | When fallback is not payable you will not be able to receive ethers.                                                  | $~~~~~~~~$✔️ |            |
+| [reason-string](./rules/best-practises/reason-string.md)           | Require or revert statement must have a reason string and check that each reason string is at most N characters long. | $~~~~~~~~$✔️ |            |
+| [constructor-syntax](./rules/best-practises/constructor-syntax.md) | Constructors should use the new constructor keyword.                                                                  |              |            |
         
 
 ## Miscellaneous
 
-| Rule Id                                                                     | Error                                                                                                                                  | Recommended |
-| --------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------- | ----------- |
-| [comprehensive-interface](./rules/miscellaneous/comprehensive-interface.md) | Check that all public or external functions are override. This is iseful to make sure that the whole API is extracted in an interface. |             |
+| Rule Id                                                                     | Error                                                                                                                                  | Recommended  | Deprecated |
+| --------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------- | ------------ | ---------- |
+| [comprehensive-interface](./rules/miscellaneous/comprehensive-interface.md) | Check that all public or external functions are override. This is iseful to make sure that the whole API is extracted in an interface. |              |            |
+| [quotes](./rules/miscellaneous/quotes.md)                                   | Use double quotes for string literals. Values must be 'single' or 'double'.                                                            | $~~~~~~~~$✔️ |            |
         
 
 ## Style Guide Rules
 
-| Rule Id                                                                              | Error                                                                                  | Recommended |
-| ------------------------------------------------------------------------------------ | -------------------------------------------------------------------------------------- | ----------- |
-| [quotes](./rules/miscellaneous/quotes.md)                                            | Use double quotes for string literals. Values must be 'single' or 'double'.            | ✔️          |
-| [const-name-snakecase](./rules/naming/const-name-snakecase.md)                       | Constant name must be in capitalized SNAKE_CASE.                                       | ✔️          |
-| [contract-name-camelcase](./rules/naming/contract-name-camelcase.md)                 | Contract name must be in CamelCase.                                                    | ✔️          |
-| [event-name-camelcase](./rules/naming/event-name-camelcase.md)                       | Event name must be in CamelCase.                                                       | ✔️          |
-| [func-name-mixedcase](./rules/naming/func-name-mixedcase.md)                         | Function name must be in mixedCase.                                                    | ✔️          |
-| [func-param-name-mixedcase](./rules/naming/func-param-name-mixedcase.md)             | Function param name must be in mixedCase                                               |             |
-| [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) | Private and internal names must start with a single underscore.                        |             |
-| [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 name must be in mixedCase.                                                    | ✔️          |
-| [func-order](./rules/order/func-order.md)                                            | Function order is incorrect.                                                           |             |
-| [imports-on-top](./rules/order/imports-on-top.md)                                    | Import statements must be on top.                                                      | ✔️          |
-| [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  |
+| ------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------- | ------------ | ----------- |
+| [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-camelcase](./rules/naming/contract-name-camelcase.md)                 | Contract name must be in CamelCase.                                                                     | $~~~~~~~~$✔️ |             |
+| [event-name-camelcase](./rules/naming/event-name-camelcase.md)                       | Event name must be in CamelCase.                                                                        | $~~~~~~~~$✔️ |             |
+| [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 function calls with Named Parameters when containing more than the configured qty               | $~~~~~~~~$✔️ |             |
+| [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) | Private and internal names must start with a single underscore.                                         |              |             |
+| [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 name must be in mixedCase. (Does not check IMMUTABLES, use immutable-vars-naming)              | $~~~~~~~~$✔️ |             |
+| [func-order](./rules/order/func-order.md)                                            | Function order is incorrect.                                                                            |              | $~~~~~~~$✔️ |
+| [imports-on-top](./rules/order/imports-on-top.md)                                    | Import statements must be on top.                                                                       | $~~~~~~~~$✔️ |             |
+| [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.                                                 | $~~~~~~~~$✔️ |             |
         
 
 ## Security Rules
 
-| Rule Id                                                                | Error                                                                    | Recommended |
-| ---------------------------------------------------------------------- | ------------------------------------------------------------------------ | ----------- |
-| [avoid-call-value](./rules/security/avoid-call-value.md)               | Avoid to use ".call.value()()".                                          | ✔️          |
-| [avoid-low-level-calls](./rules/security/avoid-low-level-calls.md)     | Avoid to use low level calls.                                            | ✔️          |
-| [avoid-sha3](./rules/security/avoid-sha3.md)                           | Use "keccak256" instead of deprecated "sha3".                            | ✔️          |
-| [avoid-suicide](./rules/security/avoid-suicide.md)                     | Use "selfdestruct" instead of deprecated "suicide".                      | ✔️          |
-| [avoid-throw](./rules/security/avoid-throw.md)                         | "throw" is deprecated, avoid to use it.                                  | ✔️          |
-| [avoid-tx-origin](./rules/security/avoid-tx-origin.md)                 | Avoid to use tx.origin.                                                  | ✔️          |
-| [check-send-result](./rules/security/check-send-result.md)             | Check result of "send" call.                                             | ✔️          |
-| [compiler-version](./rules/security/compiler-version.md)               | Compiler version must satisfy a semver requirement.                      | ✔️          |
-| [func-visibility](./rules/security/func-visibility.md)                 | Explicitly mark visibility in function.                                  | ✔️          |
-| [mark-callable-contracts](./rules/security/mark-callable-contracts.md) | Explicitly mark all external contracts as trusted or untrusted.          |             |
-| [multiple-sends](./rules/security/multiple-sends.md)                   | Avoid multiple calls of "send" method in single transaction.             | ✔️          |
-| [no-complex-fallback](./rules/security/no-complex-fallback.md)         | Fallback function must be simple.                                        | ✔️          |
-| [no-inline-assembly](./rules/security/no-inline-assembly.md)           | Avoid to use inline assembly. It is acceptable only in rare cases.       | ✔️          |
-| [not-rely-on-block-hash](./rules/security/not-rely-on-block-hash.md)   | Do not rely on "block.blockhash". Miners can influence its value.        | ✔️          |
-| [not-rely-on-time](./rules/security/not-rely-on-time.md)               | Avoid making time-based decisions in your business logic.                | ✔️          |
-| [reentrancy](./rules/security/reentrancy.md)                           | Possible reentrancy vulnerabilities. Avoid state changes after transfer. | ✔️          |
-| [state-visibility](./rules/security/state-visibility.md)               | Explicitly mark visibility of state.                                     | ✔️          |
+| Rule Id                                                                | Error                                                                    | Recommended  | Deprecated  |
+| ---------------------------------------------------------------------- | ------------------------------------------------------------------------ | ------------ | ----------- |
+| [avoid-call-value](./rules/security/avoid-call-value.md)               | Avoid to use ".call.value()()".                                          | $~~~~~~~~$✔️ |             |
+| [avoid-low-level-calls](./rules/security/avoid-low-level-calls.md)     | Avoid to use low level calls.                                            | $~~~~~~~~$✔️ |             |
+| [avoid-sha3](./rules/security/avoid-sha3.md)                           | Use "keccak256" instead of deprecated "sha3".                            | $~~~~~~~~$✔️ |             |
+| [avoid-suicide](./rules/security/avoid-suicide.md)                     | Use "selfdestruct" instead of deprecated "suicide".                      | $~~~~~~~~$✔️ |             |
+| [avoid-throw](./rules/security/avoid-throw.md)                         | "throw" is deprecated, avoid to use it.                                  | $~~~~~~~~$✔️ |             |
+| [avoid-tx-origin](./rules/security/avoid-tx-origin.md)                 | Avoid to use tx.origin.                                                  | $~~~~~~~~$✔️ |             |
+| [check-send-result](./rules/security/check-send-result.md)             | Check result of "send" call.                                             | $~~~~~~~~$✔️ |             |
+| [compiler-version](./rules/security/compiler-version.md)               | Compiler version must satisfy a semver requirement.                      | $~~~~~~~~$✔️ |             |
+| [func-visibility](./rules/security/func-visibility.md)                 | Explicitly mark visibility in function.                                  | $~~~~~~~~$✔️ |             |
+| [mark-callable-contracts](./rules/security/mark-callable-contracts.md) | Explicitly mark all external contracts as trusted or untrusted.          |              | $~~~~~~~$✔️ |
+| [multiple-sends](./rules/security/multiple-sends.md)                   | Avoid multiple calls of "send" method in single transaction.             | $~~~~~~~~$✔️ |             |
+| [no-complex-fallback](./rules/security/no-complex-fallback.md)         | Fallback function must be simple.                                        | $~~~~~~~~$✔️ |             |
+| [no-inline-assembly](./rules/security/no-inline-assembly.md)           | Avoid to use inline assembly. It is acceptable only in rare cases.       | $~~~~~~~~$✔️ |             |
+| [not-rely-on-block-hash](./rules/security/not-rely-on-block-hash.md)   | Do not rely on "block.blockhash". Miners can influence its value.        | $~~~~~~~~$✔️ |             |
+| [not-rely-on-time](./rules/security/not-rely-on-time.md)               | Avoid making time-based decisions in your business logic.                | $~~~~~~~~$✔️ |             |
+| [reentrancy](./rules/security/reentrancy.md)                           | Possible reentrancy vulnerabilities. Avoid state changes after transfer. | $~~~~~~~~$✔️ |             |
+| [state-visibility](./rules/security/state-visibility.md)               | Explicitly mark visibility of state.                                     | $~~~~~~~~$✔️ |             |
         
 
 ## References
diff --git a/docs/rules/best-practises/explicit-types.md b/docs/rules/best-practises/explicit-types.md
new file mode 100644
index 0000000..96cb06a
--- /dev/null
+++ b/docs/rules/best-practises/explicit-types.md
@@ -0,0 +1,71 @@
+---
+warning:     "This is a dynamically generated file. Do not edit manually."
+layout:      "default"
+title:       "explicit-types | Solhint"
+---
+
+# explicit-types
+![Recommended Badge](https://img.shields.io/badge/-Recommended-brightgreen)
+![Category Badge](https://img.shields.io/badge/-Best%20Practise%20Rules-informational)
+![Default Severity Badge warn](https://img.shields.io/badge/Default%20Severity-warn-yellow)
+> The {"extends": "solhint:recommended"} property in a configuration file enables this rule.
+
+
+## Description
+Forbid or enforce explicit types (like uint256) that have an alias (like uint).
+
+## Options
+This rule accepts an array of options:
+
+| Index | Description                                           | Default Value |
+| ----- | ----------------------------------------------------- | ------------- |
+| 0     | Rule severity. Must be one of "error", "warn", "off". | warn          |
+| 1     | Options need to be one of "explicit", "implicit"      | explicit      |
+
+
+### Example Config
+```json
+{
+  "rules": {
+    "explicit-types": ["warn","explicit"]
+  }
+}
+```
+
+
+## Examples
+### 👍 Examples of **correct** code for this rule
+
+#### If explicit is selected
+
+```solidity
+uint256 public variableName
+```
+
+#### If implicit is selected
+
+```solidity
+uint public variableName
+```
+
+### 👎 Examples of **incorrect** code for this rule
+
+#### If explicit is selected
+
+```solidity
+uint public variableName
+```
+
+#### If implicit is selected
+
+```solidity
+uint256 public variableName
+```
+
+## Version
+This rule is introduced in the latest version.
+
+## Resources
+- [Rule source](https://github.com/protofire/solhint/tree/master/lib/rules/best-practises/explicit-types.js)
+- [Document source](https://github.com/protofire/solhint/tree/master/docs/rules/best-practises/explicit-types.md)
+- [Test cases](https://github.com/protofire/solhint/tree/master/test/rules/best-practises/explicit-types.js)
diff --git a/docs/rules/best-practises/no-console.md b/docs/rules/best-practises/no-console.md
index 6fc4cb3..da6676e 100644
--- a/docs/rules/best-practises/no-console.md
+++ b/docs/rules/best-practises/no-console.md
@@ -14,7 +14,7 @@ title:       "no-console | Solhint"
 
 
 ## Description
-No console.log/logInt/logBytesX/logString/etc & No hardhat and forge-std console.sol import statements
+No console.log/logInt/logBytesX/logString/etc & No hardhat and forge-std console.sol import statements.
 
 ## Options
 This rule accepts a string option of rule severity. Must be one of "error", "warn", "off". Default to error.
diff --git a/docs/rules/best-practises/no-empty-blocks.md b/docs/rules/best-practises/no-empty-blocks.md
index d4f3d0b..cbc7cae 100644
--- a/docs/rules/best-practises/no-empty-blocks.md
+++ b/docs/rules/best-practises/no-empty-blocks.md
@@ -12,7 +12,7 @@ title:       "no-empty-blocks | Solhint"
 
 
 ## Description
-Code contains empty block.
+Code block has zero statements inside. Exceptions apply.
 
 ## Options
 This rule accepts a string option of rule severity. Must be one of "error", "warn", "off". Default to warn.
@@ -28,7 +28,39 @@ This rule accepts a string option of rule severity. Must be one of "error", "war
 
 
 ## Examples
-This rule does not have examples.
+### 👍 Examples of **correct** code for this rule
+
+#### Empty fallback function
+
+```solidity
+fallback () external { }
+```
+
+#### Empty constructor with member initialization list
+
+```solidity
+constructor(uint param) Foo(param) Bar(param*2) { }
+```
+
+### 👎 Examples of **incorrect** code for this rule
+
+#### Empty block on if statement
+
+```solidity
+if (condition) { }
+```
+
+#### Empty contract
+
+```solidity
+contract Foo { }
+```
+
+#### Empty block in constructor without parent initialization
+
+```solidity
+constructor () { }
+```
 
 ## Version
 This rule was introduced in [Solhint 1.1.5](https://github.com/protofire/solhint/tree/v1.1.5)
diff --git a/docs/rules/best-practises/no-global-import.md b/docs/rules/best-practises/no-global-import.md
index dd16447..1d95ccd 100644
--- a/docs/rules/best-practises/no-global-import.md
+++ b/docs/rules/best-practises/no-global-import.md
@@ -12,7 +12,7 @@ title:       "no-global-import | Solhint"
 
 
 ## Description
-Import statement includes an entire file instead of selected symbols
+Import statement includes an entire file instead of selected symbols.
 
 ## Options
 This rule accepts a string option of rule severity. Must be one of "error", "warn", "off". Default to warn.
diff --git a/docs/rules/best-practises/no-unused-import.md b/docs/rules/best-practises/no-unused-import.md
new file mode 100644
index 0000000..5643b9f
--- /dev/null
+++ b/docs/rules/best-practises/no-unused-import.md
@@ -0,0 +1,39 @@
+---
+warning:     "This is a dynamically generated file. Do not edit manually."
+layout:      "default"
+title:       "no-unused-import | Solhint"
+---
+
+# no-unused-import
+![Recommended Badge](https://img.shields.io/badge/-Recommended-brightgreen)
+![Category Badge](https://img.shields.io/badge/-Best%20Practise%20Rules-informational)
+![Default Severity Badge warn](https://img.shields.io/badge/Default%20Severity-warn-yellow)
+> The {"extends": "solhint:recommended"} property in a configuration file enables this rule.
+
+
+## Description
+Imported name is not used.
+
+## Options
+This rule accepts a string option of rule severity. Must be one of "error", "warn", "off". Default to warn.
+
+### Example Config
+```json
+{
+  "rules": {
+    "no-unused-import": "warn"
+  }
+}
+```
+
+
+## Examples
+This rule does not have examples.
+
+## Version
+This rule is introduced in the latest version.
+
+## Resources
+- [Rule source](https://github.com/protofire/solhint/tree/master/lib/rules/best-practises/no-unused-import.js)
+- [Document source](https://github.com/protofire/solhint/tree/master/docs/rules/best-practises/no-unused-import.md)
+- [Test cases](https://github.com/protofire/solhint/tree/master/test/rules/best-practises/no-unused-import.js)
diff --git a/docs/rules/miscellaneous/quotes.md b/docs/rules/miscellaneous/quotes.md
index 94cfb3d..e260f15 100644
--- a/docs/rules/miscellaneous/quotes.md
+++ b/docs/rules/miscellaneous/quotes.md
@@ -6,7 +6,7 @@ title:       "quotes | Solhint"
 
 # quotes
 ![Recommended Badge](https://img.shields.io/badge/-Recommended-brightgreen)
-![Category Badge](https://img.shields.io/badge/-Style%20Guide%20Rules-informational)
+![Category Badge](https://img.shields.io/badge/-Miscellaneous-informational)
 ![Default Severity Badge error](https://img.shields.io/badge/Default%20Severity-error-red)
 > The {"extends": "solhint:recommended"} property in a configuration file enables this rule.
 
diff --git a/docs/rules/naming/const-name-snakecase.md b/docs/rules/naming/const-name-snakecase.md
index 711bc56..fc5cb3a 100644
--- a/docs/rules/naming/const-name-snakecase.md
+++ b/docs/rules/naming/const-name-snakecase.md
@@ -12,7 +12,7 @@ title:       "const-name-snakecase | Solhint"
 
 
 ## Description
-Constant name must be in capitalized SNAKE_CASE.
+Constant name must be in capitalized SNAKE_CASE. (Does not check IMMUTABLES, use immutable-vars-naming)
 
 ## Options
 This rule accepts a string option of rule severity. Must be one of "error", "warn", "off". Default to warn.
diff --git a/docs/rules/naming/func-named-parameters.md b/docs/rules/naming/func-named-parameters.md
new file mode 100644
index 0000000..3c6710c
--- /dev/null
+++ b/docs/rules/naming/func-named-parameters.md
@@ -0,0 +1,71 @@
+---
+warning:     "This is a dynamically generated file. Do not edit manually."
+layout:      "default"
+title:       "func-named-parameters | Solhint"
+---
+
+# func-named-parameters
+![Recommended Badge](https://img.shields.io/badge/-Recommended-brightgreen)
+![Category Badge](https://img.shields.io/badge/-Style%20Guide%20Rules-informational)
+![Default Severity Badge warn](https://img.shields.io/badge/Default%20Severity-warn-yellow)
+> The {"extends": "solhint:recommended"} property in a configuration file enables this rule.
+
+
+## Description
+Enforce function calls with Named Parameters when containing more than the configured qty
+
+## Options
+This rule accepts an array of options:
+
+| Index | Description                                            | Default Value |
+| ----- | ------------------------------------------------------ | ------------- |
+| 0     | Rule severity. Must be one of "error", "warn", "off".  | warn          |
+| 1     | Maximum qty of unnamed parameters for a function call. | 2             |
+
+
+### Example Config
+```json
+{
+  "rules": {
+    "func-named-parameters": ["warn",2]
+  }
+}
+```
+
+
+## Examples
+### 👍 Examples of **correct** code for this rule
+
+#### Function call with two UNNAMED parameters (default is max 2)
+
+```solidity
+functionName('0xA81705c8C247C413a19A244938ae7f4A0393944e', 1e18)
+```
+
+#### Function call with two NAMED parameters
+
+```solidity
+functionName({ sender: '0xA81705c8C247C413a19A244938ae7f4A0393944e', amount: 1e18})
+```
+
+#### Function call with four NAMED parameters
+
+```solidity
+functionName({ sender: _senderAddress, amount: 1e18, token: _tokenAddress, receiver: _receiverAddress })
+```
+
+### 👎 Examples of **incorrect** code for this rule
+
+#### Function call with four UNNAMED parameters (default is max 2)
+
+```solidity
+functionName(_senderAddress, 1e18, _tokenAddress, _receiverAddress )
+```
+
+## Version
+This rule is introduced in the latest version.
+
+## Resources
+- [Rule source](https://github.com/protofire/solhint/tree/master/lib/rules/naming/func-named-parameters.js)
+- [Document source](https://github.com/protofire/solhint/tree/master/docs/rules/naming/func-named-parameters.md)
+- [Test cases](https://github.com/protofire/solhint/tree/master/test/rules/naming/func-named-parameters.js)
diff --git a/docs/rules/naming/func-param-name-mixedcase.md b/docs/rules/naming/func-param-name-mixedcase.md
index b1f9397..3ad1965 100644
--- a/docs/rules/naming/func-param-name-mixedcase.md
+++ b/docs/rules/naming/func-param-name-mixedcase.md
@@ -9,7 +9,7 @@ title:       "func-param-name-mixedcase | Solhint"
 ![Default Severity Badge warn](https://img.shields.io/badge/Default%20Severity-warn-yellow)
 
 ## Description
-Function param name must be in mixedCase
+Function param name must be in mixedCase.
 
 ## Options
 This rule accepts a string option of rule severity. Must be one of "error", "warn", "off". Default to warn.
diff --git a/docs/rules/naming/immutable-vars-naming.md b/docs/rules/naming/immutable-vars-naming.md
new file mode 100644
index 0000000..8705d7e
--- /dev/null
+++ b/docs/rules/naming/immutable-vars-naming.md
@@ -0,0 +1,45 @@
+---
+warning:     "This is a dynamically generated file. Do not edit manually."
+layout:      "default"
+title:       "immutable-vars-naming | Solhint"
+---
+
+# immutable-vars-naming
+![Recommended Badge](https://img.shields.io/badge/-Recommended-brightgreen)
+![Category Badge](https://img.shields.io/badge/-Style%20Guide%20Rules-informational)
+![Default Severity Badge warn](https://img.shields.io/badge/Default%20Severity-warn-yellow)
+> The {"extends": "solhint:recommended"} property in a configuration file enables this rule.
+
+
+## Description
+Check Immutable variables. Capitalized SNAKE_CASE or mixedCase depending on configuration.
+
+## Options
+This rule accepts an array of options:
+
+| Index | Description                                                                                                                              | Default Value                  |
+| ----- | ---------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------ |
+| 0     | Rule severity. Must be one of "error", "warn", "off".                                                                                    | warn                           |
+| 1     | A JSON object with a single property "immutablesAsConstants" as boolean specifying if immutable variables should be treated as constants | {"immutablesAsConstants":true} |
+
+
+### Example Config
+```json
+{
+  "rules": {
+    "immutable-vars-naming": ["warn",{"immutablesAsConstants":true}]
+  }
+}
+```
+
+
+## Examples
+This rule does not have examples.
+
+## Version
+This rule is introduced in the latest version.
+
+## Resources
+- [Rule source](https://github.com/protofire/solhint/tree/master/lib/rules/naming/immutable-vars-naming.js)
+- [Document source](https://github.com/protofire/solhint/tree/master/docs/rules/naming/immutable-vars-naming.md)
+- [Test cases](https://github.com/protofire/solhint/tree/master/test/rules/naming/immutable-vars-naming.js)
diff --git a/docs/rules/naming/named-parameters-mapping.md b/docs/rules/naming/named-parameters-mapping.md
index f23c7a8..6ae1557 100644
--- a/docs/rules/naming/named-parameters-mapping.md
+++ b/docs/rules/naming/named-parameters-mapping.md
@@ -9,7 +9,7 @@ title:       "named-parameters-mapping | Solhint"
 ![Default Severity Badge off](https://img.shields.io/badge/Default%20Severity-off-undefined)
 
 ## Description
-Solidity v0.8.18 introduced named parameters on the mappings definition
+Solidity v0.8.18 introduced named parameters on the mappings definition.
 
 ## Options
 This rule accepts a string option of rule severity. Must be one of "error", "warn", "off". Default to off.
diff --git a/docs/rules/naming/var-name-mixedcase.md b/docs/rules/naming/var-name-mixedcase.md
index 824735f..913a62c 100644
--- a/docs/rules/naming/var-name-mixedcase.md
+++ b/docs/rules/naming/var-name-mixedcase.md
@@ -12,7 +12,7 @@ title:       "var-name-mixedcase | Solhint"
 
 
 ## Description
-Variable name must be in mixedCase.
+Variable name must be in mixedCase. (Does not check IMMUTABLES, use immutable-vars-naming)
 
 ## Options
 This rule accepts a string option of rule severity. Must be one of "error", "warn", "off". Default to warn.
diff --git a/docs/rules/order/ordering.md b/docs/rules/order/ordering.md
index 421c836..5e0d009 100644
--- a/docs/rules/order/ordering.md
+++ b/docs/rules/order/ordering.md
@@ -9,7 +9,7 @@ title:       "ordering | Solhint"
 ![Default Severity Badge warn](https://img.shields.io/badge/Default%20Severity-warn-yellow)
 
 ## Description
-Check order of elements in file and inside each contract, according to the style guide
+Check order of elements in file and inside each contract, according to the style guide.
 
 ## Options
 This rule accepts a string option of rule severity. Must be one of "error", "warn", "off". Default to warn.
diff --git a/lib/common/identifier-naming.js b/lib/common/identifier-naming.js
index 2a969a8..1444d39 100644
--- a/lib/common/identifier-naming.js
+++ b/lib/common/identifier-naming.js
@@ -4,7 +4,7 @@ function match(text, regex) {
 
 module.exports = {
   isMixedCase(text) {
-    return match(text, /[_]*[a-z]+[a-zA-Z0-9$]*[_]?/)
+    return match(text, /[_]*[a-z$]+[a-zA-Z0-9$]*[_]?/)
   },
 
   isNotMixedCase(text) {
@@ -12,7 +12,7 @@ module.exports = {
   },
 
   isCamelCase(text) {
-    return match(text, /[A-Z]+[a-zA-Z0-9$]*/)
+    return match(text, /[A-Z$]+[a-zA-Z0-9$]*/)
   },
 
   isNotCamelCase(text) {
@@ -20,7 +20,7 @@ module.exports = {
   },
 
   isUpperSnakeCase(text) {
-    return match(text, /_{0,2}[A-Z0-9]+[_A-Z0-9]*/)
+    return match(text, /_{0,2}[A-Z0-9$]+[_A-Z0-9$]*/)
   },
 
   isNotUpperSnakeCase(text) {
diff --git a/lib/config.js b/lib/config.js
index 697ac89..44b4c42 100644
--- a/lib/config.js
+++ b/lib/config.js
@@ -7,7 +7,7 @@ module.exports = {
 
   getNumberByPath(path, defaultValue) {
     const configVal = _.get(this, path)
-    return _.isNumber(configVal) && configVal > 0 ? configVal : defaultValue
+    return _.isNumber(configVal) ? configVal : defaultValue
   },
 
   getBooleanByPath(path, defaultValue) {
diff --git a/lib/config/config-file.js b/lib/config/config-file.js
index 6275636..f101d8f 100644
--- a/lib/config/config-file.js
+++ b/lib/config/config-file.js
@@ -1,21 +1,20 @@
-const path = require('path')
 const fs = require('fs')
 const _ = require('lodash')
 const { cosmiconfigSync } = require('cosmiconfig')
 const { ConfigMissingError } = require('../common/errors')
 const packageJson = require('../../package.json')
 
-const getSolhintCoreConfigPath = (name) => {
+const getSolhintCoreConfig = (name) => {
   if (name === 'solhint:recommended') {
-    return path.resolve(__dirname, '../../conf/rulesets/solhint-recommended.js')
+    return require('../../conf/rulesets/solhint-recommended')
   }
 
   if (name === 'solhint:all') {
-    return path.resolve(__dirname, '../../conf/rulesets/solhint-all.js')
+    return require('../../conf/rulesets/solhint-all')
   }
 
   if (name === 'solhint:default') {
-    return path.resolve(__dirname, '../../conf/rulesets/solhint-default.js')
+    return require('../../conf/rulesets/solhint-default')
   }
 
   throw new ConfigMissingError(name)
@@ -65,20 +64,8 @@ const loadConfig = (configFile) => {
   return searchedFor.config || createEmptyConfig()
 }
 
-const configGetter = (path) => {
-  let extensionPath = null
-
-  if (path.startsWith('solhint:')) {
-    extensionPath = getSolhintCoreConfigPath(path)
-  } else {
-    // Load packages with rules
-    extensionPath = `solhint-config-${path}`
-  }
-
-  const extensionConfig = require(extensionPath)
-
-  return extensionConfig
-}
+const configGetter = (path) =>
+  path.startsWith('solhint:') ? getSolhintCoreConfig(path) : require(`solhint-config-${path}`)
 
 const applyExtends = (config, getter = configGetter) => {
   if (!config.extends) {
diff --git a/lib/formatters/README.md b/lib/formatters/README.md
index 4412432..9d6ee6c 100644
--- a/lib/formatters/README.md
+++ b/lib/formatters/README.md
@@ -2,8 +2,10 @@
 
 files in this directory are pulled from eslint repository:
 
-- table.js: eslint v5.6.0 Gajus Kuizinas <gajus@gajus.com>
-- unix.js: eslint v8.32.0 oshi-shinobu
-- tap.js: eslint v8.32.0 Jonathan Kingston
-- stylish.js: eslint v8.32.0 by Sindre Sorhus
-
+- table.js: eslint - Gajus Kuizinas
+- unix.js: eslint - oshi-shinobu
+- tap.js: eslint - Jonathan Kingston
+- stylish.js: eslint - Sindre Sorhus
+- json.js: eslint - Artur Lukianov
+- compact.js: eslint - Nicholas C. Zakas
+  
\ No newline at end of file
diff --git a/lib/formatters/compact.js b/lib/formatters/compact.js
new file mode 100644
index 0000000..56ebebe
--- /dev/null
+++ b/lib/formatters/compact.js
@@ -0,0 +1,66 @@
+/**
+ * @fileoverview Compact reporter
+ * @author Nicholas C. Zakas
+ */
+
+//------------------------------------------------------------------------------
+// Helper Functions
+//------------------------------------------------------------------------------
+
+/**
+ * Returns the severity of warning or error
+ * @param {Object} message message object to examine
+ * @returns {string} severity level
+ * @private
+ */
+function getMessageType(message) {
+  if (message.fatal || message.severity === 2) {
+    return 'Error'
+  }
+  return 'Warning'
+}
+
+//------------------------------------------------------------------------------
+// Public Interface
+//------------------------------------------------------------------------------
+
+// eslint-disable-next-line func-names
+module.exports = function (results) {
+  let output = ''
+  let total = 0
+  let errors = 0
+  let warnings = 0
+
+  results.forEach((result) => {
+    const messages = result.messages
+
+    total += messages.length
+
+    messages.forEach((message) => {
+      output += `${result.filePath}: `
+      output += `line ${message.line || 0}`
+      output += `, col ${message.column || 0}`
+      output += `, ${getMessageType(message)}`
+      output += ` - ${message.message}`
+      output += message.ruleId ? ` (${message.ruleId})` : ''
+      output += '\n'
+      if (message.severity === 2) errors += 1
+      else warnings += 1
+    })
+  })
+
+  let finalMessage = ''
+  if (errors > 0 && warnings > 0) {
+    finalMessage = `${errors + warnings} problem/s (${errors} error/s, ${warnings} warning/s)`
+  } else if (errors > 0 && warnings === 0) {
+    finalMessage = `${errors} problem/s (${errors} error/s)`
+  } else if (errors === 0 && warnings > 0) {
+    finalMessage = `${warnings} problem/s (${warnings} warning/s)`
+  }
+
+  if (total > 0) {
+    output += `\n${finalMessage}`
+  }
+
+  return output
+}
diff --git a/lib/formatters/json.js b/lib/formatters/json.js
new file mode 100644
index 0000000..2bf6f8b
--- /dev/null
+++ b/lib/formatters/json.js
@@ -0,0 +1,62 @@
+/**
+ * @fileoverview JSON Style formatter
+ * @author ArturLukianov <Original Idea and base source code> <https://github.com/ArturLukianov>
+ */
+
+//------------------------------------------------------------------------------
+// Helper Functions
+//------------------------------------------------------------------------------
+
+/**
+ * Returns a canonical error level string based upon the error message passed in.
+ * @param {Object} message Individual error message provided by eslint
+ * @returns {string} Error level string
+ */
+function getMessageType(message) {
+  if (message.fatal || message.severity === 2) {
+    return 'Error'
+  }
+  return 'Warning'
+}
+
+//------------------------------------------------------------------------------
+// Public Interface
+//------------------------------------------------------------------------------
+
+// eslint-disable-next-line func-names
+module.exports = function (results) {
+  const allMessages = []
+  let errors = 0
+  let warnings = 0
+
+  results.forEach((result) => {
+    const messages = result.messages
+
+    messages.forEach((message) => {
+      const fullObject = { ...message, filePath: result.filePath }
+      fullObject.severity = getMessageType(fullObject)
+      if (fullObject.severity === 'Error') errors += 1
+      else warnings += 1
+      allMessages.push(fullObject)
+    })
+  })
+
+  let finalMessage
+  if (errors > 0 && warnings > 0) {
+    finalMessage = {
+      conclusion: `${errors + warnings} problem/s (${errors} error/s, ${warnings} warning/s)`,
+    }
+  } else if (errors > 0 && warnings === 0) {
+    finalMessage = {
+      conclusion: `${errors} problem/s (${errors} error/s)`,
+    }
+  } else if (errors === 0 && warnings > 0) {
+    finalMessage = {
+      conclusion: `${warnings} problem/s (${warnings} warning/s)`,
+    }
+  }
+
+  if (finalMessage) allMessages.push(finalMessage)
+
+  return allMessages.length > 0 ? JSON.parse(JSON.stringify(allMessages)) : ''
+}
diff --git a/lib/formatters/stylish.js b/lib/formatters/stylish.js
index d3d323f..02bf8cb 100644
--- a/lib/formatters/stylish.js
+++ b/lib/formatters/stylish.js
@@ -1,5 +1,5 @@
 /**
- * @fileoverview Stylish reporter
+ * @fileoverview Stylish Style formatter
  * @author Sindre Sorhus
  */
 
@@ -25,7 +25,8 @@ function pluralize(word, count) {
 // Public Interface
 //------------------------------------------------------------------------------
 
-module.exports = function (results) {   // eslint-disable-line
+// eslint-disable-next-line func-names
+module.exports = function (results) {
   let output = '\n'
   let errorCount = 0
   let warningCount = 0
diff --git a/lib/formatters/table.js b/lib/formatters/table.js
index 274b8be..12bdc45 100644
--- a/lib/formatters/table.js
+++ b/lib/formatters/table.js
@@ -1,5 +1,5 @@
 /**
- * @fileoverview "table reporter.
+ * @fileoverview Table Style formatter
  * @author Gajus Kuizinas <gajus@gajus.com>
  */
 
@@ -108,7 +108,8 @@ function drawReport(results) {
 // Public Interface
 //------------------------------------------------------------------------------
 
-module.exports = function (report) {  // eslint-disable-line
+// eslint-disable-next-line func-names
+module.exports = function (report) {
   let result
   let errorCount
   let warningCount
diff --git a/lib/formatters/tap.js b/lib/formatters/tap.js
index d4933cb..67f06aa 100644
--- a/lib/formatters/tap.js
+++ b/lib/formatters/tap.js
@@ -1,5 +1,5 @@
 /**
- * @fileoverview TAP reporter
+ * @fileoverview TAP Style formatter
  * @author Jonathan Kingston
  */
 
@@ -39,7 +39,8 @@ function outputDiagnostics(diagnostic) {
 // Public Interface
 //------------------------------------------------------------------------------
 
-module.exports = function (results) {   // eslint-disable-line
+// eslint-disable-next-line func-names
+module.exports = function (results) {
   let output = `TAP version 13\n1..${results.length}\n`
 
   results.forEach((result, id) => {
diff --git a/lib/formatters/unix.js b/lib/formatters/unix.js
index 95be0f3..581f758 100644
--- a/lib/formatters/unix.js
+++ b/lib/formatters/unix.js
@@ -1,5 +1,5 @@
 /**
- * @fileoverview unix-style formatter.
+ * @fileoverview Unix Style formatter
  * @author oshi-shinobu
  */
 
@@ -23,9 +23,12 @@ function getMessageType(message) {
 // Public Interface
 //------------------------------------------------------------------------------
 
-module.exports = function (results) {   // eslint-disable-line
+// eslint-disable-next-line func-names
+module.exports = function (results) {
   let output = ''
   let total = 0
+  let errors = 0
+  let warnings = 0
 
   results.forEach((result) => {
     const messages = result.messages
@@ -39,11 +42,22 @@ module.exports = function (results) {   // eslint-disable-line
       output += ` ${message.message} `
       output += `[${getMessageType(message)}${message.ruleId ? `/${message.ruleId}` : ''}]`
       output += '\n'
+      if (message.severity === 2) errors += 1
+      else warnings += 1
     })
   })
 
+  let finalMessage = ''
+  if (errors > 0 && warnings > 0) {
+    finalMessage = `${errors + warnings} problem/s (${errors} error/s, ${warnings} warning/s)`
+  } else if (errors > 0 && warnings === 0) {
+    finalMessage = `${errors} problem/s (${errors} error/s)`
+  } else if (errors === 0 && warnings > 0) {
+    finalMessage = `${warnings} problem/s (${warnings} warning/s)`
+  }
+
   if (total > 0) {
-    output += `\n${total} problem${total !== 1 ? 's' : ''}`
+    output += `\n${finalMessage}`
   }
 
   return output
diff --git a/lib/load-rules.js b/lib/load-rules.js
index 9163ac8..c870b77 100644
--- a/lib/load-rules.js
+++ b/lib/load-rules.js
@@ -1,7 +1,7 @@
 const path = require('path')
 const { walkSync } = require('./common/utils')
 
-const blacklistedFiles = ['index.js', 'base-deprecation.js', 'load-rules.js']
+const blocklistedFiles = ['index.js', 'base-deprecation.js', 'load-rules.js']
 
 /**
  * Load all rule modules from specified directory
@@ -13,7 +13,7 @@ const loadRules = () => {
 
   const filesFiltered = files.filter((file) => {
     const filename = path.parse(file).base
-    return path.extname(filename) === '.js' && !blacklistedFiles.includes(filename)
+    return path.extname(filename) === '.js' && !blocklistedFiles.includes(filename)
   })
 
   for (const file of filesFiltered) {
@@ -40,7 +40,7 @@ const loadRule = (rule) => {
 
   const filesFiltered = files.filter((file) => {
     const filename = path.parse(file).base
-    return path.extname(filename) === '.js' && !blacklistedFiles.includes(filename)
+    return path.extname(filename) === '.js' && !blocklistedFiles.includes(filename)
   })
 
   for (const file of filesFiltered) {
diff --git a/lib/rules/best-practises/explicit-types.js b/lib/rules/best-practises/explicit-types.js
new file mode 100644
index 0000000..4f2a614
--- /dev/null
+++ b/lib/rules/best-practises/explicit-types.js
@@ -0,0 +1,128 @@
+const BaseChecker = require('../base-checker')
+const { severityDescription, formatEnum } = require('../../doc/utils')
+
+const EXPLICIT_TYPES = ['uint256', 'int256', 'ufixed128x18', 'fixed128x18']
+const IMPLICIT_TYPES = ['uint', 'int', 'ufixed', 'fixed']
+const ALL_TYPES = EXPLICIT_TYPES.concat(IMPLICIT_TYPES)
+const VALID_CONFIGURATION_OPTIONS = ['explicit', 'implicit']
+const DEFAULT_OPTION = 'explicit'
+const DEFAULT_SEVERITY = 'warn'
+
+const ruleId = 'explicit-types'
+const meta = {
+  type: 'best-practises',
+
+  docs: {
+    description: 'Forbid or enforce explicit types (like uint256) that have an alias (like uint).',
+    category: 'Best Practise Rules',
+    options: [
+      {
+        description: severityDescription,
+        default: DEFAULT_SEVERITY,
+      },
+      {
+        description: `Options need to be one of ${formatEnum(VALID_CONFIGURATION_OPTIONS)}`,
+        default: DEFAULT_OPTION,
+      },
+    ],
+    examples: {
+      good: [
+        {
+          description: 'If explicit is selected',
+          code: 'uint256 public variableName',
+        },
+        {
+          description: 'If implicit is selected',
+          code: 'uint public variableName',
+        },
+      ],
+      bad: [
+        {
+          description: 'If explicit is selected',
+          code: 'uint public variableName',
+        },
+        {
+          description: 'If implicit is selected',
+          code: 'uint256 public variableName',
+        },
+      ],
+    },
+  },
+
+  isDefault: false,
+  recommended: true,
+  defaultSetup: [DEFAULT_SEVERITY, DEFAULT_OPTION],
+
+  schema: {
+    type: 'string',
+    enum: VALID_CONFIGURATION_OPTIONS,
+  },
+}
+
+class ExplicitTypesChecker extends BaseChecker {
+  constructor(reporter, config) {
+    super(reporter, ruleId, meta)
+    this.configOption = (config && config.getString(ruleId, DEFAULT_OPTION)) || DEFAULT_OPTION
+    this.isExplicit = this.configOption === 'explicit'
+
+    this.validateConfigOption(this.configOption)
+  }
+
+  VariableDeclaration(node) {
+    const typeToFind = 'ElementaryTypeName'
+    const onlyTypes = this.findNamesOfElementaryTypeName(node, typeToFind)
+
+    // if no variables are defined, return
+    if (onlyTypes && onlyTypes.length === 0) return
+
+    // this tells if the variable needs to be checked
+    const varsToBeChecked = onlyTypes.filter((type) => ALL_TYPES.includes(type))
+
+    // if defined variables are not to be checked (example address type), return
+    if (varsToBeChecked && varsToBeChecked.length === 0) return
+
+    // if explicit, it will search for implicit and viceversa
+    if (this.isExplicit) {
+      this.validateVariables(IMPLICIT_TYPES, node, varsToBeChecked)
+    } else {
+      this.validateVariables(EXPLICIT_TYPES, node, varsToBeChecked)
+    }
+  }
+
+  validateConfigOption(configOption) {
+    const configOk = VALID_CONFIGURATION_OPTIONS.includes(configOption)
+    if (!configOk)
+      throw new Error('Error: Config error on [explicit-types]. See explicit-types.md.')
+  }
+
+  validateVariables(configType, node, varsToBeChecked) {
+    const errorVars = varsToBeChecked.filter((type) => configType.includes(type))
+
+    if (errorVars && errorVars.length > 0) {
+      for (const errorVar of errorVars) {
+        this.error(node, `Rule is set with ${this.configOption} type [var/s: ${errorVar}]`)
+      }
+    }
+  }
+
+  findNamesOfElementaryTypeName(jsonObject, typeToFind) {
+    const names = []
+
+    const searchInObject = (obj) => {
+      if (obj.type === typeToFind && obj.name) {
+        names.push(obj.name)
+      }
+
+      for (const key in obj) {
+        if (typeof obj[key] === 'object' && obj[key] !== null) {
+          searchInObject(obj[key])
+        }
+      }
+    }
+
+    searchInObject(jsonObject)
+    return names
+  }
+}
+
+module.exports = ExplicitTypesChecker
diff --git a/lib/rules/best-practises/index.js b/lib/rules/best-practises/index.js
index fc03f0f..6b2ac78 100644
--- a/lib/rules/best-practises/index.js
+++ b/lib/rules/best-practises/index.js
@@ -9,6 +9,7 @@ const ReasonStringChecker = require('./reason-string')
 const NoConsoleLogChecker = require('./no-console')
 const NoGlobalImportsChecker = require('./no-global-import')
 const NoUnusedImportsChecker = require('./no-unused-import')
+const ExplicitTypesChecker = require('./explicit-types')
 
 module.exports = function checkers(reporter, config, inputSrc) {
   return [
@@ -23,5 +24,6 @@ module.exports = function checkers(reporter, config, inputSrc) {
     new NoConsoleLogChecker(reporter),
     new NoGlobalImportsChecker(reporter),
     new NoUnusedImportsChecker(reporter),
+    new ExplicitTypesChecker(reporter, config),
   ]
 }
diff --git a/lib/rules/best-practises/max-states-count.js b/lib/rules/best-practises/max-states-count.js
index 34f7066..b2ff5cb 100644
--- a/lib/rules/best-practises/max-states-count.js
+++ b/lib/rules/best-practises/max-states-count.js
@@ -49,7 +49,8 @@ class MaxStatesCountChecker extends BaseChecker {
   constructor(reporter, config) {
     super(reporter, ruleId, meta)
 
-    this.maxStatesCount = (config && config.getNumber('max-states-count', 15)) || 15
+    this.maxStatesCount =
+      (config && config.getNumber(ruleId, DEFAULT_MAX_STATES_COUNT)) || DEFAULT_MAX_STATES_COUNT
   }
 
   ContractDefinition(node) {
diff --git a/lib/rules/best-practises/no-console.js b/lib/rules/best-practises/no-console.js
index 485c020..e6b26af 100644
--- a/lib/rules/best-practises/no-console.js
+++ b/lib/rules/best-practises/no-console.js
@@ -5,7 +5,7 @@ const meta = {
   type: 'best-practises',
   docs: {
     description:
-      'No console.log/logInt/logBytesX/logString/etc & No hardhat and forge-std console.sol import statements',
+      'No console.log/logInt/logBytesX/logString/etc & No hardhat and forge-std console.sol import statements.',
     category: 'Best Practise Rules',
     examples: {
       bad: [
diff --git a/lib/rules/best-practises/no-empty-blocks.js b/lib/rules/best-practises/no-empty-blocks.js
index f49bd93..2df7a23 100644
--- a/lib/rules/best-practises/no-empty-blocks.js
+++ b/lib/rules/best-practises/no-empty-blocks.js
@@ -1,13 +1,30 @@
 const BaseChecker = require('../base-checker')
-const { isFallbackFunction } = require('../../common/ast-types')
+const { isFallbackFunction, isReceiveFunction } = require('../../common/ast-types')
 
 const ruleId = 'no-empty-blocks'
 const meta = {
   type: 'best-practises',
 
   docs: {
-    description: 'Code contains empty block.',
+    description: 'Code block has zero statements inside. Exceptions apply.',
     category: 'Best Practise Rules',
+    examples: {
+      bad: [
+        { description: 'Empty block on if statement', code: 'if (condition) { }' },
+        { description: 'Empty contract', code: 'contract Foo { }' },
+        {
+          description: 'Empty block in constructor without parent initialization',
+          code: 'constructor () { }',
+        },
+      ],
+      good: [
+        { description: 'Empty fallback function', code: 'fallback () external { }' },
+        {
+          description: 'Empty constructor with member initialization list',
+          code: 'constructor(uint param) Foo(param) Bar(param*2) { }',
+        },
+      ],
+    },
   },
 
   isDefault: false,
@@ -33,8 +50,9 @@ class NoEmptyBlocksChecker extends BaseChecker {
     }
 
     const isFallbackFunctionBlock = isFallbackFunction(node.parent)
-    if (isFallbackFunctionBlock) {
-      // ignore empty blocks in fallback functions
+    const isReceiveFunctionBlock = isReceiveFunction(node.parent)
+    if (isFallbackFunctionBlock || isReceiveFunctionBlock) {
+      // ignore empty blocks in fallback or receive functions
       return
     }
     this._validateChildrenCount(node, 'statements')
diff --git a/lib/rules/best-practises/no-global-import.js b/lib/rules/best-practises/no-global-import.js
index 2f3e27b..a4e337a 100644
--- a/lib/rules/best-practises/no-global-import.js
+++ b/lib/rules/best-practises/no-global-import.js
@@ -5,7 +5,7 @@ const meta = {
   type: 'best-practises',
 
   docs: {
-    description: 'Import statement includes an entire file instead of selected symbols',
+    description: 'Import statement includes an entire file instead of selected symbols.',
     category: 'Best Practise Rules',
     examples: {
       bad: [
diff --git a/lib/rules/best-practises/no-unused-import.js b/lib/rules/best-practises/no-unused-import.js
index 5e80100..78ba6f7 100644
--- a/lib/rules/best-practises/no-unused-import.js
+++ b/lib/rules/best-practises/no-unused-import.js
@@ -6,7 +6,7 @@ const meta = {
   type: 'best-practises',
 
   docs: {
-    description: 'Imported name is not used',
+    description: 'Imported name is not used.',
     category: 'Best Practise Rules',
   },
 
diff --git a/lib/rules/best-practises/reason-string.js b/lib/rules/best-practises/reason-string.js
index f535b90..33b44a2 100644
--- a/lib/rules/best-practises/reason-string.js
+++ b/lib/rules/best-practises/reason-string.js
@@ -82,7 +82,12 @@ class ReasonStringChecker extends BaseChecker {
       // If has reason message and is too long, throw an error
       const reason = _.last(functionParameters).value
       if (reason.length > this.maxCharactersLong) {
-        this._errorMessageIsTooLong(node, functionName)
+        const infoMessage = reason.length
+          .toString()
+          .concat(' counted / ')
+          .concat(this.maxCharactersLong.toString())
+          .concat(' allowed')
+        this._errorMessageIsTooLong(node, functionName, infoMessage)
       }
     }
   }
@@ -103,8 +108,8 @@ class ReasonStringChecker extends BaseChecker {
     this.error(node, `Provide an error message for ${key}`)
   }
 
-  _errorMessageIsTooLong(node, key) {
-    this.error(node, `Error message for ${key} is too long`)
+  _errorMessageIsTooLong(node, key, infoMessage) {
+    this.error(node, `Error message for ${key} is too long: ${infoMessage}`)
   }
 }
 
diff --git a/lib/rules/miscellaneous/quotes.js b/lib/rules/miscellaneous/quotes.js
index b53b2ba..4212ea3 100644
--- a/lib/rules/miscellaneous/quotes.js
+++ b/lib/rules/miscellaneous/quotes.js
@@ -10,7 +10,7 @@ const meta = {
 
   docs: {
     description: `Use double quotes for string literals. Values must be 'single' or 'double'.`,
-    category: 'Style Guide Rules',
+    category: 'Miscellaneous',
     options: [
       {
         description: severityDescription,
diff --git a/lib/rules/naming/const-name-snakecase.js b/lib/rules/naming/const-name-snakecase.js
index dc10fde..9f7e961 100644
--- a/lib/rules/naming/const-name-snakecase.js
+++ b/lib/rules/naming/const-name-snakecase.js
@@ -6,7 +6,8 @@ const meta = {
   type: 'naming',
 
   docs: {
-    description: 'Constant name must be in capitalized SNAKE_CASE.',
+    description:
+      'Constant name must be in capitalized SNAKE_CASE. (Does not check IMMUTABLES, use immutable-vars-naming)',
     category: 'Style Guide Rules',
   },
 
diff --git a/lib/rules/naming/func-named-parameters.js b/lib/rules/naming/func-named-parameters.js
new file mode 100644
index 0000000..24d71d1
--- /dev/null
+++ b/lib/rules/naming/func-named-parameters.js
@@ -0,0 +1,78 @@
+const BaseChecker = require('../base-checker')
+const { severityDescription } = require('../../doc/utils')
+
+const DEFAULT_SEVERITY = 'warn'
+const DEFAULT_MAX_UNNAMED_ARGUMENTS = 2
+
+const ruleId = 'func-named-parameters'
+const meta = {
+  type: 'naming',
+
+  docs: {
+    description:
+      'Enforce function calls with Named Parameters when containing more than the configured qty',
+    category: 'Style Guide Rules',
+    options: [
+      {
+        description: severityDescription,
+        default: DEFAULT_SEVERITY,
+      },
+      {
+        description: 'Maximum qty of unnamed parameters for a function call.',
+        default: JSON.stringify(DEFAULT_MAX_UNNAMED_ARGUMENTS),
+      },
+    ],
+    examples: {
+      good: [
+        {
+          description: 'Function call with two UNNAMED parameters (default is max 2)',
+          code: "functionName('0xA81705c8C247C413a19A244938ae7f4A0393944e', 1e18)",
+        },
+        {
+          description: 'Function call with two NAMED parameters',
+          code: "functionName({ sender: '0xA81705c8C247C413a19A244938ae7f4A0393944e', amount: 1e18})",
+        },
+        {
+          description: 'Function call with four NAMED parameters',
+          code: 'functionName({ sender: _senderAddress, amount: 1e18, token: _tokenAddress, receiver: _receiverAddress })',
+        },
+      ],
+      bad: [
+        {
+          description: 'Function call with four UNNAMED parameters (default is max 2)',
+          code: 'functionName(_senderAddress, 1e18, _tokenAddress, _receiverAddress )',
+        },
+      ],
+    },
+  },
+
+  isDefault: false,
+  recommended: true,
+  defaultSetup: [DEFAULT_SEVERITY, DEFAULT_MAX_UNNAMED_ARGUMENTS],
+
+  schema: { type: 'integer' },
+}
+
+class FunctionNamedParametersChecker extends BaseChecker {
+  constructor(reporter, config) {
+    super(reporter, ruleId, meta)
+    this.maxUnnamedArguments = config && config.getNumber(ruleId, DEFAULT_MAX_UNNAMED_ARGUMENTS)
+    if (!(this.maxUnnamedArguments >= 0)) this.maxUnnamedArguments = DEFAULT_MAX_UNNAMED_ARGUMENTS
+  }
+
+  FunctionCall(node) {
+    const qtyNamed = node.names.length
+    const qtyArgs = node.arguments.length
+
+    if (qtyArgs !== 0) {
+      if (qtyNamed === 0 && qtyArgs > this.maxUnnamedArguments) {
+        this.error(
+          node,
+          `Missing named parameters. Max unnamed parameters value is ${this.maxUnnamedArguments}`
+        )
+      }
+    }
+  }
+}
+
+module.exports = FunctionNamedParametersChecker
diff --git a/lib/rules/naming/func-param-name-mixedcase.js b/lib/rules/naming/func-param-name-mixedcase.js
index 204c49b..56dae1f 100644
--- a/lib/rules/naming/func-param-name-mixedcase.js
+++ b/lib/rules/naming/func-param-name-mixedcase.js
@@ -6,7 +6,7 @@ const meta = {
   type: 'naming',
 
   docs: {
-    description: 'Function param name must be in mixedCase',
+    description: 'Function param name must be in mixedCase.',
     category: 'Style Guide Rules',
   },
 
diff --git a/lib/rules/naming/immutable-vars-naming.js b/lib/rules/naming/immutable-vars-naming.js
new file mode 100644
index 0000000..5a26d3b
--- /dev/null
+++ b/lib/rules/naming/immutable-vars-naming.js
@@ -0,0 +1,80 @@
+const BaseChecker = require('../base-checker')
+const naming = require('../../common/identifier-naming')
+const { severityDescription } = require('../../doc/utils')
+
+const DEFAULT_INMUTABLE_AS_CONSTANTS = true
+const DEFAULT_SEVERITY = 'warn'
+const DEFAULT_OPTION = { immutablesAsConstants: DEFAULT_INMUTABLE_AS_CONSTANTS }
+
+const ruleId = 'immutable-vars-naming'
+const meta = {
+  type: 'naming',
+
+  docs: {
+    description:
+      'Check Immutable variables. Capitalized SNAKE_CASE or mixedCase depending on configuration.',
+    category: 'Style Guide Rules',
+    options: [
+      {
+        description: severityDescription,
+        default: DEFAULT_SEVERITY,
+      },
+      {
+        description:
+          'A JSON object with a single property "immutablesAsConstants" as boolean specifying if immutable variables should be treated as constants',
+        default: JSON.stringify(DEFAULT_OPTION),
+      },
+    ],
+  },
+
+  isDefault: false,
+  recommended: true,
+  defaultSetup: [DEFAULT_SEVERITY, DEFAULT_OPTION],
+
+  schema: {
+    type: 'object',
+    properties: {
+      immutablesAsConstants: {
+        type: 'boolean',
+      },
+    },
+  },
+}
+
+class ImmutableVarsNamingChecker extends BaseChecker {
+  constructor(reporter, config) {
+    super(reporter, ruleId, meta)
+
+    this.treatImmutablesAsConstants =
+      config &&
+      config.getObjectPropertyBoolean(
+        ruleId,
+        'immutablesAsConstants',
+        DEFAULT_INMUTABLE_AS_CONSTANTS
+      )
+  }
+
+  VariableDeclaration(node) {
+    if (node.isImmutable) {
+      if (this.treatImmutablesAsConstants) {
+        this.validateImmutableAsConstantName(node)
+      } else {
+        this.validateImmutableAsRegularVariables(node)
+      }
+    }
+  }
+
+  validateImmutableAsConstantName(node) {
+    if (naming.isNotUpperSnakeCase(node.name)) {
+      this.error(node, 'Immutable variables name are set to be in capitalized SNAKE_CASE')
+    }
+  }
+
+  validateImmutableAsRegularVariables(node) {
+    if (naming.isNotMixedCase(node.name)) {
+      this.error(node, 'Immutable variables names are set to be in mixedCase')
+    }
+  }
+}
+
+module.exports = ImmutableVarsNamingChecker
diff --git a/lib/rules/naming/index.js b/lib/rules/naming/index.js
index ad0a9e5..1dffa3f 100644
--- a/lib/rules/naming/index.js
+++ b/lib/rules/naming/index.js
@@ -4,10 +4,12 @@ const EventNameCamelcaseChecker = require('./event-name-camelcase')
 const FuncNameMixedcaseChecker = require('./func-name-mixedcase')
 const FuncParamNameMixedcaseChecker = require('./func-param-name-mixedcase')
 const ModifierNameMixedcaseChecker = require('./modifier-name-mixedcase')
-const PrivateVarsLeadingUnderscore = require('./private-vars-leading-underscore')
+const PrivateVarsLeadingUnderscoreChecker = require('./private-vars-leading-underscore')
 const UseForbiddenNameChecker = require('./use-forbidden-name')
 const VarNameMixedcaseChecker = require('./var-name-mixedcase')
-const NamedParametersMapping = require('./named-parameters-mapping')
+const NamedParametersMappingChecker = require('./named-parameters-mapping')
+const ImmutableVarsNamingChecker = require('./immutable-vars-naming')
+const FunctionNamedParametersChecker = require('./func-named-parameters')
 
 module.exports = function checkers(reporter, config) {
   return [
@@ -17,9 +19,11 @@ module.exports = function checkers(reporter, config) {
     new FuncNameMixedcaseChecker(reporter),
     new FuncParamNameMixedcaseChecker(reporter),
     new ModifierNameMixedcaseChecker(reporter),
-    new PrivateVarsLeadingUnderscore(reporter, config),
+    new PrivateVarsLeadingUnderscoreChecker(reporter, config),
     new UseForbiddenNameChecker(reporter),
     new VarNameMixedcaseChecker(reporter),
-    new NamedParametersMapping(reporter),
+    new NamedParametersMappingChecker(reporter),
+    new ImmutableVarsNamingChecker(reporter, config),
+    new FunctionNamedParametersChecker(reporter, config),
   ]
 }
diff --git a/lib/rules/naming/named-parameters-mapping.js b/lib/rules/naming/named-parameters-mapping.js
index 099e71c..4452d0d 100644
--- a/lib/rules/naming/named-parameters-mapping.js
+++ b/lib/rules/naming/named-parameters-mapping.js
@@ -4,7 +4,7 @@ const ruleId = 'named-parameters-mapping'
 const meta = {
   type: 'naming',
   docs: {
-    description: `Solidity v0.8.18 introduced named parameters on the mappings definition`,
+    description: `Solidity v0.8.18 introduced named parameters on the mappings definition.`,
     category: 'Style Guide Rules',
     examples: {
       good: [
@@ -60,7 +60,7 @@ const meta = {
   schema: null,
 }
 
-class NamedParametersMapping extends BaseChecker {
+class NamedParametersMappingChecker extends BaseChecker {
   constructor(reporter) {
     super(reporter, ruleId, meta)
   }
@@ -109,4 +109,4 @@ class NamedParametersMapping extends BaseChecker {
   }
 }
 
-module.exports = NamedParametersMapping
+module.exports = NamedParametersMappingChecker
diff --git a/lib/rules/naming/var-name-mixedcase.js b/lib/rules/naming/var-name-mixedcase.js
index a7268da..3e9581c 100644
--- a/lib/rules/naming/var-name-mixedcase.js
+++ b/lib/rules/naming/var-name-mixedcase.js
@@ -6,7 +6,7 @@ const meta = {
   type: 'naming',
 
   docs: {
-    description: `Variable name must be in mixedCase.`,
+    description: `Variable name must be in mixedCase. (Does not check IMMUTABLES, use immutable-vars-naming)`,
     category: 'Style Guide Rules',
   },
 
@@ -23,7 +23,7 @@ class VarNameMixedcaseChecker extends BaseChecker {
   }
 
   VariableDeclaration(node) {
-    if (!node.isDeclaredConst) {
+    if (!node.isDeclaredConst && !node.isImmutable) {
       this.validateVariablesName(node)
     }
   }
diff --git a/lib/rules/order/ordering.js b/lib/rules/order/ordering.js
index 19c731f..494f3ad 100644
--- a/lib/rules/order/ordering.js
+++ b/lib/rules/order/ordering.js
@@ -6,7 +6,7 @@ const meta = {
   type: 'order',
 
   docs: {
-    description: `Check order of elements in file and inside each contract, according to the style guide`,
+    description: `Check order of elements in file and inside each contract, according to the style guide.`,
     category: 'Style Guide Rules',
     examples: {
       good: require('../../../test/fixtures/order/ordering-correct'),
diff --git a/lib/rules/security/avoid-throw.js b/lib/rules/security/avoid-throw.js
index 5e5bace..30a942a 100644
--- a/lib/rules/security/avoid-throw.js
+++ b/lib/rules/security/avoid-throw.js
@@ -27,7 +27,7 @@ class AvoidThrowChecker extends BaseChecker {
       // we don't use just `node.range` because ThrowStatement includes the semicolon and the spaces before it
       // we know that node.range[0] is the 't' of throw
       // we're also pretty sure that 'throw' has 5 letters
-      fixer.replaceTextRange([node.range[0], node.range[0] + 5], 'revert()')
+      fixer.replaceTextRange([node.range[0], node.range[0] + 5], 'revert();')
     )
   }
 }
diff --git a/package-lock.json b/package-lock.json
index 450008b..2a94ced 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -16,6 +16,8 @@
         "chalk": "^4.1.2",
         "commander": "^10.0.0",
         "cosmiconfig": "^8.0.0",
+        "cross-spawn": "^7.0.3",
+        "execa": "^4.1.0",
         "fast-diff": "^1.2.0",
         "glob": "^8.0.3",
         "ignore": "^5.2.4",
@@ -38,6 +40,7 @@
         "eslint-config-prettier": "^8.6.0",
         "eslint-plugin-import": "^2.27.4",
         "eslint-plugin-prettier": "^4.2.1",
+        "husky": "^8.0.3",
         "markdown-table": "^2.0.0",
         "mocha": "^10.2.0",
         "mocha-lcov-reporter": "^1.3.0",
@@ -1317,7 +1320,6 @@
       "version": "7.0.3",
       "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz",
       "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==",
-      "dev": true,
       "dependencies": {
         "path-key": "^3.1.0",
         "shebang-command": "^2.0.0",
@@ -1453,6 +1455,14 @@
       "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
       "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="
     },
+    "node_modules/end-of-stream": {
+      "version": "1.4.4",
+      "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz",
+      "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==",
+      "dependencies": {
+        "once": "^1.4.0"
+      }
+    },
     "node_modules/error-ex": {
       "version": "1.3.2",
       "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz",
@@ -1924,6 +1934,28 @@
         "node": ">=0.10.0"
       }
     },
+    "node_modules/execa": {
+      "version": "4.1.0",
+      "resolved": "https://registry.npmjs.org/execa/-/execa-4.1.0.tgz",
+      "integrity": "sha512-j5W0//W7f8UxAn8hXVnwG8tLwdiUy4FJLcSupCg6maBYZDpyBvTApK7KyuI4bKj8KOh1r2YH+6ucuYtJv1bTZA==",
+      "dependencies": {
+        "cross-spawn": "^7.0.0",
+        "get-stream": "^5.0.0",
+        "human-signals": "^1.1.1",
+        "is-stream": "^2.0.0",
+        "merge-stream": "^2.0.0",
+        "npm-run-path": "^4.0.0",
+        "onetime": "^5.1.0",
+        "signal-exit": "^3.0.2",
+        "strip-final-newline": "^2.0.0"
+      },
+      "engines": {
+        "node": ">=10"
+      },
+      "funding": {
+        "url": "https://github.com/sindresorhus/execa?sponsor=1"
+      }
+    },
     "node_modules/extend": {
       "version": "3.0.2",
       "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz",
@@ -2212,6 +2244,20 @@
         "node": ">=8.0.0"
       }
     },
+    "node_modules/get-stream": {
+      "version": "5.2.0",
+      "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz",
+      "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==",
+      "dependencies": {
+        "pump": "^3.0.0"
+      },
+      "engines": {
+        "node": ">=8"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
     "node_modules/get-symbol-description": {
       "version": "1.0.0",
       "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.0.0.tgz",
@@ -2498,6 +2544,21 @@
         "npm": ">=1.3.7"
       }
     },
+    "node_modules/husky": {
+      "version": "8.0.3",
+      "resolved": "https://registry.npmjs.org/husky/-/husky-8.0.3.tgz",
+      "integrity": "sha512-+dQSyqPh4x1hlO1swXBiNb2HzTDN1I2IGLQx1GrBuiqFJfoMrnZWwVmatvSiO+Iz8fBUnf+lekwNo4c2LlXItg==",
+      "dev": true,
+      "bin": {
+        "husky": "lib/bin.js"
+      },
+      "engines": {
+        "node": ">=14"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/typicode"
+      }
+    },
     "node_modules/ignore": {
       "version": "5.2.4",
       "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.4.tgz",
@@ -2836,7 +2897,6 @@
       "version": "2.0.1",
       "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz",
       "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==",
-      "dev": true,
       "engines": {
         "node": ">=8"
       },
@@ -2935,8 +2995,7 @@
     "node_modules/isexe": {
       "version": "2.0.0",
       "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
-      "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==",
-      "dev": true
+      "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw=="
     },
     "node_modules/isstream": {
       "version": "0.1.2",
@@ -3281,6 +3340,11 @@
         "url": "https://github.com/sponsors/wooorm"
       }
     },
+    "node_modules/merge-stream": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz",
+      "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w=="
+    },
     "node_modules/mime-db": {
       "version": "1.52.0",
       "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
@@ -3302,6 +3366,14 @@
         "node": ">= 0.6"
       }
     },
+    "node_modules/mimic-fn": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz",
+      "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==",
+      "engines": {
+        "node": ">=6"
+      }
+    },
     "node_modules/minimatch": {
       "version": "3.1.2",
       "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
@@ -3497,6 +3569,17 @@
         "node": ">=0.10.0"
       }
     },
+    "node_modules/npm-run-path": {
+      "version": "4.0.1",
+      "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz",
+      "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==",
+      "dependencies": {
+        "path-key": "^3.0.0"
+      },
+      "engines": {
+        "node": ">=8"
+      }
+    },
     "node_modules/nyc": {
       "version": "15.1.0",
       "resolved": "https://registry.npmjs.org/nyc/-/nyc-15.1.0.tgz",
@@ -3785,6 +3868,20 @@
         "wrappy": "1"
       }
     },
+    "node_modules/onetime": {
+      "version": "5.1.2",
+      "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz",
+      "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==",
+      "dependencies": {
+        "mimic-fn": "^2.1.0"
+      },
+      "engines": {
+        "node": ">=6"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
     "node_modules/optionator": {
       "version": "0.9.1",
       "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz",
@@ -3918,7 +4015,6 @@
       "version": "3.1.1",
       "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz",
       "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==",
-      "dev": true,
       "engines": {
         "node": ">=8"
       }
@@ -4087,6 +4183,15 @@
       "integrity": "sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag==",
       "dev": true
     },
+    "node_modules/pump": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz",
+      "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==",
+      "dependencies": {
+        "end-of-stream": "^1.1.0",
+        "once": "^1.3.1"
+      }
+    },
     "node_modules/punycode": {
       "version": "2.2.0",
       "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.2.0.tgz",
@@ -4454,7 +4559,6 @@
       "version": "2.0.0",
       "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz",
       "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==",
-      "dev": true,
       "dependencies": {
         "shebang-regex": "^3.0.0"
       },
@@ -4466,7 +4570,6 @@
       "version": "3.0.0",
       "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz",
       "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==",
-      "dev": true,
       "engines": {
         "node": ">=8"
       }
@@ -4525,8 +4628,7 @@
     "node_modules/signal-exit": {
       "version": "3.0.7",
       "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz",
-      "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==",
-      "dev": true
+      "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ=="
     },
     "node_modules/slice-ansi": {
       "version": "4.0.0",
@@ -4662,6 +4764,14 @@
         "node": ">=8"
       }
     },
+    "node_modules/strip-final-newline": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz",
+      "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==",
+      "engines": {
+        "node": ">=6"
+      }
+    },
     "node_modules/strip-json-comments": {
       "version": "3.1.1",
       "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz",
@@ -4992,7 +5102,6 @@
       "version": "2.0.2",
       "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
       "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==",
-      "dev": true,
       "dependencies": {
         "isexe": "^2.0.0"
       },
@@ -6153,7 +6262,6 @@
       "version": "7.0.3",
       "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz",
       "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==",
-      "dev": true,
       "requires": {
         "path-key": "^3.1.0",
         "shebang-command": "^2.0.0",
@@ -6251,6 +6359,14 @@
       "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
       "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="
     },
+    "end-of-stream": {
+      "version": "1.4.4",
+      "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz",
+      "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==",
+      "requires": {
+        "once": "^1.4.0"
+      }
+    },
     "error-ex": {
       "version": "1.3.2",
       "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz",
@@ -6609,6 +6725,22 @@
       "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==",
       "dev": true
     },
+    "execa": {
+      "version": "4.1.0",
+      "resolved": "https://registry.npmjs.org/execa/-/execa-4.1.0.tgz",
+      "integrity": "sha512-j5W0//W7f8UxAn8hXVnwG8tLwdiUy4FJLcSupCg6maBYZDpyBvTApK7KyuI4bKj8KOh1r2YH+6ucuYtJv1bTZA==",
+      "requires": {
+        "cross-spawn": "^7.0.0",
+        "get-stream": "^5.0.0",
+        "human-signals": "^1.1.1",
+        "is-stream": "^2.0.0",
+        "merge-stream": "^2.0.0",
+        "npm-run-path": "^4.0.0",
+        "onetime": "^5.1.0",
+        "signal-exit": "^3.0.2",
+        "strip-final-newline": "^2.0.0"
+      }
+    },
     "extend": {
       "version": "3.0.2",
       "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz",
@@ -6819,6 +6951,14 @@
       "integrity": "sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==",
       "dev": true
     },
+    "get-stream": {
+      "version": "5.2.0",
+      "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz",
+      "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==",
+      "requires": {
+        "pump": "^3.0.0"
+      }
+    },
     "get-symbol-description": {
       "version": "1.0.0",
       "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.0.0.tgz",
@@ -7023,6 +7163,12 @@
         "sshpk": "^1.7.0"
       }
     },
+    "husky": {
+      "version": "8.0.3",
+      "resolved": "https://registry.npmjs.org/husky/-/husky-8.0.3.tgz",
+      "integrity": "sha512-+dQSyqPh4x1hlO1swXBiNb2HzTDN1I2IGLQx1GrBuiqFJfoMrnZWwVmatvSiO+Iz8fBUnf+lekwNo4c2LlXItg==",
+      "dev": true
+    },
     "ignore": {
       "version": "5.2.4",
       "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.4.tgz",
@@ -7252,8 +7398,7 @@
     "is-stream": {
       "version": "2.0.1",
       "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz",
-      "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==",
-      "dev": true
+      "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg=="
     },
     "is-string": {
       "version": "1.0.7",
@@ -7316,8 +7461,7 @@
     "isexe": {
       "version": "2.0.0",
       "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
-      "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==",
-      "dev": true
+      "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw=="
     },
     "isstream": {
       "version": "0.1.2",
@@ -7586,6 +7730,11 @@
         "repeat-string": "^1.0.0"
       }
     },
+    "merge-stream": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz",
+      "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w=="
+    },
     "mime-db": {
       "version": "1.52.0",
       "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
@@ -7601,6 +7750,11 @@
         "mime-db": "1.52.0"
       }
     },
+    "mimic-fn": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz",
+      "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg=="
+    },
     "minimatch": {
       "version": "3.1.2",
       "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
@@ -7752,6 +7906,14 @@
       "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==",
       "dev": true
     },
+    "npm-run-path": {
+      "version": "4.0.1",
+      "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz",
+      "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==",
+      "requires": {
+        "path-key": "^3.0.0"
+      }
+    },
     "nyc": {
       "version": "15.1.0",
       "resolved": "https://registry.npmjs.org/nyc/-/nyc-15.1.0.tgz",
@@ -7973,6 +8135,14 @@
         "wrappy": "1"
       }
     },
+    "onetime": {
+      "version": "5.1.2",
+      "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz",
+      "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==",
+      "requires": {
+        "mimic-fn": "^2.1.0"
+      }
+    },
     "optionator": {
       "version": "0.9.1",
       "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz",
@@ -8066,8 +8236,7 @@
     "path-key": {
       "version": "3.1.1",
       "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz",
-      "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==",
-      "dev": true
+      "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q=="
     },
     "path-parse": {
       "version": "1.0.7",
@@ -8187,6 +8356,15 @@
       "integrity": "sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag==",
       "dev": true
     },
+    "pump": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz",
+      "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==",
+      "requires": {
+        "end-of-stream": "^1.1.0",
+        "once": "^1.3.1"
+      }
+    },
     "punycode": {
       "version": "2.2.0",
       "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.2.0.tgz",
@@ -8437,7 +8615,6 @@
       "version": "2.0.0",
       "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz",
       "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==",
-      "dev": true,
       "requires": {
         "shebang-regex": "^3.0.0"
       }
@@ -8445,8 +8622,7 @@
     "shebang-regex": {
       "version": "3.0.0",
       "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz",
-      "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==",
-      "dev": true
+      "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A=="
     },
     "shelljs": {
       "version": "0.8.5",
@@ -8489,8 +8665,7 @@
     "signal-exit": {
       "version": "3.0.7",
       "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz",
-      "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==",
-      "dev": true
+      "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ=="
     },
     "slice-ansi": {
       "version": "4.0.0",
@@ -8591,6 +8766,11 @@
       "integrity": "sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==",
       "dev": true
     },
+    "strip-final-newline": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz",
+      "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA=="
+    },
     "strip-json-comments": {
       "version": "3.1.1",
       "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz",
@@ -8841,7 +9021,6 @@
       "version": "2.0.2",
       "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
       "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==",
-      "dev": true,
       "requires": {
         "isexe": "^2.0.0"
       }
diff --git a/package.json b/package.json
index 4865ef1..24ac1cf 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
 {
   "name": "solhint",
-  "version": "3.4.1",
+  "version": "3.4.2",
   "description": "Solidity Code Linter",
   "main": "lib/index.js",
   "keywords": [
@@ -20,7 +20,10 @@
     "test:coverage": "npm run exec-tests",
     "test": "mocha --recursive",
     "lint": "eslint .",
-    "docs": "node scripts/generate-rule-docs.js"
+    "generate-rulesets": "node scripts/generate-rulesets.js && prettier --write conf/rulesets",
+    "docs": "node scripts/generate-rule-docs.js",
+    "prepare": "husky install",
+    "prepublishOnly": "npm run lint && npm run test && npm run generate-rulesets"
   },
   "bin": {
     "solhint": "solhint.js"
@@ -60,6 +63,7 @@
     "eslint-config-prettier": "^8.6.0",
     "eslint-plugin-import": "^2.27.4",
     "eslint-plugin-prettier": "^4.2.1",
+    "husky": "^8.0.3",
     "markdown-table": "^2.0.0",
     "mocha": "^10.2.0",
     "mocha-lcov-reporter": "^1.3.0",
diff --git a/scripts/generate-changed.sh b/scripts/generate-changed.sh
new file mode 100644
index 0000000..a5b92f5
--- /dev/null
+++ b/scripts/generate-changed.sh
@@ -0,0 +1,11 @@
+#!/bin/sh
+
+changed() {
+  git diff-index --name-only -B -R -M -C HEAD lib/rules
+  git ls-files -t -o -m lib/rules
+}
+
+if [ "$(changed)" != "" ]; then
+  npm run generate-rulesets
+  npm run docs
+fi
diff --git a/scripts/generate-rule-docs.js b/scripts/generate-rule-docs.js
index beb2310..1e7a02e 100755
--- a/scripts/generate-rule-docs.js
+++ b/scripts/generate-rule-docs.js
@@ -218,8 +218,8 @@ function getDefaultSeverity(rule) {
 
 function generateRuleIndex(rulesIndexed) {
     const contents = Object.keys(rulesIndexed).map(category => {
-        const rows = [["Rule Id", "Error", "Recommended"]];
-        rulesIndexed[category].map(rule => [`[${rule.ruleId}](./rules/${rule.meta.type}/${rule.ruleId}.md)`, rule.meta.docs.description, (rule.meta.recommended && !rule.meta.deprecated) ? '✔️' : '']).forEach(row => rows.push(row));
+        const rows = [["Rule Id", "Error", "Recommended", "Deprecated"]];
+        rulesIndexed[category].map(rule => [`[${rule.ruleId}](./rules/${rule.meta.type}/${rule.ruleId}.md)`, rule.meta.docs.description, (rule.meta.recommended && !rule.meta.deprecated) ? '$~~~~~~~~$✔️' : '', rule.meta.deprecated ? '$~~~~~~~$✔️' : '']).forEach(row => rows.push(row));
         return `## ${category}
 
 ${table(rows)}
diff --git a/scripts/generate-rulesets.js b/scripts/generate-rulesets.js
new file mode 100644
index 0000000..2e624f6
--- /dev/null
+++ b/scripts/generate-rulesets.js
@@ -0,0 +1,36 @@
+const fs = require("fs")
+
+const { loadRules } = require('../lib/load-rules')
+
+const rulesConstants = loadRules()
+const allRules = {}
+const recommendedRules = {}
+const defaultRules = {}
+
+rulesConstants.forEach((rule) => {
+  if (!rule.meta.deprecated) {
+    allRules[rule.ruleId] = rule.meta.defaultSetup
+  }
+  if (!rule.meta.deprecated && rule.meta.recommended) {
+    recommendedRules[rule.ruleId] = rule.meta.defaultSetup
+  }
+  if (!rule.meta.deprecated && rule.meta.isDefault) {
+    defaultRules[rule.ruleId] = rule.meta.defaultSetup
+  }
+})
+
+function writeRuleset(ruleset, filename) {
+  const code = `/*
+* WARNING: This file is autogenerated using the scripts/generate-rulesets.js
+* script. Do not edit manually.
+*/
+
+module.exports = Object.freeze(${JSON.stringify({ rules: ruleset }, null, 2)})
+`;
+
+  fs.writeFileSync(`./conf/rulesets/solhint-${filename}.js`, code, "utf8");
+}
+
+writeRuleset(allRules, "all")
+writeRuleset(recommendedRules, "recommended")
+writeRuleset(defaultRules, "default")
diff --git a/solhint.js b/solhint.js
index 8802402..f1d6400 100644
--- a/solhint.js
+++ b/solhint.js
@@ -19,7 +19,10 @@ function init() {
   program
     .name('solhint')
     .usage('[options] <file> [...other_files]')
-    .option('-f, --formatter [name]', 'report formatter name (stylish, table, tap, unix)')
+    .option(
+      '-f, --formatter [name]',
+      'report formatter name (stylish, table, tap, unix, json, compact)'
+    )
     .option('-w, --max-warnings [maxWarningsNumber]', 'number of allowed warnings')
     .option('-c, --config [file_name]', 'file to use as your .solhint.json')
     .option('-q, --quiet', 'report errors only - default: false')
@@ -30,9 +33,11 @@ function init() {
     .action(execMainAction)
 
   program
-    .command('stdin')
-    .description('linting of source code data provided to STDIN')
+    .command('stdin', null, { noHelp: false })
     .option('--filename [file_name]', 'name of file received using STDIN')
+    .description(
+      'linting of source code data provided to STDIN\nEx: echo "contract Foo { function f1()external view returns(uint256) {} }" | solhint stdin\nEx: solhint stdin --filename "./pathToFilename" -f table'
+    )
     .action(processStdin)
 
   program
@@ -40,6 +45,11 @@ function init() {
     .description('create configuration file for solhint')
     .action(writeSampleConfigFile)
 
+  program
+    .command('list-rules', null, { noHelp: false })
+    .description('display covered rules of current .solhint.json')
+    .action(listRules)
+
   if (process.argv.length <= 2) {
     program.help()
   }
@@ -52,7 +62,6 @@ function execMainAction() {
   }
 
   let formatterFn
-
   try {
     // to check if is a valid formatter before execute linter
     formatterFn = getFormatter(program.opts().formatter)
@@ -63,9 +72,6 @@ function execMainAction() {
 
   const reportLists = program.args.filter(_.isString).map(processPath)
   const reports = _.flatten(reportLists)
-  const warningsCount = reports.reduce((acc, i) => acc + i.warningCount, 0)
-  const warningsNumberExceeded =
-    program.opts().maxWarnings >= 0 && warningsCount > program.opts().maxWarnings
 
   if (program.opts().fix) {
     for (const report of reports) {
@@ -92,36 +98,31 @@ function execMainAction() {
     })
   }
 
-  if (printReports(reports, formatterFn)) {
-    if (
-      program.opts().maxWarnings &&
-      reports &&
-      reports.length > 0 &&
-      !reports[0].errorCount &&
-      warningsNumberExceeded
-    ) {
-      console.log(
-        'Solhint found more warnings than the maximum specified (maximum: %s, found: %s)',
-        program.opts().maxWarnings,
-        warningsCount
-      )
-      process.exit(1)
-    }
-  }
+  printReports(reports, formatterFn)
 
   exitWithCode(reports)
 }
 
 function processStdin(options) {
-  const STDIN_FILE = 0
+  const STDIN_FILE = options.filename || 0
   const stdinBuffer = fs.readFileSync(STDIN_FILE)
-
   const report = processStr(stdinBuffer.toString())
   report.file = options.filename || 'stdin'
-  const formatterFn = getFormatter()
-  printReports([report], formatterFn)
 
-  exitWithCode([report])
+  let formatterFn
+  try {
+    // to check if is a valid formatter before execute linter
+    formatterFn = getFormatter(program.opts().formatter)
+  } catch (ex) {
+    console.error(ex.message)
+    process.exit(1)
+  }
+
+  const reports = [report]
+
+  printReports(reports, formatterFn)
+
+  exitWithCode(reports)
 }
 
 function writeSampleConfigFile() {
@@ -138,8 +139,6 @@ function writeSampleConfigFile() {
   } else {
     console.log('Configuration file already exists')
   }
-
-  process.exit(0)
 }
 
 const readIgnore = _.memoize(() => {
@@ -164,7 +163,6 @@ const readIgnore = _.memoize(() => {
 
 const readConfig = _.memoize(() => {
   let config = {}
-
   try {
     config = loadConfig(program.opts().config)
   } catch (e) {
@@ -189,8 +187,38 @@ function processPath(path) {
   return linter.processPath(path, readConfig())
 }
 
+function areWarningsExceeded(reports) {
+  const warningsCount = reports.reduce((acc, i) => acc + i.warningCount, 0)
+  const warningsNumberExceeded =
+    program.opts().maxWarnings >= 0 && warningsCount > program.opts().maxWarnings
+
+  return { warningsNumberExceeded, warningsCount }
+}
+
 function printReports(reports, formatter) {
-  console.log(formatter(reports))
+  const warnings = areWarningsExceeded(reports)
+  let finalMessage = ''
+  let exitWithOne = false
+  if (
+    program.opts().maxWarnings &&
+    reports &&
+    reports.length > 0 &&
+    warnings.warningsNumberExceeded
+  ) {
+    if (!reports[0].errorCount) {
+      finalMessage = `Solhint found more warnings than the maximum specified (maximum: ${
+        program.opts().maxWarnings
+      }, found: ${warnings.warningsCount})`
+      exitWithOne = true
+    } else {
+      finalMessage =
+        'Error/s found on rules! [max-warnings] param is ignored. Fixing errors enables max-warnings'
+    }
+  }
+
+  console.log(formatter(reports), finalMessage || '')
+
+  if (exitWithOne) process.exit(1)
   return reports
 }
 
@@ -206,9 +234,40 @@ function getFormatter(formatter) {
   }
 }
 
+function listRules() {
+  if (process.argv.length !== 3) {
+    console.log('Error!! no additional parameters after list-rules command')
+    process.exit(1)
+  }
+
+  const configPath = '.solhint.json'
+  if (!fs.existsSync(configPath)) {
+    console.log('Error!! Configuration does not exists')
+    process.exit(1)
+  } else {
+    const config = readConfig()
+    console.log('\nConfiguration File: \n', config)
+
+    const reportLists = linter.processPath(configPath, config)
+    const rulesObject = reportLists[0].config
+
+    console.log('\nRules: \n')
+    const orderedRules = Object.keys(rulesObject)
+      .sort()
+      .reduce((obj, key) => {
+        obj[key] = rulesObject[key]
+        return obj
+      }, {})
+
+    // eslint-disable-next-line func-names
+    Object.keys(orderedRules).forEach(function (key) {
+      console.log('- ', key, ': ', orderedRules[key])
+    })
+  }
+}
+
 function exitWithCode(reports) {
   const errorsCount = reports.reduce((acc, i) => acc + i.errorCount, 0)
-
   process.exit(errorsCount > 0 ? 1 : 0)
 }
 


Test patch:
diff --git a/.github/workflows/E2E.yml b/.github/workflows/E2E.yml
index 0a8e311..60d1e06 100644
--- a/.github/workflows/E2E.yml
+++ b/.github/workflows/E2E.yml
@@ -14,7 +14,7 @@ jobs:
     name: Test on Linux with Node ${{ matrix.node }}
     strategy:
       matrix:
-        node: [14, 16, 18]
+        node: [16, 18, 20]
     
     steps:
       - uses: actions/checkout@v3
@@ -36,15 +36,21 @@ jobs:
       
       - name: Global Installation
         run: npm i -g solhint*tgz
+
+      - name: Check solhint version
+        run: solhint --version
       
       - name: Run E2E Tests
         run: cd e2e && npm install && npm test
 
   e2e_windows:
     runs-on: windows-latest
-    name: Run linter and E2E Tests on Windows
+    name: Test on Windows    
 
     steps:
+      - name: Enable Debugging
+        run: |
+          echo "::debug::Debugging enabled"
       - uses: actions/checkout@v3
       - uses: actions/setup-node@v3
         with:
@@ -53,8 +59,23 @@ jobs:
       - name: Install dependencies
         run: npm install --include=dev
 
-      - name: Install solhint
-        run: npm i -g solhint
+      - name: Run linter
+        run: npm run lint
+
+      - name: Generate Docs
+        run: npm run docs 
+
+      - name: Pack
+        run: npm pack
+
+      - name: Global Installation
+        run: npm i -g @(Get-ChildItem -Filter *.tgz)
+
+      - name: Check solhint version
+        run: solhint --version
+
+      - name: List directory contents
+        run: dir
 
       - name: Run linter
         run: npm run lint
@@ -64,7 +85,7 @@ jobs:
 
   e2e_macos:
     runs-on: macos-latest
-    name: Run linter and E2E Tests on MacOS
+    name: Test on MacOS
 
     steps:
     - uses: actions/checkout@v3
diff --git a/.github/workflows/TESTS.yml b/.github/workflows/TESTS.yml
index 82b44da..c04448e 100644
--- a/.github/workflows/TESTS.yml
+++ b/.github/workflows/TESTS.yml
@@ -16,7 +16,7 @@ jobs:
     name: Run linter and tests on Linux
     strategy:
       matrix:
-        node-version: [14, 16, 18]
+        node-version: [16, 18, 20]
 
     steps:
     - uses: actions/checkout@v3
diff --git a/e2e/05-max-warnings/.solhint.json b/e2e/05-max-warnings/.solhint.json
new file mode 100644
index 0000000..3b8ee84
--- /dev/null
+++ b/e2e/05-max-warnings/.solhint.json
@@ -0,0 +1,3 @@
+{
+  "extends": "solhint:all"
+}
diff --git a/e2e/05-max-warnings/contracts/Foo.sol b/e2e/05-max-warnings/contracts/Foo.sol
new file mode 100644
index 0000000..659755d
--- /dev/null
+++ b/e2e/05-max-warnings/contracts/Foo.sol
@@ -0,0 +1,11 @@
+// SPDX-License-Identifier: MIT
+pragma solidity >=0.5.8;
+
+contract Foo {
+    uint256 public constant test1 = 1;
+    uint256 TEST2;
+
+    constructor() {
+
+    }
+}
diff --git a/e2e/05-max-warnings/contracts/Foo2.sol b/e2e/05-max-warnings/contracts/Foo2.sol
new file mode 100644
index 0000000..e29d2dc
--- /dev/null
+++ b/e2e/05-max-warnings/contracts/Foo2.sol
@@ -0,0 +1,15 @@
+// SPDX-License-Identifier: MIT
+pragma solidity >=0.6.0;
+
+contract Foo {
+    uint256 public constant test1 = 1;
+    uint256 TEST2;
+
+    constructor() {}
+
+    function TEST() {}
+
+    modifier ZAR_tok() {}
+
+    uint256 SOSO;
+}
diff --git a/e2e/06-formatters/.solhint.json b/e2e/06-formatters/.solhint.json
new file mode 100644
index 0000000..3b8ee84
--- /dev/null
+++ b/e2e/06-formatters/.solhint.json
@@ -0,0 +1,3 @@
+{
+  "extends": "solhint:all"
+}
diff --git a/e2e/06-formatters/contracts/Foo.sol b/e2e/06-formatters/contracts/Foo.sol
new file mode 100644
index 0000000..1151c60
--- /dev/null
+++ b/e2e/06-formatters/contracts/Foo.sol
@@ -0,0 +1,11 @@
+// SPDX-License-Identifier: MIT
+pragma solidity >=0.6.0;
+
+contract Foo {
+    uint256 public constant test1 = 1;
+    uint256 TEST2;
+
+    constructor() {
+
+    }
+}
diff --git a/e2e/06-formatters/contracts/Foo2.sol b/e2e/06-formatters/contracts/Foo2.sol
new file mode 100644
index 0000000..9fb9801
--- /dev/null
+++ b/e2e/06-formatters/contracts/Foo2.sol
@@ -0,0 +1,10 @@
+// SPDX-License-Identifier: MIT
+pragma solidity >=0.5.8;
+
+contract Foo {
+    uint256 public constant test1 = 1;
+
+    constructor() {
+
+    }
+}
diff --git a/e2e/06-formatters/contracts/Foo3.sol b/e2e/06-formatters/contracts/Foo3.sol
new file mode 100644
index 0000000..f89c691
--- /dev/null
+++ b/e2e/06-formatters/contracts/Foo3.sol
@@ -0,0 +1,11 @@
+// SPDX-License-Identifier: MIT
+pragma solidity >=0.5.8;
+
+contract Foo {
+    uint256 public constant TEST1 = 1;
+    uint256 public value;
+
+    function _goodContract() private {
+        value = TEST1;
+    }
+}
diff --git a/e2e/06-formatters/helpers/helpers.js b/e2e/06-formatters/helpers/helpers.js
new file mode 100644
index 0000000..0508991
--- /dev/null
+++ b/e2e/06-formatters/helpers/helpers.js
@@ -0,0 +1,99 @@
+const foo1Output = [
+  {
+    line: 2,
+    column: 1,
+    severity: 'Error',
+    message: 'Compiler version >=0.6.0 does not satisfy the ^0.5.8 semver requirement',
+    ruleId: 'compiler-version',
+    fix: null,
+    filePath: 'contracts/Foo.sol',
+  },
+  {
+    line: 5,
+    column: 5,
+    severity: 'Warning',
+    message: 'Constant name must be in capitalized SNAKE_CASE',
+    ruleId: 'const-name-snakecase',
+    fix: null,
+    filePath: 'contracts/Foo.sol',
+  },
+  {
+    line: 6,
+    column: 5,
+    severity: 'Warning',
+    message: 'Explicitly mark visibility of state',
+    ruleId: 'state-visibility',
+    fix: null,
+    filePath: 'contracts/Foo.sol',
+  },
+  {
+    line: 6,
+    column: 5,
+    severity: 'Warning',
+    message: "'TEST2' should start with _",
+    ruleId: 'private-vars-leading-underscore',
+    fix: null,
+    filePath: 'contracts/Foo.sol',
+  },
+  {
+    line: 6,
+    column: 5,
+    severity: 'Warning',
+    message: 'Variable name must be in mixedCase',
+    ruleId: 'var-name-mixedcase',
+    fix: null,
+    filePath: 'contracts/Foo.sol',
+  },
+  {
+    line: 8,
+    column: 5,
+    severity: 'Warning',
+    message:
+      'Explicitly mark visibility in function (Set ignoreConstructors to true if using solidity >=0.7.0)',
+    ruleId: 'func-visibility',
+    fix: null,
+    filePath: 'contracts/Foo.sol',
+  },
+  {
+    line: 8,
+    column: 19,
+    severity: 'Warning',
+    message: 'Code contains empty blocks',
+    ruleId: 'no-empty-blocks',
+    fix: null,
+    filePath: 'contracts/Foo.sol',
+  },
+]
+
+const foo2Output = [
+  {
+    line: 5,
+    column: 5,
+    severity: 'Warning',
+    message: 'Constant name must be in capitalized SNAKE_CASE',
+    ruleId: 'const-name-snakecase',
+    fix: null,
+    filePath: 'contracts/Foo2.sol',
+  },
+  {
+    line: 7,
+    column: 5,
+    severity: 'Warning',
+    message:
+      'Explicitly mark visibility in function (Set ignoreConstructors to true if using solidity >=0.7.0)',
+    ruleId: 'func-visibility',
+    fix: null,
+    filePath: 'contracts/Foo2.sol',
+  },
+  {
+    line: 7,
+    column: 19,
+    severity: 'Warning',
+    message: 'Code contains empty blocks',
+    ruleId: 'no-empty-blocks',
+    fix: null,
+    filePath: 'contracts/Foo2.sol',
+  },
+]
+
+module.exports = { foo1Output, foo2Output }
diff --git a/e2e/formatters-test.js b/e2e/formatters-test.js
new file mode 100644
index 0000000..07f60a5
--- /dev/null
+++ b/e2e/formatters-test.js
@@ -0,0 +1,348 @@
+const chai = require('chai')
+const { expect } = chai
+const fs = require('fs-extra')
+const os = require('os')
+const path = require('path')
+const shell = require('shelljs')
+
+function useFixture(dir) {
+  beforeEach(`switch to ${dir}`, function () {
+    const fixturePath = path.join(__dirname, dir)
+
+    const tmpDirContainer = os.tmpdir()
+    this.testDirPath = path.join(tmpDirContainer, `solhint-tests-${dir}`)
+
+    fs.ensureDirSync(this.testDirPath)
+    fs.emptyDirSync(this.testDirPath)
+
+    fs.copySync(fixturePath, this.testDirPath)
+
+    shell.cd(this.testDirPath)
+  })
+}
+
+describe('e2e', function () {
+  describe('formatter tests', () => {
+    // Foo contract has 1 error and 6 warnings
+    // Foo2 contract has 3 warnings
+    // Foo3 contract has no warnings and no errors
+    const { foo1Output, foo2Output } = require('./06-formatters/helpers/helpers.js')
+    const PATH = ''
+
+    useFixture('06-formatters')
+
+    it('should fail when wrong formatter is specify', () => {
+      const formatterType = 'wrongOne'
+      const { code } = shell.exec(`solhint ${PATH}contracts/Foo2.sol -f ${formatterType}`)
+      expect(code).to.equal(1)
+    })
+
+    describe('unix formatter tests', () => {
+      const formatterType = 'unix'
+
+      it('should return nothing when file does not exist and unix is the formatter', () => {
+        const { code, stdout } = shell.exec(`solhint ${PATH}contracts/Foo1.sol -f ${formatterType}`)
+        expect(code).to.equal(0)
+        expect(stdout.trim()).to.be.empty
+      })
+
+      it('should return nothing when file exists and there is no error/warning', () => {
+        const { code, stdout } = shell.exec(`solhint ${PATH}contracts/Foo3.sol -f ${formatterType}`)
+        expect(code).to.equal(0)
+        expect(stdout.trim()).to.be.empty
+      })
+      it('should make the output report with unix formatter for Foo2', () => {
+        const { code, stdout } = shell.exec(`solhint ${PATH}contracts/Foo2.sol -f ${formatterType}`)
+
+        const reportLines = stdout.split('\n')
+        let expectedLine
+
+        for (let i = 0; i < reportLines.length - 3; i++) {
+          expectedLine = `${foo2Output[i].filePath}:${foo2Output[i].line}:${foo2Output[i].column}: ${foo2Output[i].message} [${foo2Output[i].severity}/${foo2Output[i].ruleId}]`
+          expect(reportLines[i]).to.equal(expectedLine)
+        }
+        expect(code).to.equal(0)
+
+        const finalLine = '3 problem/s (3 warning/s) '
+        expect(reportLines[reportLines.length - 2]).to.equal(finalLine)
+      })
+      it('should make the output report with unix formatter for Foo and Foo2 and Foo3', () => {
+        const { code, stdout } = shell.exec(
+          `solhint ${PATH}contracts/Foo.sol ${PATH}contracts/Foo2.sol ${PATH}contracts/Foo3.sol -f ${formatterType}`
+        )
+
+        const reportLines = stdout.split('\n')
+        const joinedFoo = foo1Output.concat(foo2Output)
+        let expectedLine
+
+        for (let i = 0; i < reportLines.length - 3; i++) {
+          expectedLine = `${joinedFoo[i].filePath}:${joinedFoo[i].line}:${joinedFoo[i].column}: ${joinedFoo[i].message} [${joinedFoo[i].severity}/${joinedFoo[i].ruleId}]`
+          expect(reportLines[i]).to.equal(expectedLine)
+        }
+        // because there's an error
+        expect(code).to.equal(1)
+
+        const finalLine = '10 problem/s (1 error/s, 9 warning/s)'
+        expect(reportLines[reportLines.length - 2]).to.contain(finalLine)
+      })
+    })
+
+    describe('json formatter tests', () => {
+      const formatterType = 'json'
+
+      it('should return nothing when file does not exist and json is the formatter', () => {
+        const { code, stdout } = shell.exec(`solhint ${PATH}contracts/Foo1.sol -f ${formatterType}`)
+        expect(code).to.equal(0)
+        expect(stdout.trim()).to.be.empty
+      })
+      it('should return nothing when file exists and there is no error/warning', () => {
+        const { code, stdout } = shell.exec(`solhint ${PATH}contracts/Foo3.sol -f ${formatterType}`)
+        expect(code).to.equal(0)
+        expect(stdout.trim()).to.be.empty
+      })
+      it('should make the output report with json formatter for Foo2', () => {
+        const { code, stdout } = shell.exec(`solhint ${PATH}contracts/Foo2.sol -f ${formatterType}`)
+
+        const expectedFinalOutput = foo2Output.concat([{ conclusion: '3 problem/s (3 warning/s)' }])
+
+        // eslint-disable-next-line no-eval
+        const objectOutput = eval('(' + stdout + ')')
+        const strOutput = JSON.stringify(objectOutput)
+        const strExpected = JSON.stringify(expectedFinalOutput)
+        expect(strExpected).to.equal(strOutput)
+        expect(code).to.equal(0)
+      })
+      it('should make the output report with unix formatter for Foo and Foo2 and Foo3', () => {
+        const { code, stdout } = shell.exec(
+          `solhint ${PATH}contracts/Foo.sol ${PATH}contracts/Foo2.sol ${PATH}contracts/Foo3.sol -f ${formatterType}`
+        )
+
+        const expectedFinalOutput = foo1Output
+          .concat(foo2Output)
+          .concat([{ conclusion: '10 problem/s (1 error/s, 9 warning/s)' }])
+
+        // eslint-disable-next-line no-eval
+        const objectOutput = eval('(' + stdout + ')')
+        const strOutput = JSON.stringify(objectOutput)
+        const strExpected = JSON.stringify(expectedFinalOutput)
+        expect(strExpected).to.equal(strOutput)
+        // There's an error, that is why exit code is 1
+        expect(code).to.equal(1)
+      })
+    })
+
+    describe('compact formatter tests', () => {
+      const formatterType = 'compact'
+
+      it('should return nothing when file does not exist and compact is the formatter', () => {
+        const { code, stdout } = shell.exec(`solhint ${PATH}contracts/Foo1.sol -f ${formatterType}`)
+        expect(code).to.equal(0)
+        expect(stdout.trim()).to.be.empty
+      })
+
+      it('should return nothing when file exists and there is no error/warning', () => {
+        const { code, stdout } = shell.exec(`solhint ${PATH}contracts/Foo3.sol -f ${formatterType}`)
+        expect(code).to.equal(0)
+        expect(stdout.trim()).to.be.empty
+      })
+      it('should make the output report with compact formatter for Foo2', () => {
+        const { code, stdout } = shell.exec(`solhint ${PATH}contracts/Foo2.sol -f ${formatterType}`)
+
+        const reportLines = stdout.split('\n')
+        let expectedLine
+
+        for (let i = 0; i < reportLines.length - 3; i++) {
+          expectedLine = `${foo2Output[i].filePath}: line ${foo2Output[i].line}, col ${foo2Output[i].column}, ${foo2Output[i].severity} - ${foo2Output[i].message} (${foo2Output[i].ruleId})`
+          expect(reportLines[i]).to.equal(expectedLine)
+        }
+        expect(code).to.equal(0)
+
+        const finalLine = '3 problem/s (3 warning/s) '
+        expect(reportLines[reportLines.length - 2]).to.equal(finalLine)
+      })
+      it('should make the output report with compact formatter for Foo and Foo2 and Foo3', () => {
+        const { code, stdout } = shell.exec(
+          `solhint ${PATH}contracts/Foo.sol ${PATH}contracts/Foo2.sol ${PATH}contracts/Foo3.sol -f ${formatterType}`
+        )
+
+        const reportLines = stdout.split('\n')
+        const joinedFoo = foo1Output.concat(foo2Output)
+        let expectedLine
+
+        for (let i = 0; i < reportLines.length - 3; i++) {
+          expectedLine = `${joinedFoo[i].filePath}: line ${joinedFoo[i].line}, col ${joinedFoo[i].column}, ${joinedFoo[i].severity} - ${joinedFoo[i].message} (${joinedFoo[i].ruleId})`
+          expect(reportLines[i]).to.equal(expectedLine)
+        }
+        // because there's an error
+        expect(code).to.equal(1)
+
+        const finalLine = '10 problem/s (1 error/s, 9 warning/s)'
+        expect(reportLines[reportLines.length - 2]).to.contain(finalLine)
+      })
+    })
+
+    describe('stylish formatter tests', () => {
+      const formatterType = 'stylish'
+
+      it('should return nothing when file does not exist and stylish is the formatter', () => {
+        const { code, stdout } = shell.exec(`solhint ${PATH}contracts/Foo1.sol -f ${formatterType}`)
+        expect(code).to.equal(0)
+        expect(stdout.trim()).to.be.empty
+      })
+
+      it('should return nothing when file exists and there is no error/warning', () => {
+        const { code, stdout } = shell.exec(`solhint ${PATH}contracts/Foo3.sol -f ${formatterType}`)
+        expect(code).to.equal(0)
+        expect(stdout.trim()).to.be.empty
+      })
+      it('should make the output report with stylish formatter for Foo2', () => {
+        const { code, stdout } = shell.exec(`solhint ${PATH}contracts/Foo2.sol -f ${formatterType}`)
+
+        const reportLines = stdout.split('\n')
+        let expectedLine = foo2Output[0].filePath
+        expect(reportLines[1]).to.equal(expectedLine)
+
+        for (let i = 2; i < reportLines.length - 4; i++) {
+          expectedLine = `  ${foo2Output[i - 2].line}:${foo2Output[i - 2].column}  ${foo2Output[
+            i - 2
+          ].severity.toLowerCase()}  ${foo2Output[i - 2].message} ${foo2Output[i - 2].ruleId}`
+
+          expect(reportLines[i].replace(/\s/g, '')).to.equal(expectedLine.replace(/\s/g, ''))
+        }
+        expect(code).to.equal(0)
+
+        const finalLine = '\u2716 3 problems (0 errors, 3 warnings)'
+        expect(reportLines[reportLines.length - 3]).to.equal(finalLine)
+      })
+      it('should make the output report with stylish formatter for Foo and Foo2 and Foo3', () => {
+        const { code, stdout } = shell.exec(
+          `solhint ${PATH}contracts/Foo.sol ${PATH}contracts/Foo2.sol ${PATH}contracts/Foo3.sol -f ${formatterType}`
+        )
+
+        const reportLines = stdout.split('\n')
+
+        let expectedLine
+        expectedLine = foo1Output[0].filePath
+        expect(reportLines[1]).to.equal(expectedLine)
+
+        expectedLine = foo2Output[0].filePath
+        expect(reportLines[10]).to.equal(expectedLine)
+
+        const joinedFoo = foo1Output.concat(foo2Output)
+        reportLines.splice(9, 2)
+        reportLines.splice(1, 1)
+
+        for (let i = 1; i < reportLines.length - 4; i++) {
+          expectedLine = `  ${joinedFoo[i - 1].line}:${joinedFoo[i - 1].column}  ${joinedFoo[
+            i - 1
+          ].severity.toLowerCase()}  ${joinedFoo[i - 1].message} ${joinedFoo[i - 1].ruleId}`
+
+          expect(reportLines[i].replace(/\s/g, '')).to.equal(expectedLine.replace(/\s/g, ''))
+        }
+        expect(code).to.equal(1)
+
+        const finalLine = '\u2716 10 problems (1 error, 9 warnings)'
+        expect(reportLines[reportLines.length - 3]).to.equal(finalLine)
+      })
+    })
+
+    describe('tap formatter tests', () => {
+      const formatterType = 'tap'
+
+      it('should return TAP header when file does not exist and tap is the formatter', () => {
+        const { code, stdout } = shell.exec(`solhint ${PATH}contracts/Foo1.sol -f ${formatterType}`)
+
+        const reportLines = stdout.split('\n')
+        expect(reportLines[0]).to.eq('TAP version 13')
+        expect(reportLines[1]).to.eq('1..0')
+
+        expect(code).to.equal(0)
+      })
+
+      it('should return TAP header [ok 1] when file exists and there is no error/warning', () => {
+        const { code, stdout } = shell.exec(`solhint ${PATH}contracts/Foo3.sol -f ${formatterType}`)
+        const reportLines = stdout.split('\n')
+        expect(reportLines[0]).to.eq('TAP version 13')
+        expect(reportLines[1]).to.eq('1..1')
+        expect(reportLines[2]).to.eq(`ok 1 - ${PATH}contracts/Foo3.sol`)
+
+        expect(code).to.equal(0)
+      })
+      it('should make the output report with tap formatter for Foo2', () => {
+        const { code, stdout } = shell.exec(`solhint ${PATH}contracts/Foo2.sol -f ${formatterType}`)
+
+        const reportLines = stdout.split('\n')
+        expect(reportLines[0]).to.eq('TAP version 13')
+        expect(reportLines[1]).to.eq('1..1')
+        expect(reportLines[2]).to.eq(`ok 1 - ${foo2Output[0].filePath}`)
+        expect(reportLines[4]).to.eq(`  message: ${foo2Output[0].message}`)
+        expect(reportLines[5]).to.eq(`  severity: ${foo2Output[0].severity.toLowerCase()}`)
+        expect(reportLines[6]).to.eq(`  data:`)
+        expect(reportLines[7]).to.eq(`    line: ${foo2Output[0].line}`)
+        expect(reportLines[8]).to.eq(`    column: ${foo2Output[0].column}`)
+        expect(reportLines[9]).to.eq(`    ruleId: ${foo2Output[0].ruleId}`)
+
+        expect(code).to.equal(0)
+      })
+    })
+
+    describe('table formatter tests', () => {
+      const formatterType = 'table'
+      const tableFooter1 =
+        '╔════════════════════════════════════════════════════════════════════════════════════════════════════════════════╗'
+      let tableFooter2 = `║ 0 Errors                                                                                                       ║`
+      const tableFooter3 =
+        '╟────────────────────────────────────────────────────────────────────────────────────────────────────────────────╢'
+      let tableFooter4 = `║ 0 Warnings                                                                                                     ║`
+      const tableFooter5 =
+        '╚════════════════════════════════════════════════════════════════════════════════════════════════════════════════╝'
+      const tableHeader1 =
+        '║ Line     │ Column   │ Type     │ Message                                                │ Rule ID              ║'
+      const tableHeader2 =
+        '╟──────────┼──────────┼──────────┼────────────────────────────────────────────────────────┼──────────────────────╢'
+
+      it('should return TABLE Footer when file does not exist and table is the formatter', () => {
+        const { code, stdout } = shell.exec(`solhint ${PATH}contracts/Foo1.sol -f ${formatterType}`)
+        const reportLines = stdout.split('\n')
+        expect(reportLines[1]).to.eq(tableFooter1)
+        expect(reportLines[2]).to.eq(tableFooter2)
+        expect(reportLines[3]).to.eq(tableFooter3)
+        expect(reportLines[4]).to.eq(tableFooter4)
+        expect(reportLines[5]).to.eq(tableFooter5)
+
+        expect(code).to.equal(0)
+      })
+
+      it('should return TABLE Footer when file exists and there is no error/warning', () => {
+        const { code, stdout } = shell.exec(`solhint ${PATH}contracts/Foo3.sol -f ${formatterType}`)
+        const reportLines = stdout.split('\n')
+        expect(reportLines[1]).to.eq(tableFooter1)
+        expect(reportLines[2]).to.eq(tableFooter2)
+        expect(reportLines[3]).to.eq(tableFooter3)
+        expect(reportLines[4]).to.eq(tableFooter4)
+        expect(reportLines[5]).to.eq(tableFooter5)
+
+        expect(code).to.equal(0)
+      })
+      it('should make the output report with table formatter for Foo', () => {
+        const { code, stdout } = shell.exec(`solhint ${PATH}contracts/Foo.sol -f ${formatterType}`)
+        const reportLines = stdout.split('\n')
+
+        expect(reportLines[1]).to.eq(foo1Output[0].filePath)
+
+        tableFooter2 = tableFooter2.replace('0 Errors', '1 Error ')
+        tableFooter4 = tableFooter4.replace('0', '6')
+        expect(reportLines[17]).to.eq(tableFooter1)
+        expect(reportLines[18]).to.eq(tableFooter2)
+        expect(reportLines[19]).to.eq(tableFooter3)
+        expect(reportLines[20]).to.eq(tableFooter4)
+        expect(reportLines[21]).to.eq(tableFooter5)
+
+        expect(reportLines[3]).to.eq(tableHeader1)
+        expect(reportLines[4]).to.eq(tableHeader2)
+
+        expect(code).to.equal(1)
+      })
+    })
+  })
+})
diff --git a/e2e/package.json b/e2e/package.json
index 8222399..3f81707 100644
--- a/e2e/package.json
+++ b/e2e/package.json
@@ -4,12 +4,13 @@
   "description": "E2E tests for solhint",
   "main": "index.js",
   "scripts": {
-    "test": "mocha test.js"
+    "test": "mocha test.js formatters-test.js"    
   },
   "author": "",
   "license": "MIT",
   "devDependencies": {
     "chai": "^4.3.7",
+    "chai-spies": "^1.0.0",
     "fs-extra": "^11.1.0",
     "get-stream": "^6.0.0",
     "mocha": "^10.2.0",
diff --git a/e2e/test.js b/e2e/test.js
index b4b9399..bf3910f 100644
--- a/e2e/test.js
+++ b/e2e/test.js
@@ -1,7 +1,3 @@
-/* eslint-disable func-names */
-/* eslint-disable prefer-arrow-callback */
-/* eslint-disable no-unused-expressions */
-
 const { expect } = require('chai')
 const cp = require('child_process')
 const fs = require('fs-extra')
@@ -11,7 +7,7 @@ const path = require('path')
 const shell = require('shelljs')
 
 function useFixture(dir) {
-  beforeEach(`switch to ${dir}`, function() {
+  beforeEach(`switch to ${dir}`, function () {
     const fixturePath = path.join(__dirname, dir)
 
     const tmpDirContainer = os.tmpdir()
@@ -26,17 +22,17 @@ function useFixture(dir) {
   })
 }
 
-describe('e2e', function() {
-  describe('no config', function() {
+describe('e2e', function () {
+  describe('no config', function () {
     useFixture('01-no-config')
 
-    it('should fail', function() {
+    it('should fail', function () {
       const { code } = shell.exec('solhint Foo.sol')
 
       expect(code).to.equal(1)
     })
 
-    it('should create an initial config with --init', function() {
+    it('should create an initial config with --init', function () {
       const { code } = shell.exec('solhint --init')
 
       expect(code).to.equal(0)
@@ -46,55 +42,51 @@ describe('e2e', function() {
       expect(fs.existsSync(solhintConfigPath)).to.be.true
     })
 
-    it('should print usage if called without arguments', function() {
+    it('should print usage if called without arguments', function () {
       const { code, stdout } = shell.exec('solhint')
 
       expect(code).to.equal(0)
-
       expect(stdout).to.include('Usage: solhint [options]')
       expect(stdout).to.include('Linter for Solidity programming language')
       expect(stdout).to.include('linting of source code data provided to STDIN')
     })
   })
 
-  describe('empty-config', function() {
+  describe('empty-config', function () {
     useFixture('02-empty-solhint-json')
 
-    it('should print nothing', function() {
+    it('should print nothing', function () {
       const { code, stdout } = shell.exec('solhint Foo.sol')
 
       expect(code).to.equal(0)
-
       expect(stdout.trim()).to.equal('')
     })
 
-    it('should show warning when using --init', function() {
+    it('should show warning when using --init', function () {
       const { code, stdout } = shell.exec('solhint --init')
 
       expect(code).to.equal(0)
-
       expect(stdout.trim()).to.equal('Configuration file already exists')
     })
   })
 
-  describe('no-empty-blocks', function() {
+  describe('no-empty-blocks', function () {
     useFixture('03-no-empty-blocks')
 
-    it('should exit with 1', function() {
+    it('should exit with 1', function () {
       const { code, stdout } = shell.exec('solhint Foo.sol')
 
       expect(code).to.equal(1)
-
       expect(stdout.trim()).to.contain('Code contains empty blocks')
     })
 
-    it('should work with stdin', async function() {
+    it('should work with stdin', async function () {
       const child = cp.exec('solhint stdin')
 
       const stdoutPromise = getStream(child.stdout)
 
-      const codePromise = new Promise(resolve => {
-        child.on('close', code => {
+      const codePromise = new Promise((resolve) => {
+        child.on('close', (code) => {
           resolve(code)
         })
       })
@@ -112,13 +104,47 @@ describe('e2e', function() {
     })
   })
 
-  describe('.sol on path', function() {
+  describe('.sol on path', function () {
     useFixture('04-dotSol-on-path')
 
-    it('should handle directory names that end with .sol', function() {
+    it('should handle directory names that end with .sol', function () {
       const { code } = shell.exec('solhint contracts/**/*.sol')
+      expect(code).to.equal(0)
+    })
+  })
 
+  describe('--max-warnings parameter tests', function () {
+    // Foo contract has 6 warnings
+    // Foo2 contract has 1 error and 14 warnings
+    useFixture('05-max-warnings')
+    const warningExceededMsg = 'Solhint found more warnings than the maximum specified'
+    const errorFound =
+      'Error/s found on rules! [max-warnings] param is ignored. Fixing errors enables max-warnings'
+
+    it('should not display [warnings exceeded] for max 7 warnings', function () {
+      const { code, stdout } = shell.exec('solhint contracts/Foo.sol --max-warnings 7')
       expect(code).to.equal(0)
+      expect(stdout.trim()).to.not.contain(warningExceededMsg)
+    })
+
+    it('should display [warnings exceeded] for max 3 warnings and exit error 1', function () {
+      const { code, stdout } = shell.exec('solhint contracts/Foo.sol --max-warnings 3')
+
+      expect(code).to.equal(1)
+      expect(stdout.trim()).to.contain(warningExceededMsg)
+    })
+
+    it('should return error for Compiler version rule, ignoring 3 --max-warnings', function () {
+      const { code, stdout } = shell.exec('solhint contracts/Foo2.sol --max-warnings 3')
+
+      expect(code).to.equal(1)
+      expect(stdout.trim()).to.contain(errorFound)
+    })
+
+    it('should return error for Compiler version rule. No message for max-warnings', function () {
+      const { code, stdout } = shell.exec('solhint contracts/Foo2.sol --max-warnings 27')
+      expect(code).to.equal(1)
+      expect(stdout.trim()).to.not.contain(errorFound)
     })
   })
 })
diff --git a/test/fixtures/best-practises/explicit-types.js b/test/fixtures/best-practises/explicit-types.js
new file mode 100644
index 0000000..b63c483
--- /dev/null
+++ b/test/fixtures/best-practises/explicit-types.js
@@ -0,0 +1,135 @@
+const VAR_DECLARATIONS = {
+  uint256: {
+    code: 'uint256 public varUint256;',
+    errorsImplicit: 1,
+    errorsExplicit: 0,
+  },
+
+  uint: {
+    code: 'uint public varUint;',
+    errorsImplicit: 0,
+    errorsExplicit: 1,
+  },
+
+  int256: {
+    code: 'int256 public varInt256;',
+    errorsImplicit: 1,
+    errorsExplicit: 0,
+  },
+
+  int: {
+    code: 'int public varInt;',
+    errorsImplicit: 0,
+    errorsExplicit: 1,
+  },
+
+  ufixed128x18: {
+    code: 'ufixed128x18 public varUfixed128x18;',
+    errorsImplicit: 1,
+    errorsExplicit: 0,
+  },
+
+  ufixed: {
+    code: 'ufixed public varUfixed;',
+    errorsImplicit: 0,
+    errorsExplicit: 1,
+  },
+
+  fixed128x18: {
+    code: 'fixed128x18 public varFixed128x18;',
+    errorsImplicit: 1,
+    errorsExplicit: 0,
+  },
+
+  fixed: {
+    code: 'fixed public varFixed;',
+    errorsImplicit: 0,
+    errorsExplicit: 1,
+  },
+
+  functionParameterAndReturns1: {
+    code: 'function withUint256(uint256 varUint256, uint varUint) public returns(int256 returnInt256) {}',
+    errorsImplicit: 2,
+    errorsExplicit: 1,
+  },
+
+  functionParameterAndReturns2: {
+    code: 'function withUint256(uint varUint, uint varUint) public returns(int returnInt) {}',
+    errorsImplicit: 0,
+    errorsExplicit: 3,
+  },
+
+  eventParameter: {
+    code: 'event EventWithInt256(int256 varWithInt256, address addressVar, bool boolVat, int varWithInt);',
+    errorsImplicit: 1,
+    errorsExplicit: 1,
+  },
+
+  regularMap1: {
+    code: 'mapping(uint256 => fixed128x18) public mapWithFixed128x18;',
+    errorsImplicit: 2,
+    errorsExplicit: 0,
+  },
+
+  regularMap2: {
+    code: 'mapping(uint => fixed128x18) public mapWithFixed128x18;',
+    errorsImplicit: 1,
+    errorsExplicit: 1,
+  },
+
+  mapOfMapping: {
+    code: 'mapping(uint => mapping(fixed128x18 => int256)) mapOfMap;',
+    errorsImplicit: 2,
+    errorsExplicit: 1,
+  },
+
+  struct: {
+    code: 'struct Estructura { ufixed128x18 varWithUfixed128x18; uint varUint; }',
+    errorsImplicit: 1,
+    errorsExplicit: 1,
+  },
+
+  mapOfStruct: {
+    code: 'struct Estructura { ufixed128x18 varWithUfixed128x18; uint varUint; mapping(uint256 => Estructura) mapOfStruct; }',
+    errorsImplicit: 2,
+    errorsExplicit: 1,
+  },
+
+  structWithMap: {
+    code: 'struct Estructura { ufixed varWithUfixed; uint varUint; mapping(address => uint256) structWithMap; } ',
+    errorsImplicit: 1,
+    errorsExplicit: 2,
+  },
+
+  mapOfArrayStructWithMap: {
+    code: 'struct Estructura { ufixed varWithUfixed; uint varUint; mapping(address => uint256) structWithMap; } mapping(uint256 => Estructura[]) mapOfArrayStructWithMap;',
+    errorsImplicit: 2,
+    errorsExplicit: 2,
+  },
+
+  regularArray: {
+    code: 'uint256[] public arr;',
+    errorsImplicit: 1,
+    errorsExplicit: 0,
+  },
+
+  fixedArray: {
+    code: 'uint[] public arr = [1,2,3];',
+    errorsImplicit: 0,
+    errorsExplicit: 1,
+  },
+
+  fixedArrayOfArrays: {
+    code: 'uint256[] public arr = [[1,2,3]];',
+    errorsImplicit: 1,
+    errorsExplicit: 0,
+  },
+
+  mapOfFixedArray: {
+    code: 'uint[] public arr = [1,2,3]; mapping(uint256 => arr) mapOfFixedArray;',
+    errorsImplicit: 1,
+    errorsExplicit: 1,
+  },
+}
+
+module.exports = VAR_DECLARATIONS
diff --git a/test/fixtures/naming/func-named-parameters.js b/test/fixtures/naming/func-named-parameters.js
new file mode 100644
index 0000000..d4ceb7a
--- /dev/null
+++ b/test/fixtures/naming/func-named-parameters.js
@@ -0,0 +1,54 @@
+const FUNCTION_CALLS_ERRORS = {
+  maxUnnamed_0_1u: {
+    code: 'funcName(_sender);',
+    maxUnnamed: 0,
+  },
+
+  maxUnnamed_1_3u: {
+    code: 'funcName(_sender, amount, receiver);',
+    maxUnnamed: 1,
+  },
+
+  maxUnnamed_2_3u: {
+    code: 'funcName(_sender, amount, receiver);',
+    maxUnnamed: 2,
+  },
+
+  maxUnnamed_3_4u: {
+    code: 'funcName(_sender, amount, receiver, token);',
+    maxUnnamed: 3,
+  },
+}
+
+const FUNCTION_CALLS_OK = {
+  maxUnnamed_0_0u: {
+    code: 'funcName();',
+    maxUnnamed: 0,
+  },
+
+  maxUnnamed_0_0uB: {
+    code: 'funcName();',
+    maxUnnamed: 10,
+  },
+
+  maxUnnamed_1_0u: {
+    code: 'funcName({ sender: _sender, amount: _amount, receiver: _receiver });',
+    maxUnnamed: 1,
+  },
+
+  maxUnnamed_1_1u: {
+    code: 'funcName(sender);',
+    maxUnnamed: 1,
+  },
+  maxUnnamed_2_2u: {
+    code: 'funcName(sender, amount);',
+    maxUnnamed: 2,
+  },
+
+  maxUnnamed3_0u: {
+    code: 'funcName({ sender: _sender, amount: _amount, receiver: _receiver });',
+    maxUnnamed: 3,
+  },
+}
+
+module.exports = { FUNCTION_CALLS_ERRORS, FUNCTION_CALLS_OK }
diff --git a/test/rules/best-practises/explicit-types.js b/test/rules/best-practises/explicit-types.js
new file mode 100644
index 0000000..842329c
--- /dev/null
+++ b/test/rules/best-practises/explicit-types.js
@@ -0,0 +1,95 @@
+const assert = require('assert')
+const linter = require('../../../lib/index')
+const contractWith = require('../../common/contract-builder').contractWith
+const { assertErrorCount, assertNoErrors, assertErrorMessage } = require('../../common/asserts')
+const VAR_DECLARATIONS = require('../../fixtures/best-practises/explicit-types')
+
+const getZeroErrosObject = () => {
+  const zeroErrorsExplicit = {}
+  const zeroErrorsImplicit = {}
+  for (const key in VAR_DECLARATIONS) {
+    const obj = VAR_DECLARATIONS[key]
+    if (obj.errorsImplicit === 0) {
+      zeroErrorsImplicit[key] = obj
+    }
+    if (obj.errorsExplicit === 0) {
+      zeroErrorsExplicit[key] = obj
+    }
+  }
+
+  return { zeroErrorsImplicit, zeroErrorsExplicit }
+}
+
+describe('Linter - explicit-types rule', () => {
+  it('should throw an error with a wrong configuration rule example 1', () => {
+    const code = contractWith('uint256 public constant SNAKE_CASE = 1;')
+    try {
+      linter.processStr(code, {
+        rules: { 'explicit-types': ['error', 'implicito'] },
+      })
+    } catch (error) {
+      assert.ok(
+        error.toString().includes('Error: Config error on [explicit-types]. See explicit-types.md.')
+      )
+    }
+  })
+
+  it('should throw an error with a wrong configuration rule example 2', () => {
+    const code = contractWith('uint256 public constant SNAKE_CASE = 1;')
+    try {
+      linter.processStr(code, {
+        rules: { 'explicit-types': 'error' },
+      })
+    } catch (error) {
+      assert.ok(
+        error.toString().includes('Error: Config error on [explicit-types]. See explicit-types.md.')
+      )
+    }
+  })
+
+  for (const key in VAR_DECLARATIONS) {
+    it(`should raise error for ${key} on 'implicit' mode`, () => {
+      const { code, errorsImplicit } = VAR_DECLARATIONS[key]
+      const report = linter.processStr(contractWith(code), {
+        rules: { 'explicit-types': ['error', 'implicit'] },
+      })
+      assertErrorCount(report, errorsImplicit)
+      if (errorsImplicit > 0) assertErrorMessage(report, `Rule is set with implicit type [var/s:`)
+    })
+  }
+
+  for (const key in VAR_DECLARATIONS) {
+    it(`should raise error for ${key} on 'explicit' mode`, () => {
+      const { code, errorsExplicit } = VAR_DECLARATIONS[key]
+      const report = linter.processStr(contractWith(code), {
+        rules: { 'explicit-types': ['error', 'explicit'] },
+      })
+      assertErrorCount(report, errorsExplicit)
+      if (errorsExplicit > 0) assertErrorMessage(report, `Rule is set with explicit type [var/s:`)
+    })
+  }
+
+  describe('Rule [explicit-types] - should not raise any error', () => {
+    const { zeroErrorsImplicit, zeroErrorsExplicit } = getZeroErrosObject()
+
+    for (const key in zeroErrorsExplicit) {
+      it(`should NOT raise error for ${key} on 'implicit' mode`, () => {
+        const { code } = zeroErrorsExplicit[key]
+        const report = linter.processStr(contractWith(code), {
+          rules: { 'explicit-types': ['error', 'explicit'] },
+        })
+        assertNoErrors(report)
+      })
+    }
+
+    for (const key in zeroErrorsImplicit) {
+      it(`should NOT raise error for ${key} on 'implicit' mode`, () => {
+        const { code } = zeroErrorsImplicit[key]
+        const report = linter.processStr(contractWith(code), {
+          rules: { 'explicit-types': ['error', 'implicit'] },
+        })
+        assertNoErrors(report)
+      })
+    }
+  })
+})
diff --git a/test/rules/best-practises/no-empty-blocks.js b/test/rules/best-practises/no-empty-blocks.js
index 5df5e7b..ca74fcc 100644
--- a/test/rules/best-practises/no-empty-blocks.js
+++ b/test/rules/best-practises/no-empty-blocks.js
@@ -25,6 +25,8 @@ describe('Linter - no-empty-blocks', () => {
 
   const BLOCKS_WITH_DEFINITIONS = [
     contractWith('function () public payable { make1(); }'),
+    contractWith('receive() external payable {}'),
+    contractWith('fallback() external payable {}'),
     funcWith('if (a < b) { make1(); }'),
     contractWith('struct Abc { uint a; }'),
     contractWith('enum Abc { Test1 }'),
diff --git a/test/rules/best-practises/reason-string.js b/test/rules/best-practises/reason-string.js
index c7f0cfd..30546d7 100644
--- a/test/rules/best-practises/reason-string.js
+++ b/test/rules/best-practises/reason-string.js
@@ -101,4 +101,25 @@ describe('Linter - reason-string', () => {
     assertNoWarnings(report)
     assertNoErrors(report)
   })
+
+  it('should raise reason string maxLength error with added data', () => {
+    const qtyChars = 'Roles: account already has role'.length
+    const maxLength = 5
+
+    const code = funcWith(`require(!has(role, account), "Roles: account already has role");
+        role.bearer[account] = true;role.bearer[account] = true;
+    `)
+
+    const report = linter.processStr(code, {
+      rules: { 'reason-string': ['warn', { maxLength: 5 }] },
+    })
+
+    assert.ok(report.warningCount > 0)
+    assertWarnsCount(report, 1)
+
+    assert.equal(
+      report.reports[0].message,
+      `Error message for require is too long: ${qtyChars} counted / ${maxLength} allowed`
+    )
+  })
 })
diff --git a/test/rules/naming/const-name-snakecase.js b/test/rules/naming/const-name-snakecase.js
index 95289ab..5435751 100644
--- a/test/rules/naming/const-name-snakecase.js
+++ b/test/rules/naming/const-name-snakecase.js
@@ -54,4 +54,43 @@ describe('Linter - const-name-snakecase', () => {
     assert.equal(report.errorCount, 1)
     assert.ok(report.messages[0].message.includes('SNAKE_CASE'))
   })
+
+  it('should not raise const name error for immutable variables in SNAKE_CASE', () => {
+    const code = contractWith('uint32 private immutable SNAKE_CASE;')
+
+    const report = linter.processStr(code, {
+      rules: { 'const-name-snakecase': 'error' },
+    })
+
+    assert.equal(report.errorCount, 0)
+  })
+
+  it('should not raise const name error for immutable variables in mixedCase', () => {
+    const code = contractWith('uint32 private immutable SNAKE_CASE;')
+
+    const report = linter.processStr(code, {
+      rules: { 'const-name-snakecase': 'error' },
+    })
+
+    assert.equal(report.errorCount, 0)
+  })
+
+  describe('constant name with $ character', () => {
+    const WITH_$ = {
+      'starting with $': contractWith('uint32 private constant $THE_CONSTANT = 10;'),
+      'containing a $': contractWith('uint32 private constant THE_$_CONSTANT = 10;'),
+      'ending with $': contractWith('uint32 private constant THE_CONSTANT$ = 10;'),
+      'only with $': contractWith('uint32 private constant $ = 10;'),
+    }
+
+    for (const [key, code] of Object.entries(WITH_$)) {
+      it(`should not raise error for  ${key}`, () => {
+        const report = linter.processStr(code, {
+          rules: { 'const-name-snakecase': 'error' },
+        })
+
+        assert.equal(report.errorCount, 0)
+      })
+    }
+  })
 })
diff --git a/test/rules/naming/contract-name-camelcase.js b/test/rules/naming/contract-name-camelcase.js
index 02f9869..787816c 100644
--- a/test/rules/naming/contract-name-camelcase.js
+++ b/test/rules/naming/contract-name-camelcase.js
@@ -35,4 +35,61 @@ describe('Linter - contract-name-camelcase', () => {
     assert.equal(report.errorCount, 1)
     assert.ok(report.messages[0].message.includes('CamelCase'))
   })
+
+  describe('Struct name with $ character', () => {
+    const WITH_$ = {
+      'starting with $': contractWith('struct $MyStruct {}'),
+      'containing a $': contractWith('struct My$Struct {}'),
+      'ending with $': contractWith('struct MyStruct$ {}'),
+      'only with $': contractWith('struct $ {}'),
+    }
+
+    for (const [key, code] of Object.entries(WITH_$)) {
+      it(`should not raise contract name error for Structs ${key}`, () => {
+        const report = linter.processStr(code, {
+          rules: { 'contract-name-camelcase': 'error' },
+        })
+
+        assert.equal(report.errorCount, 0)
+      })
+    }
+  })
+
+  describe('Enums name with $ character', () => {
+    const WITH_$ = {
+      'starting with $': contractWith('enum $MyEnum {}'),
+      'containing a $': contractWith('enum My$Enum {}'),
+      'ending with $': contractWith('enum MyEnum$ {}'),
+      'only with $': contractWith('enum $ {}'),
+    }
+
+    for (const [key, code] of Object.entries(WITH_$)) {
+      it(`should not raise contract name error for Enums ${key}`, () => {
+        const report = linter.processStr(code, {
+          rules: { 'contract-name-camelcase': 'error' },
+        })
+
+        assert.equal(report.errorCount, 0)
+      })
+    }
+  })
+
+  describe('Contract name with $ character', () => {
+    const WITH_$ = {
+      'starting with $': 'contract $MyContract {}',
+      'containing a $': 'contract My$Contract {}',
+      'ending with $': 'contract MyContract$ {}',
+      'only with $': 'contract $ {}',
+    }
+
+    for (const [key, code] of Object.entries(WITH_$)) {
+      it(`should not raise contract name error for Contracts ${key}`, () => {
+        const report = linter.processStr(code, {
+          rules: { 'contract-name-camelcase': 'error' },
+        })
+
+        assert.equal(report.errorCount, 0)
+      })
+    }
+  })
 })
diff --git a/test/rules/naming/event-name-camelcase.js b/test/rules/naming/event-name-camelcase.js
index 9c0dd2d..27bcd05 100644
--- a/test/rules/naming/event-name-camelcase.js
+++ b/test/rules/naming/event-name-camelcase.js
@@ -13,4 +13,23 @@ describe('Linter - event-name-camelcase', () => {
     assert.equal(report.errorCount, 1)
     assert.ok(report.messages[0].message.includes('CamelCase'))
   })
+
+  describe('Event name with $ character', () => {
+    const WITH_$ = {
+      'starting with $': contractWith('event $Event1(uint a);'),
+      'containing a $': contractWith('event Eve$nt1(uint a);'),
+      'ending with $': contractWith('event Event1$(uint a);'),
+      'only with $': contractWith('event $(uint a);'),
+    }
+
+    for (const [key, code] of Object.entries(WITH_$)) {
+      it(`should not raise event name error for Events ${key}`, () => {
+        const report = linter.processStr(code, {
+          rules: { 'event-name-camelcase': 'error' },
+        })
+
+        assert.equal(report.errorCount, 0)
+      })
+    }
+  })
 })
diff --git a/test/rules/naming/func-name-mixedcase.js b/test/rules/naming/func-name-mixedcase.js
index fe9d62c..c468b26 100644
--- a/test/rules/naming/func-name-mixedcase.js
+++ b/test/rules/naming/func-name-mixedcase.js
@@ -23,4 +23,23 @@ describe('Linter - func-name-mixedcase', () => {
 
     assert.equal(report.errorCount, 0)
   })
+
+  describe('Function names with $ character', () => {
+    const WITH_$ = {
+      'starting with $': contractWith('function $aFunc1Nam23e () public {}'),
+      'containing a $': contractWith('function aFunc$1Nam23e () public {}'),
+      'ending with $': contractWith('function aFunc1Nam23e$ () public {}'),
+      'only with $': contractWith('function $() public {}'),
+    }
+
+    for (const [key, code] of Object.entries(WITH_$)) {
+      it(`should not raise func name error for Functions ${key}`, () => {
+        const report = linter.processStr(code, {
+          rules: { 'func-name-mixedcase': 'error' },
+        })
+
+        assert.equal(report.errorCount, 0)
+      })
+    }
+  })
 })
diff --git a/test/rules/naming/func-named-parameters.js b/test/rules/naming/func-named-parameters.js
new file mode 100644
index 0000000..9fe9056
--- /dev/null
+++ b/test/rules/naming/func-named-parameters.js
@@ -0,0 +1,85 @@
+const linter = require('../../../lib/index')
+const contractWith = require('../../common/contract-builder').contractWith
+const {
+  assertErrorCount,
+  assertNoErrors,
+  assertErrorMessage,
+  assertWarnsCount,
+} = require('../../common/asserts')
+const {
+  FUNCTION_CALLS_ERRORS,
+  FUNCTION_CALLS_OK,
+} = require('../../fixtures/naming/func-named-parameters')
+
+describe('Linter - func-named-parameters', () => {
+  for (const key in FUNCTION_CALLS_ERRORS) {
+    it(`should raise error for FUNCTION_CALLS_ERRORS [${key}]`, () => {
+      const { code, maxUnnamed } = FUNCTION_CALLS_ERRORS[key]
+
+      const sourceCode = contractWith('function callerFunction() public { ' + code + ' }')
+
+      const report = linter.processStr(sourceCode, {
+        rules: { 'func-named-parameters': ['error', maxUnnamed] },
+      })
+
+      assertErrorCount(report, 1)
+      assertErrorMessage(
+        report,
+        `Missing named parameters. Max unnamed parameters value is ${maxUnnamed}`
+      )
+    })
+  }
+
+  for (const key in FUNCTION_CALLS_OK) {
+    it(`should NOT raise error for FUNCTION_CALLS_OK [${key}]`, () => {
+      const { code, maxUnnamed } = FUNCTION_CALLS_OK[key]
+
+      const sourceCode = contractWith('function callerFunction() public { ' + code + ' }')
+
+      const report = linter.processStr(sourceCode, {
+        rules: { 'func-named-parameters': ['error', maxUnnamed] },
+      })
+
+      assertNoErrors(report)
+    })
+  }
+
+  it('should NOT raise error when default rules are configured', () => {
+    const code = contractWith(
+      `function callerFunction() public { funcName(sender, amount, receiver); }`
+    )
+    const report = linter.processStr(code, {
+      extends: 'solhint:default',
+      rules: {},
+    })
+
+    assertNoErrors(report)
+  })
+
+  it('should raise error when recommended rules are configured', () => {
+    const code = contractWith(
+      `function callerFunction() public { funcName(sender, amount, receiver); }`
+    )
+    const report = linter.processStr(code, {
+      extends: 'solhint:recommended',
+      rules: { 'compiler-version': 'off' },
+    })
+
+    assertWarnsCount(report, 1)
+    assertErrorMessage(report, `Missing named parameters. Max unnamed parameters value is 2`)
+  })
+
+  it('should raise error when rule has no maxUnnamed arguments is set and default value takes over', () => {
+    const code = contractWith(
+      `function callerFunction() public { funcName(sender, amount, receiver); }`
+    )
+    const report = linter.processStr(code, {
+      rules: { 'func-named-parameters': 'error' },
+    })
+
+    assertErrorCount(report, 1)
+
+    // default value is 2 for this rule
+    assertErrorMessage(report, `Missing named parameters. Max unnamed parameters value is 2`)
+  })
+})
diff --git a/test/rules/naming/func-param-name-mixedcase.js b/test/rules/naming/func-param-name-mixedcase.js
index 5d7169d..72ef9be 100644
--- a/test/rules/naming/func-param-name-mixedcase.js
+++ b/test/rules/naming/func-param-name-mixedcase.js
@@ -24,4 +24,41 @@ describe('Linter - func-param-name-mixedcase', () => {
     assert.equal(report.errorCount, 1)
     assert.ok(report.messages[0].message.includes('mixedCase'))
   })
+
+  describe('Function Paramters name with $ character', () => {
+    const WITH_$ = {
+      'starting with $': contractWith('function funcName (uint $param) public {}'),
+      'containing a $': contractWith('function funcName (uint pa$ram) public {}'),
+      'ending with $': contractWith('function funcName (uint param$) public {}'),
+      'only with $': contractWith('function funcName(uint $) public {}'),
+    }
+
+    for (const [key, code] of Object.entries(WITH_$)) {
+      it(`should not raise func param name error for Function Parameters ${key}`, () => {
+        const report = linter.processStr(code, {
+          rules: { 'func-param-name-mixedcase': 'error' },
+        })
+
+        assert.equal(report.errorCount, 0)
+      })
+    }
+  })
+  describe('Event parameters name with $ character', () => {
+    const WITH_$ = {
+      'starting with $': contractWith('event Event1(uint $param);'),
+      'containing a $': contractWith('event Event1(uint pa$ram);'),
+      'ending with $': contractWith('event Event1(uint param$);'),
+      'only with $': contractWith('event Event1(uint $);'),
+    }
+
+    for (const [key, code] of Object.entries(WITH_$)) {
+      it(`should not raise func name error for Event Parameters ${key}`, () => {
+        const report = linter.processStr(code, {
+          rules: { 'func-name-mixedcase': 'error' },
+        })
+
+        assert.equal(report.errorCount, 0)
+      })
+    }
+  })
 })
diff --git a/test/rules/naming/immutable-vars-naming.js b/test/rules/naming/immutable-vars-naming.js
new file mode 100644
index 0000000..b695a7e
--- /dev/null
+++ b/test/rules/naming/immutable-vars-naming.js
@@ -0,0 +1,107 @@
+const assert = require('assert')
+const linter = require('../../../lib/index')
+const contractWith = require('../../common/contract-builder').contractWith
+
+describe('immutable-vars-naming', () => {
+  it('should not raise error for non immutable variables if rule is off', () => {
+    const code = contractWith('uint32 private immutable D;')
+
+    const report = linter.processStr(code, {
+      rules: {
+        'immutable-vars-naming': 'off',
+        'var-name-mixedcase': 'error',
+        'const-name-snakecase': 'error',
+      },
+    })
+
+    assert.equal(report.errorCount, 0)
+  })
+
+  it('should raise error when immutablesAsConstants = false and variable is in snake case', () => {
+    const code = contractWith('uint32 private immutable SNAKE_CASE;')
+
+    const report = linter.processStr(code, {
+      rules: { 'immutable-vars-naming': ['error', { immutablesAsConstants: false }] },
+    })
+
+    assert.equal(report.errorCount, 1)
+    assert.ok(
+      report.messages[0].message.includes('Immutable variables names are set to be in mixedCase')
+    )
+  })
+
+  it('should NOT raise error when immutablesAsConstants = false and variable is in snake case', () => {
+    const code = contractWith('uint32 private immutable SNAKE_CASE;')
+
+    const report = linter.processStr(code, {
+      rules: { 'immutable-vars-naming': ['error', { immutablesAsConstants: true }] },
+    })
+
+    assert.equal(report.errorCount, 0)
+  })
+
+  it('should raise error when immutablesAsConstants = true and variable is in mixedCase', () => {
+    const code = contractWith('uint32 private immutable mixedCase;')
+
+    const report = linter.processStr(code, {
+      rules: { 'immutable-vars-naming': ['error', { immutablesAsConstants: true }] },
+    })
+
+    assert.equal(report.errorCount, 1)
+    assert.ok(
+      report.messages[0].message.includes(
+        'Immutable variables name are set to be in capitalized SNAKE_CASE'
+      )
+    )
+  })
+
+  it('should NOT raise error when immutablesAsConstants = false and variable is in mixedCase', () => {
+    const code = contractWith('uint32 private immutable mixedCase;')
+
+    const report = linter.processStr(code, {
+      rules: { 'immutable-vars-naming': ['error', { immutablesAsConstants: false }] },
+    })
+
+    assert.equal(report.errorCount, 0)
+  })
+
+  describe('Immutable variable with $ character as mixedCase', () => {
+    const WITH_$ = {
+      'starting with $': contractWith('uint32 immutable private $D;'),
+      'containing a $': contractWith('uint32 immutable private testWith$Contained;'),
+      'ending with $': contractWith('uint32 immutable private testWithEnding$;'),
+      'only with $': contractWith('uint32 immutable private $;'),
+    }
+
+    for (const [key, code] of Object.entries(WITH_$)) {
+      it(`should not raise immutable error for variables ${key}`, () => {
+        const report = linter.processStr(code, {
+          rules: { 'immutable-vars-naming': ['error', { immutablesAsConstants: false }] },
+        })
+
+        assert.equal(report.errorCount, 0)
+      })
+    }
+  })
+
+  describe('Immutable variable with $ character as SNAKE_CASE', () => {
+    const WITH_$ = {
+      'starting with $': contractWith('uint32 immutable private $_D;'),
+      'starting with $D': contractWith('uint32 immutable private $D;'),
+      'containing a $': contractWith('uint32 immutable private TEST_WITH_$_CONTAINED;'),
+      'ending with $': contractWith('uint32 immutable private TEST_WITH_ENDING_$;'),
+      'ending with D$': contractWith('uint32 immutable private TEST_WITH_ENDING_D$;'),
+      'only with $': contractWith('uint32 immutable private $;'),
+    }
+
+    for (const [key, code] of Object.entries(WITH_$)) {
+      it(`should not raise immutable error for variables ${key}`, () => {
+        const report = linter.processStr(code, {
+          rules: { 'immutable-vars-naming': ['error', { immutablesAsConstants: true }] },
+        })
+
+        assert.equal(report.errorCount, 0)
+      })
+    }
+  })
+})
diff --git a/test/rules/naming/modifier-name-mixedcase.js b/test/rules/naming/modifier-name-mixedcase.js
index a652c15..5dfd1a3 100644
--- a/test/rules/naming/modifier-name-mixedcase.js
+++ b/test/rules/naming/modifier-name-mixedcase.js
@@ -23,4 +23,23 @@ describe('Linter - modifier-name-mixedcase', () => {
 
     assert.equal(report.errorCount, 0)
   })
+
+  describe('with $ character', () => {
+    const WITH_$ = {
+      'starting with $': contractWith('modifier $ownedBy(address a) { }'),
+      'containing a $': contractWith('modifier owned$By(address a) { }'),
+      'ending with $': contractWith('modifier ownedBy$(address a) { }'),
+      'only with $': contractWith('modifier $(uint a) { }'),
+    }
+
+    for (const [key, code] of Object.entries(WITH_$)) {
+      it(`should not raise func name error for Modifiers ${key}`, () => {
+        const report = linter.processStr(code, {
+          rules: { 'modifier-name-mixedcase': 'error' },
+        })
+
+        assert.equal(report.errorCount, 0)
+      })
+    }
+  })
 })
diff --git a/test/rules/naming/var-name-mixedcase.js b/test/rules/naming/var-name-mixedcase.js
index 13fa86e..2e6730e 100644
--- a/test/rules/naming/var-name-mixedcase.js
+++ b/test/rules/naming/var-name-mixedcase.js
@@ -46,4 +46,43 @@ describe('Linter - var-name-mixedcase', () => {
 
     assert.equal(report.errorCount, 0)
   })
+
+  it('should not raise const name error for immutable variables in SNAKE_CASE', () => {
+    const code = contractWith('uint32 private immutable SNAKE_CASE;')
+
+    const report = linter.processStr(code, {
+      rules: { 'no-unused-vars': 'error', 'var-name-mixedcase': 'error' },
+    })
+
+    assert.equal(report.errorCount, 0)
+  })
+
+  it('should not raise const name error for immutable variables in mixedCase', () => {
+    const code = contractWith('uint32 private immutable SNAKE_CASE;')
+
+    const report = linter.processStr(code, {
+      rules: { 'no-unused-vars': 'error', 'var-name-mixedcase': 'error' },
+    })
+
+    assert.equal(report.errorCount, 0)
+  })
+
+  describe('with $ character', () => {
+    const WITH_$ = {
+      'starting with $': contractWith('uint32 private $D = 10;'),
+      'containing a $': contractWith('uint32 private testWith$Contained = 10;'),
+      'ending with $': contractWith('uint32 private testWithEnding$ = 10;'),
+      'only with $': contractWith('uint32 private $;'),
+    }
+
+    for (const [key, code] of Object.entries(WITH_$)) {
+      it(`should not raise var name error for variables ${key}`, () => {
+        const report = linter.processStr(code, {
+          rules: { 'var-name-mixedcase': 'error' },
+        })
+
+        assert.equal(report.errorCount, 0)
+      })
+    }
+  })
 })