Repository: protofire/solhint
Pull request #694 (2025-07-04 19:24:48)

 MERGE: develop into master v6.0.0


Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 26a3e61..afdf29a 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,11 +1,47 @@
+## [6.0.0] - 2025-07-04
+
+### BREAKING CHANGE
+Some rules were removed from the recommended ruleset and other were promoted. See below:
+Rules revision - [#692](https://github.com/protofire/solhint/pull/692)
+
+#### 🟥 DEMOTED
+
+- `payable-fallback`: Outdated due to the introduction of receive(); rarely needed in modern Solidity.
+<br>
+
+#### ✅ PROMOTED
+
+- `interface-starts-with-i`: Promotes clarity by clearly distinguishing interfaces from contracts.
+- `gas-calldata-parameters`: Encourages optimal memory usage for external functions.
+- `gas-increment-by-one`: Suggests using ++i style for gas-efficient increments.
+- `gas-indexed-events`: Improves event indexing for off-chain systems and reduces gas usage.
+- `gas-small-strings`: Recommends cheaper encoding using bytes32 for short strings.
+- `gas-strict-inequalities`: Helps avoid subtle off-by-one bugs in range conditions.
+- `gas-struct-packing`: Maximizes storage efficiency by suggesting tight struct packing.
+- `duplicated-imports`: Prevents redundant imports that bloat the AST and confuse code structure.
+- `import-path-check`: Ensures all imported files exist and resolve correctly, avoiding runtime errors.
+- `function-max-lines`: Encourages smaller, more readable and maintainable functions.
+- `constructor-syntax`: Enforces modern constructor syntax for consistency and clarity.
+- `use-natspec`: Promotes proper documentation with NatSpec for better audits and readability.
+
+### Fixed
+- `gas-struct-packing` false positives - [#690](https://github.com/protofire/solhint/pull/690)
+- `gas-increment-by-one` backward expression - [#691](https://github.com/protofire/solhint/pull/691)
+- Typo (thanks to @[MarkFizz77](https://github.com/MarkFizz77))
+
+### Added
+ - `use-natspec`: Promote the use of natspec and make several checks to enforce it - [#689](https://github.com/protofire/solhint/pull/689)
+
+<br><br>
+
 ## [5.2.0] - 2025-06-27
 ### Fixed
- - `imports-order` interference with `no-unused-imports` [#684](https://github.com/protofire/solhint/pull/684)
+ - `imports-order` interference with `no-unused-imports` - [#684](https://github.com/protofire/solhint/pull/684)
  
 ### Added
 - Added `cache` flag allowing to skip already evaluated files if no error was thrown and file did not change - [#685](https://github.com/protofire/solhint/pull/685)
 - Added multi-directory config support by hierarchy - [#678](https://github.com/protofire/solhint/pull/678)
-- Added better error handling on invalid configs -  [#683](https://github.com/protofire/solhint/pull/683)
+- Added better error handling on invalid configs - [#683](https://github.com/protofire/solhint/pull/683)
  
 
 Thanks to [@smol-ninja](https://github.com/smol-ninja) - [@PaulRBerg](https://github.com/PaulRBerg) [@lechpzn](https://github.com/lechpzn), [@otc-png](https://github.com/otc-png), [@MamunC0der](https://github.com/MamunC0der), [@kks-code](https://github.com/kks-code), [@RidaMichofi](https://github.com/RidaMichofi)
diff --git a/conf/rulesets/solhint-all.js b/conf/rulesets/solhint-all.js
index 3e6a635..3a659a5 100644
--- a/conf/rulesets/solhint-all.js
+++ b/conf/rulesets/solhint-all.js
@@ -24,6 +24,31 @@ module.exports = Object.freeze({
         maxLength: 32,
       },
     ],
+    'use-natspec': [
+      'warn',
+      {
+        title: {
+          enabled: true,
+          ignore: {},
+        },
+        author: {
+          enabled: true,
+          ignore: {},
+        },
+        notice: {
+          enabled: true,
+          ignore: {},
+        },
+        param: {
+          enabled: true,
+          ignore: {},
+        },
+        return: {
+          enabled: true,
+          ignore: {},
+        },
+      },
+    ],
     'constructor-syntax': 'warn',
     'gas-calldata-parameters': 'warn',
     'gas-custom-errors': 'warn',
diff --git a/conf/rulesets/solhint-recommended.js b/conf/rulesets/solhint-recommended.js
index bdc7808..7515c0d 100644
--- a/conf/rulesets/solhint-recommended.js
+++ b/conf/rulesets/solhint-recommended.js
@@ -6,6 +6,8 @@
 module.exports = Object.freeze({
   rules: {
     'explicit-types': ['warn', 'explicit'],
+    'function-max-lines': ['warn', 50],
+    'interface-starts-with-i': 'warn',
     'max-states-count': ['warn', 15],
     'no-console': 'error',
     'no-empty-blocks': 'warn',
@@ -13,14 +15,46 @@ module.exports = Object.freeze({
     'no-unused-import': 'warn',
     'no-unused-vars': 'warn',
     'one-contract-per-file': 'warn',
-    'payable-fallback': 'warn',
     'reason-string': [
       'warn',
       {
         maxLength: 32,
       },
     ],
+    'use-natspec': [
+      'warn',
+      {
+        title: {
+          enabled: true,
+          ignore: {},
+        },
+        author: {
+          enabled: true,
+          ignore: {},
+        },
+        notice: {
+          enabled: true,
+          ignore: {},
+        },
+        param: {
+          enabled: true,
+          ignore: {},
+        },
+        return: {
+          enabled: true,
+          ignore: {},
+        },
+      },
+    ],
+    'constructor-syntax': 'warn',
+    'gas-calldata-parameters': 'warn',
     'gas-custom-errors': 'warn',
+    'gas-increment-by-one': 'warn',
+    'gas-indexed-events': 'warn',
+    'gas-small-strings': 'warn',
+    'gas-strict-inequalities': 'warn',
+    'gas-struct-packing': 'warn',
+    'duplicated-imports': 'warn',
     'import-path-check': ['warn', ['[~dependenciesPath]']],
     quotes: ['error', 'double'],
     'const-name-snakecase': 'warn',
diff --git a/docker/Dockerfile b/docker/Dockerfile
index 22840bd..76b5684 100644
--- a/docker/Dockerfile
+++ b/docker/Dockerfile
@@ -1,5 +1,5 @@
 FROM node:20-alpine
 LABEL maintainer="diego.bale@protofire.io" 
-ENV VERSION=5.2.0
+ENV VERSION=6.0.0
 
 RUN npm install -g solhint@"$VERSION"
\ No newline at end of file
diff --git a/docs/rules.md b/docs/rules.md
index 23d55ee..a471695 100644
--- a/docs/rules.md
+++ b/docs/rules.md
@@ -10,7 +10,7 @@ title:       "Rule Index of Solhint"
 | ------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------ | ------------ | ---------- |
 | [code-complexity](./rules/best-practices/code-complexity.md)             | Function has cyclomatic complexity "current" but allowed no more than configured max complexity.                                           |              |            |
 | [explicit-types](./rules/best-practices/explicit-types.md)               | Forbid or enforce explicit types (like uint256) that have an alias (like uint).                                                            | $~~~~~~~~$✔️ |            |
-| [function-max-lines](./rules/best-practices/function-max-lines.md)       | Function body contains "count" lines but allowed no more than maxlines.                                                                    |              |            |
+| [function-max-lines](./rules/best-practices/function-max-lines.md)       | Function body contains "count" lines but allowed no more than maxlines.                                                                    | $~~~~~~~~$✔️ |            |
 | [max-line-length](./rules/best-practices/max-line-length.md)             | Line length should not exceed configured number of characters.                                                                             |              |            |
 | [max-states-count](./rules/best-practices/max-states-count.md)           | Contract has "some count" states declarations but allowed no more than defined max states.                                                 | $~~~~~~~~$✔️ |            |
 | [no-console](./rules/best-practices/no-console.md)                       | No console.log/logInt/logBytesX/logString/etc & No hardhat and forge-std console.sol import statements.                                    | $~~~~~~~~$✔️ |            |
@@ -19,17 +19,18 @@ title:       "Rule Index of Solhint"
 | [no-unused-import](./rules/best-practices/no-unused-import.md)           | Imported object name is not being used by the contract.                                                                                    | $~~~~~~~~$✔️ |            |
 | [no-unused-vars](./rules/best-practices/no-unused-vars.md)               | Variable "name" is unused.                                                                                                                 | $~~~~~~~~$✔️ |            |
 | [one-contract-per-file](./rules/best-practices/one-contract-per-file.md) | Enforces the use of ONE Contract per file see [here](https://docs.soliditylang.org/en/v0.8.21/style-guide.html#contract-and-library-names) | $~~~~~~~~$✔️ |            |
-| [payable-fallback](./rules/best-practices/payable-fallback.md)           | When fallback is not payable and there is no receive function you will not be able to receive currency.                                    | $~~~~~~~~$✔️ |            |
+| [payable-fallback](./rules/best-practices/payable-fallback.md)           | When fallback is not payable and there is no receive function you will not be able to receive currency.                                    |              |            |
 | [reason-string](./rules/best-practices/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-practices/constructor-syntax.md)       | Constructors should use the new constructor keyword.                                                                                       |              |            |
+| [use-natspec](./rules/best-practices/use-natspec.md)                     | Enforces the presence and correctness of NatSpec tags.                                                                                     | $~~~~~~~~$✔️ |            |
+| [constructor-syntax](./rules/best-practices/constructor-syntax.md)       | Constructors should use the new constructor keyword.                                                                                       | $~~~~~~~~$✔️ |            |
         
 
 ## Style Guide Rules
 
 | Rule Id                                                                              | Error                                                                                                         | Recommended  | Deprecated |
 | ------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------- | ------------ | ---------- |
-| [interface-starts-with-i](./rules/naming/interface-starts-with-i.md)                 | Solidity Interfaces names should start with an `I`                                                            |              |            |
-| [duplicated-imports](./rules/miscellaneous/duplicated-imports.md)                    | Check if an import is done twice in the same file and there is no alias                                       |              |            |
+| [interface-starts-with-i](./rules/naming/interface-starts-with-i.md)                 | Solidity Interfaces names should start with an `I`                                                            | $~~~~~~~~$✔️ |            |
+| [duplicated-imports](./rules/miscellaneous/duplicated-imports.md)                    | Check if an import is done twice in the same file and there is no alias                                       | $~~~~~~~~$✔️ |            |
 | [const-name-snakecase](./rules/naming/const-name-snakecase.md)                       | Constant name must be in capitalized SNAKE_CASE. (Does not check IMMUTABLES, use immutable-vars-naming)       | $~~~~~~~~$✔️ |            |
 | [contract-name-capwords](./rules/naming/contract-name-capwords.md)                   | Contract, Structs and Enums should be in CapWords.                                                            | $~~~~~~~~$✔️ |            |
 | [event-name-capwords](./rules/naming/event-name-capwords.md)                         | Event name must be in CapWords.                                                                               | $~~~~~~~~$✔️ |            |
@@ -53,16 +54,16 @@ title:       "Rule Index of Solhint"
 
 | Rule Id                                                                       | Error                                                                                   | Recommended  | Deprecated |
 | ----------------------------------------------------------------------------- | --------------------------------------------------------------------------------------- | ------------ | ---------- |
-| [gas-calldata-parameters](./rules/gas-consumption/gas-calldata-parameters.md) | Suggest calldata keyword on function arguments when read only                           |              |            |
+| [gas-calldata-parameters](./rules/gas-consumption/gas-calldata-parameters.md) | Suggest calldata keyword on function arguments when read only                           | $~~~~~~~~$✔️ |            |
 | [gas-custom-errors](./rules/gas-consumption/gas-custom-errors.md)             | Enforces the use of Custom Errors over Require with strings error and Revert statements | $~~~~~~~~$✔️ |            |
-| [gas-increment-by-one](./rules/gas-consumption/gas-increment-by-one.md)       | Suggest increments by one, like this ++i instead of other type                          |              |            |
-| [gas-indexed-events](./rules/gas-consumption/gas-indexed-events.md)           | Suggest indexed arguments on events for uint, bool and address                          |              |            |
+| [gas-increment-by-one](./rules/gas-consumption/gas-increment-by-one.md)       | Suggest increments by one, like this ++i instead of other type                          | $~~~~~~~~$✔️ |            |
+| [gas-indexed-events](./rules/gas-consumption/gas-indexed-events.md)           | Suggest indexed arguments on events for uint, bool and address                          | $~~~~~~~~$✔️ |            |
 | [gas-length-in-loops](./rules/gas-consumption/gas-length-in-loops.md)         | Suggest replacing object.length in a loop condition to avoid calculation on each lap    |              |            |
 | [gas-multitoken1155](./rules/gas-consumption/gas-multitoken1155.md)           | ERC1155 is a cheaper non-fungible token than ERC721                                     |              |            |
 | [gas-named-return-values](./rules/gas-consumption/gas-named-return-values.md) | Enforce the return values of a function to be named                                     |              |            |
-| [gas-small-strings](./rules/gas-consumption/gas-small-strings.md)             | Keep strings smaller than 32 bytes                                                      |              |            |
-| [gas-strict-inequalities](./rules/gas-consumption/gas-strict-inequalities.md) | Suggest Strict Inequalities over non Strict ones                                        |              |            |
-| [gas-struct-packing](./rules/gas-consumption/gas-struct-packing.md)           | Suggest to re-arrange struct packing order when it is inefficient                       |              |            |
+| [gas-small-strings](./rules/gas-consumption/gas-small-strings.md)             | Keep strings smaller than 32 bytes. Promote the use of custom errors                    | $~~~~~~~~$✔️ |            |
+| [gas-strict-inequalities](./rules/gas-consumption/gas-strict-inequalities.md) | Suggest Strict Inequalities over non Strict ones                                        | $~~~~~~~~$✔️ |            |
+| [gas-struct-packing](./rules/gas-consumption/gas-struct-packing.md)           | Suggest to re-arrange struct packing order when it is inefficient                       | $~~~~~~~~$✔️ |            |
         
 
 ## Miscellaneous
diff --git a/docs/rules/best-practices/code-complexity.md b/docs/rules/best-practices/code-complexity.md
index 5076ada..f62246b 100644
--- a/docs/rules/best-practices/code-complexity.md
+++ b/docs/rules/best-practices/code-complexity.md
@@ -24,7 +24,10 @@ This rule accepts an array of options:
 ```json
 {
   "rules": {
-    "code-complexity": ["warn",7]
+    "code-complexity": [
+      "warn",
+      7
+    ]
   }
 }
 ```
diff --git a/docs/rules/best-practices/constructor-syntax.md b/docs/rules/best-practices/constructor-syntax.md
index 27a8b19..5e44939 100644
--- a/docs/rules/best-practices/constructor-syntax.md
+++ b/docs/rules/best-practices/constructor-syntax.md
@@ -5,8 +5,11 @@ title:       "constructor-syntax | Solhint"
 ---
 
 # constructor-syntax
+![Recommended Badge](https://img.shields.io/badge/-Recommended-brightgreen)
 ![Category Badge](https://img.shields.io/badge/-Best%20Practices%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
 Constructors should use the new constructor keyword.
diff --git a/docs/rules/best-practices/explicit-types.md b/docs/rules/best-practices/explicit-types.md
index 1e8cd62..7c13a8f 100644
--- a/docs/rules/best-practices/explicit-types.md
+++ b/docs/rules/best-practices/explicit-types.md
@@ -27,7 +27,10 @@ This rule accepts an array of options:
 ```json
 {
   "rules": {
-    "explicit-types": ["warn","explicit"]
+    "explicit-types": [
+      "warn",
+      "explicit"
+    ]
   }
 }
 ```
diff --git a/docs/rules/best-practices/function-max-lines.md b/docs/rules/best-practices/function-max-lines.md
index f5e86e3..7d0f809 100644
--- a/docs/rules/best-practices/function-max-lines.md
+++ b/docs/rules/best-practices/function-max-lines.md
@@ -5,8 +5,11 @@ title:       "function-max-lines | Solhint"
 ---
 
 # function-max-lines
+![Recommended Badge](https://img.shields.io/badge/-Recommended-brightgreen)
 ![Category Badge](https://img.shields.io/badge/-Best%20Practices%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
 Function body contains "count" lines but allowed no more than maxlines.
@@ -24,7 +27,10 @@ This rule accepts an array of options:
 ```json
 {
   "rules": {
-    "function-max-lines": ["warn",50]
+    "function-max-lines": [
+      "warn",
+      50
+    ]
   }
 }
 ```
diff --git a/docs/rules/best-practices/max-line-length.md b/docs/rules/best-practices/max-line-length.md
index 5c133ea..d950331 100644
--- a/docs/rules/best-practices/max-line-length.md
+++ b/docs/rules/best-practices/max-line-length.md
@@ -26,7 +26,10 @@ This rule accepts an array of options:
 ```json
 {
   "rules": {
-    "max-line-length": ["error",120]
+    "max-line-length": [
+      "error",
+      120
+    ]
   }
 }
 ```
diff --git a/docs/rules/best-practices/max-states-count.md b/docs/rules/best-practices/max-states-count.md
index cd39de6..a50aa4a 100644
--- a/docs/rules/best-practices/max-states-count.md
+++ b/docs/rules/best-practices/max-states-count.md
@@ -27,7 +27,10 @@ This rule accepts an array of options:
 ```json
 {
   "rules": {
-    "max-states-count": ["warn",15]
+    "max-states-count": [
+      "warn",
+      15
+    ]
   }
 }
 ```
diff --git a/docs/rules/best-practices/payable-fallback.md b/docs/rules/best-practices/payable-fallback.md
index d997d10..b1b340c 100644
--- a/docs/rules/best-practices/payable-fallback.md
+++ b/docs/rules/best-practices/payable-fallback.md
@@ -5,11 +5,8 @@ title:       "payable-fallback | Solhint"
 ---
 
 # payable-fallback
-![Recommended Badge](https://img.shields.io/badge/-Recommended-brightgreen)
 ![Category Badge](https://img.shields.io/badge/-Best%20Practices%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
 When fallback is not payable and there is no receive function you will not be able to receive currency.
diff --git a/docs/rules/best-practices/reason-string.md b/docs/rules/best-practices/reason-string.md
index 4e22fae..e2320f2 100644
--- a/docs/rules/best-practices/reason-string.md
+++ b/docs/rules/best-practices/reason-string.md
@@ -27,7 +27,12 @@ This rule accepts an array of options:
 ```json
 {
   "rules": {
-    "reason-string": ["warn",{"maxLength":32}]
+    "reason-string": [
+      "warn",
+      {
+        "maxLength": 32
+      }
+    ]
   }
 }
 ```
diff --git a/docs/rules/best-practices/use-natspec.md b/docs/rules/best-practices/use-natspec.md
new file mode 100644
index 0000000..c4f7a8a
--- /dev/null
+++ b/docs/rules/best-practices/use-natspec.md
@@ -0,0 +1,141 @@
+---
+warning:     "This is a dynamically generated file. Do not edit manually."
+layout:      "default"
+title:       "use-natspec | Solhint"
+---
+
+# use-natspec
+![Recommended Badge](https://img.shields.io/badge/-Recommended-brightgreen)
+![Category Badge](https://img.shields.io/badge/-Best%20Practices%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
+Enforces the presence and correctness of NatSpec tags.
+
+## 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 natspec properties. See EXAMPLE CONFIG section below. | Check EXAMPLE CONFIG |
+
+
+### Example Config
+```json
+{
+  "rules": {
+    "use-natspec": [
+      "warn",
+      {
+        "title": {
+          "enabled": true,
+          "ignore": {}
+        },
+        "author": {
+          "enabled": true,
+          "ignore": {}
+        },
+        "notice": {
+          "enabled": true,
+          "ignore": {}
+        },
+        "param": {
+          "enabled": true,
+          "ignore": {}
+        },
+        "return": {
+          "enabled": true,
+          "ignore": {}
+        }
+      }
+    ]
+  }
+}
+```
+
+### Notes
+- If a function or return value has unnamed parameters (e.g. `function foo(uint256)`), the rule only checks the number of `@param` or `@return` tags, not their names.
+- If a function or variable has `@inheritdoc`, the rule skips the validation.
+- The rule supports both `///` and `/** */` style NatSpec comments.
+- If a custom config is provided, it is merged with the default config. Only the overridden fields need to be specified.
+
+## Examples
+### 👍 Examples of **correct** code for this rule
+
+#### Contract with valid NatSpec
+
+```solidity
+
+                /// @title Token contract
+                /// @author Me
+                /// @notice This contract handles tokens
+                contract Token {
+                  /// @notice Transfers tokens
+                  /// @param to The recipient address
+                  /// @param amount The amount to transfer
+                  /// @return success Whether the transfer succeeded
+                  function transfer(address to, uint256 amount) public returns (bool success) {
+                    return true;
+                  }
+                }
+              
+```
+
+#### You can disable specific tags globally or by type/name using the `ignore` option in the config. For example:
+
+```solidity
+{
+                "use-natspec": [
+                  "warn",
+                  {
+                    "title": {
+                      "enabled": true,
+                      "ignore": {
+                        "contract": ["MyContract"],
+                        "*": ["LegacyContract"]
+                      }
+                    },
+                    "param": { "enabled": false }
+                  }
+                ]
+              }
+```
+
+#### The default configuration enables all checks with no ignore rules:
+
+```solidity
+{
+                "title": { "enabled": true, "ignore": {} },
+                "author": { "enabled": true, "ignore": {} },
+                "notice": { "enabled": true, "ignore": {} },
+                "param": { "enabled": true, "ignore": {} },
+                "return": { "enabled": true, "ignore": {} }
+              }
+```
+
+### 👎 Examples of **incorrect** code for this rule
+
+#### Missing @notice and incorrect @param and @return tags
+
+```solidity
+
+                /// @title Token contract
+                contract Token {
+                  /// @param wrongParam Not matching actual parameter
+                  function transfer(address to) public returns (bool) {
+                    return true;
+                  }
+                }
+                
+```
+
+## Version
+This rule was introduced in the latest version.
+
+## Resources
+- [Rule source](https://github.com/protofire/solhint/blob/master/lib/rules/best-practices/use-natspec.js)
+- [Document source](https://github.com/protofire/solhint/blob/master/docs/rules/best-practices/use-natspec.md)
+- [Test cases](https://github.com/protofire/solhint/blob/master/test/rules/best-practices/use-natspec.js)
diff --git a/docs/rules/gas-consumption/gas-calldata-parameters.md b/docs/rules/gas-consumption/gas-calldata-parameters.md
index 70a1ca4..2de581f 100644
--- a/docs/rules/gas-consumption/gas-calldata-parameters.md
+++ b/docs/rules/gas-consumption/gas-calldata-parameters.md
@@ -5,8 +5,11 @@ title:       "gas-calldata-parameters | Solhint"
 ---
 
 # gas-calldata-parameters
+![Recommended Badge](https://img.shields.io/badge/-Recommended-brightgreen)
 ![Category Badge](https://img.shields.io/badge/-Gas%20Consumption%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
 Suggest calldata keyword on function arguments when read only
diff --git a/docs/rules/gas-consumption/gas-increment-by-one.md b/docs/rules/gas-consumption/gas-increment-by-one.md
index 8d732d4..92ede96 100644
--- a/docs/rules/gas-consumption/gas-increment-by-one.md
+++ b/docs/rules/gas-consumption/gas-increment-by-one.md
@@ -5,8 +5,11 @@ title:       "gas-increment-by-one | Solhint"
 ---
 
 # gas-increment-by-one
+![Recommended Badge](https://img.shields.io/badge/-Recommended-brightgreen)
 ![Category Badge](https://img.shields.io/badge/-Gas%20Consumption%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
 Suggest increments by one, like this ++i instead of other type
@@ -24,7 +27,6 @@ This rule accepts a string option for rule severity. Must be one of "error", "wa
 ```
 
 ### Notes
-- This rule only works for expressions like this: [ j = j + 1 ] but will fail if the code is written like this: [ j = 1 + j ]
 - [source 1](https://coinsbench.com/comprehensive-guide-tips-and-tricks-for-gas-optimization-in-solidity-5380db734404) of the rule initiative (Incrementing/Decrementing By 1)
 - [source 2](https://www.rareskills.io/post/gas-optimization?postId=c9db474a-ff97-4fa3-a51d-fe13ccb8fe3b#viewer-8rekj) of the rule initiative
 
diff --git a/docs/rules/gas-consumption/gas-indexed-events.md b/docs/rules/gas-consumption/gas-indexed-events.md
index cad0d25..78dc077 100644
--- a/docs/rules/gas-consumption/gas-indexed-events.md
+++ b/docs/rules/gas-consumption/gas-indexed-events.md
@@ -5,8 +5,11 @@ title:       "gas-indexed-events | Solhint"
 ---
 
 # gas-indexed-events
+![Recommended Badge](https://img.shields.io/badge/-Recommended-brightgreen)
 ![Category Badge](https://img.shields.io/badge/-Gas%20Consumption%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
 Suggest indexed arguments on events for uint, bool and address
diff --git a/docs/rules/gas-consumption/gas-small-strings.md b/docs/rules/gas-consumption/gas-small-strings.md
index 639218f..222e049 100644
--- a/docs/rules/gas-consumption/gas-small-strings.md
+++ b/docs/rules/gas-consumption/gas-small-strings.md
@@ -5,11 +5,14 @@ title:       "gas-small-strings | Solhint"
 ---
 
 # gas-small-strings
+![Recommended Badge](https://img.shields.io/badge/-Recommended-brightgreen)
 ![Category Badge](https://img.shields.io/badge/-Gas%20Consumption%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
-Keep strings smaller than 32 bytes
+Keep strings smaller than 32 bytes. Promote the use of custom errors
 
 ## Options
 This rule accepts a string option for rule severity. Must be one of "error", "warn", "off". Defaults to warn.
diff --git a/docs/rules/gas-consumption/gas-strict-inequalities.md b/docs/rules/gas-consumption/gas-strict-inequalities.md
index be7876b..86fcbe1 100644
--- a/docs/rules/gas-consumption/gas-strict-inequalities.md
+++ b/docs/rules/gas-consumption/gas-strict-inequalities.md
@@ -5,8 +5,11 @@ title:       "gas-strict-inequalities | Solhint"
 ---
 
 # gas-strict-inequalities
+![Recommended Badge](https://img.shields.io/badge/-Recommended-brightgreen)
 ![Category Badge](https://img.shields.io/badge/-Gas%20Consumption%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
 Suggest Strict Inequalities over non Strict ones
diff --git a/docs/rules/gas-consumption/gas-struct-packing.md b/docs/rules/gas-consumption/gas-struct-packing.md
index 5e6147f..c9f2113 100644
--- a/docs/rules/gas-consumption/gas-struct-packing.md
+++ b/docs/rules/gas-consumption/gas-struct-packing.md
@@ -5,8 +5,11 @@ title:       "gas-struct-packing | Solhint"
 ---
 
 # gas-struct-packing
+![Recommended Badge](https://img.shields.io/badge/-Recommended-brightgreen)
 ![Category Badge](https://img.shields.io/badge/-Gas%20Consumption%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
 Suggest to re-arrange struct packing order when it is inefficient
diff --git a/docs/rules/miscellaneous/duplicated-imports.md b/docs/rules/miscellaneous/duplicated-imports.md
index 1f9582b..dd2a8a6 100644
--- a/docs/rules/miscellaneous/duplicated-imports.md
+++ b/docs/rules/miscellaneous/duplicated-imports.md
@@ -5,8 +5,11 @@ title:       "duplicated-imports | Solhint"
 ---
 
 # duplicated-imports
+![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 if an import is done twice in the same file and there is no alias
diff --git a/docs/rules/miscellaneous/import-path-check.md b/docs/rules/miscellaneous/import-path-check.md
index 52a8253..5ba9842 100644
--- a/docs/rules/miscellaneous/import-path-check.md
+++ b/docs/rules/miscellaneous/import-path-check.md
@@ -27,7 +27,12 @@ This rule accepts an array of options:
 ```json
 {
   "rules": {
-    "import-path-check": ["warn",["[~dependenciesPath]"]]
+    "import-path-check": [
+      "warn",
+      [
+        "[~dependenciesPath]"
+      ]
+    ]
   }
 }
 ```
diff --git a/docs/rules/miscellaneous/quotes.md b/docs/rules/miscellaneous/quotes.md
index 54e9e07..cdc0925 100644
--- a/docs/rules/miscellaneous/quotes.md
+++ b/docs/rules/miscellaneous/quotes.md
@@ -27,7 +27,10 @@ This rule accepts an array of options:
 ```json
 {
   "rules": {
-    "quotes": ["error","double"]
+    "quotes": [
+      "error",
+      "double"
+    ]
   }
 }
 ```
diff --git a/docs/rules/naming/func-named-parameters.md b/docs/rules/naming/func-named-parameters.md
index b5b0a10..b41d087 100644
--- a/docs/rules/naming/func-named-parameters.md
+++ b/docs/rules/naming/func-named-parameters.md
@@ -24,7 +24,10 @@ This rule accepts an array of options:
 ```json
 {
   "rules": {
-    "func-named-parameters": ["warn",4]
+    "func-named-parameters": [
+      "warn",
+      4
+    ]
   }
 }
 ```
diff --git a/docs/rules/naming/immutable-vars-naming.md b/docs/rules/naming/immutable-vars-naming.md
index d7d301b..57c1453 100644
--- a/docs/rules/naming/immutable-vars-naming.md
+++ b/docs/rules/naming/immutable-vars-naming.md
@@ -27,7 +27,12 @@ This rule accepts an array of options:
 ```json
 {
   "rules": {
-    "immutable-vars-naming": ["warn",{"immutablesAsConstants":true}]
+    "immutable-vars-naming": [
+      "warn",
+      {
+        "immutablesAsConstants": true
+      }
+    ]
   }
 }
 ```
diff --git a/docs/rules/naming/interface-starts-with-i.md b/docs/rules/naming/interface-starts-with-i.md
index 2ef6634..13417a9 100644
--- a/docs/rules/naming/interface-starts-with-i.md
+++ b/docs/rules/naming/interface-starts-with-i.md
@@ -5,8 +5,11 @@ title:       "interface-starts-with-i | Solhint"
 ---
 
 # interface-starts-with-i
+![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
 Solidity Interfaces names should start with an `I`
diff --git a/docs/rules/naming/private-vars-leading-underscore.md b/docs/rules/naming/private-vars-leading-underscore.md
index 909c43c..7366bf3 100644
--- a/docs/rules/naming/private-vars-leading-underscore.md
+++ b/docs/rules/naming/private-vars-leading-underscore.md
@@ -24,7 +24,12 @@ This rule accepts an array of options:
 ```json
 {
   "rules": {
-    "private-vars-leading-underscore": ["warn",{"strict":false}]
+    "private-vars-leading-underscore": [
+      "warn",
+      {
+        "strict": false
+      }
+    ]
   }
 }
 ```
diff --git a/docs/rules/naming/var-name-mixedcase.md b/docs/rules/naming/var-name-mixedcase.md
index 38eec98..cc1e626 100644
--- a/docs/rules/naming/var-name-mixedcase.md
+++ b/docs/rules/naming/var-name-mixedcase.md
@@ -27,7 +27,12 @@ This rule accepts an array of options:
 ```json
 {
   "rules": {
-    "var-name-mixedcase": ["warn",{"prefixForImmutables":"IMM_"}]
+    "var-name-mixedcase": [
+      "warn",
+      {
+        "prefixForImmutables": "IMM_"
+      }
+    ]
   }
 }
 ```
diff --git a/docs/rules/security/compiler-version.md b/docs/rules/security/compiler-version.md
index a80f364..fab7d63 100644
--- a/docs/rules/security/compiler-version.md
+++ b/docs/rules/security/compiler-version.md
@@ -27,7 +27,10 @@ This rule accepts an array of options:
 ```json
 {
   "rules": {
-    "compiler-version": ["error","^0.8.24"]
+    "compiler-version": [
+      "error",
+      "^0.8.24"
+    ]
   }
 }
 ```
diff --git a/docs/rules/security/func-visibility.md b/docs/rules/security/func-visibility.md
index c85eda8..c781fe8 100644
--- a/docs/rules/security/func-visibility.md
+++ b/docs/rules/security/func-visibility.md
@@ -27,7 +27,12 @@ This rule accepts an array of options:
 ```json
 {
   "rules": {
-    "func-visibility": ["warn",{"ignoreConstructors":false}]
+    "func-visibility": [
+      "warn",
+      {
+        "ignoreConstructors": false
+      }
+    ]
   }
 }
 ```
diff --git a/lib/common/statements-indent-validator.js b/lib/common/statements-indent-validator.js
index a7f3b87..8575e08 100644
--- a/lib/common/statements-indent-validator.js
+++ b/lib/common/statements-indent-validator.js
@@ -98,15 +98,15 @@ class SyntacticSequence {
   }
 
   syntaxMatch(ctx) {
-    const childs = ctx.children
+    const children = ctx.children
     const syntaxMatchers = this.items.filter((i) => i.syntaxMatch)
 
-    if (!childs || childs.length === 0 || syntaxMatchers.length !== childs.length) {
+    if (!children || children.length === 0 || syntaxMatchers.length !== children.length) {
       return false
     }
 
-    for (let i = 0; i < childs.length; i += 1) {
-      if (!syntaxMatchers[i].syntaxMatch(childs[i])) {
+    for (let i = 0; i < children.length; i += 1) {
+      if (!syntaxMatchers[i].syntaxMatch(children[i])) {
         return false
       }
     }
diff --git a/lib/config.js b/lib/config.js
index 6983683..6485818 100644
--- a/lib/config.js
+++ b/lib/config.js
@@ -53,4 +53,10 @@ module.exports = {
     const configVal = _.get(this, path)
     return _.isArray(configVal) ? configVal : defaultValue
   },
+
+  getObject(ruleName, defaultValue) {
+    const path = `rules["${ruleName}"][1]`
+    const configVal = _.get(this, path)
+    return _.isObject(configVal) ? configVal : defaultValue
+  },
 }
diff --git a/lib/rules/best-practices/function-max-lines.js b/lib/rules/best-practices/function-max-lines.js
index dca92cf..6648692 100644
--- a/lib/rules/best-practices/function-max-lines.js
+++ b/lib/rules/best-practices/function-max-lines.js
@@ -25,7 +25,7 @@ const meta = {
     ],
   },
 
-  recommended: false,
+  recommended: true,
   defaultSetup: [DEFAULT_SEVERITY, DEFAULT_MAX_LINES_COUNT],
 
   schema: {
diff --git a/lib/rules/best-practices/index.js b/lib/rules/best-practices/index.js
index c7d2f09..1644d6f 100644
--- a/lib/rules/best-practices/index.js
+++ b/lib/rules/best-practices/index.js
@@ -12,6 +12,7 @@ const NoUnusedImportsChecker = require('./no-unused-import')
 const ExplicitTypesChecker = require('./explicit-types')
 const OneContractPerFileChecker = require('./one-contract-per-file')
 const InterfaceStartsWithIChecker = require('./interface-starts-with-i')
+const UseNatspecChecker = require('./use-natspec')
 
 module.exports = function checkers(reporter, config, inputSrc, tokens) {
   return [
@@ -29,5 +30,6 @@ module.exports = function checkers(reporter, config, inputSrc, tokens) {
     new ExplicitTypesChecker(reporter, config),
     new OneContractPerFileChecker(reporter),
     new InterfaceStartsWithIChecker(reporter),
+    new UseNatspecChecker(reporter, config, tokens),
   ]
 }
diff --git a/lib/rules/best-practices/interface-starts-with-i.js b/lib/rules/best-practices/interface-starts-with-i.js
index 7c7ba49..c6b247f 100644
--- a/lib/rules/best-practices/interface-starts-with-i.js
+++ b/lib/rules/best-practices/interface-starts-with-i.js
@@ -22,7 +22,7 @@ const meta = {
     },
   },
 
-  recommended: false,
+  recommended: true,
   defaultSetup: 'warn',
   schema: null,
 }
diff --git a/lib/rules/best-practices/payable-fallback.js b/lib/rules/best-practices/payable-fallback.js
index 7e7b779..1fa665d 100644
--- a/lib/rules/best-practices/payable-fallback.js
+++ b/lib/rules/best-practices/payable-fallback.js
@@ -40,7 +40,7 @@ const meta = {
     ],
   },
 
-  recommended: true,
+  recommended: false,
   defaultSetup: 'warn',
   fixable: true,
   schema: null,
diff --git a/lib/rules/best-practices/use-natspec.js b/lib/rules/best-practices/use-natspec.js
new file mode 100644
index 0000000..e375741
--- /dev/null
+++ b/lib/rules/best-practices/use-natspec.js
@@ -0,0 +1,435 @@
+const _ = require('lodash')
+const BaseChecker = require('../base-checker')
+const { severityDescription } = require('../../doc/utils')
+
+const ruleId = 'use-natspec'
+
+const DEFAULT_SEVERITY = 'warn'
+const VALID_TAGS = new Set(['title', 'author', 'notice', 'param', 'return', 'inheritdoc'])
+const DEFAULT_CONFIG = {
+  title: { enabled: true, ignore: {} },
+  author: { enabled: true, ignore: {} },
+  notice: { enabled: true, ignore: {} },
+  param: { enabled: true, ignore: {} },
+  return: { enabled: true, ignore: {} },
+}
+
+const NATSPEC_TARGETS = {
+  contract: ['title', 'author', 'notice'],
+  library: ['title', 'author', 'notice'],
+  interface: ['title', 'author', 'notice'],
+  function: ['notice', 'param', 'return'],
+  event: ['notice', 'param'],
+  variable: ['notice'],
+}
+
+const META_SCHEMA = {
+  type: 'object',
+  properties: {
+    title: {
+      type: 'object',
+      properties: {
+        enabled: { type: 'boolean' },
+        ignore: {
+          type: 'object',
+          patternProperties: {
+            '.*': {
+              type: 'array',
+              items: { type: 'string' },
+            },
+          },
+          additionalProperties: false,
+        },
+      },
+      required: ['enabled'],
+      additionalProperties: false,
+    },
+    author: {
+      type: 'object',
+      properties: {
+        enabled: { type: 'boolean' },
+        ignore: {
+          type: 'object',
+          patternProperties: {
+            '.*': {
+              type: 'array',
+              items: { type: 'string' },
+            },
+          },
+          additionalProperties: false,
+        },
+      },
+      required: ['enabled'],
+      additionalProperties: false,
+    },
+    notice: {
+      type: 'object',
+      properties: {
+        enabled: { type: 'boolean' },
+        ignore: {
+          type: 'object',
+          patternProperties: {
+            '.*': {
+              type: 'array',
+              items: { type: 'string' },
+            },
+          },
+          additionalProperties: false,
+        },
+      },
+      required: ['enabled'],
+      additionalProperties: false,
+    },
+    param: {
+      type: 'object',
+      properties: {
+        enabled: { type: 'boolean' },
+        ignore: {
+          type: 'object',
+          patternProperties: {
+            '.*': {
+              type: 'array',
+              items: { type: 'string' },
+            },
+          },
+          additionalProperties: false,
+        },
+      },
+      required: ['enabled'],
+      additionalProperties: false,
+    },
+    return: {
+      type: 'object',
+      properties: {
+        enabled: { type: 'boolean' },
+        ignore: {
+          type: 'object',
+          patternProperties: {
+            '.*': {
+              type: 'array',
+              items: { type: 'string' },
+            },
+          },
+          additionalProperties: false,
+        },
+      },
+      required: ['enabled'],
+      additionalProperties: false,
+    },
+  },
+  additionalProperties: false,
+}
+
+const meta = {
+  type: 'best-practices',
+
+  docs: {
+    description: 'Enforces the presence and correctness of NatSpec tags.',
+    category: 'Best Practices Rules',
+    options: [
+      {
+        description: severityDescription,
+        default: DEFAULT_SEVERITY,
+      },
+      {
+        description: 'A JSON object with natspec properties. See EXAMPLE CONFIG section below.',
+        default: 'Check EXAMPLE CONFIG',
+      },
+    ],
+
+    examples: {
+      good: [
+        {
+          description: 'Contract with valid NatSpec',
+          code: `
+                /// @title Token contract
+                /// @author Me
+                /// @notice This contract handles tokens
+                contract Token {
+                  /// @notice Transfers tokens
+                  /// @param to The recipient address
+                  /// @param amount The amount to transfer
+                  /// @return success Whether the transfer succeeded
+                  function transfer(address to, uint256 amount) public returns (bool success) {
+                    return true;
+                  }
+                }
+              `,
+        },
+        {
+          description:
+            'You can disable specific tags globally or by type/name using the `ignore` option in the config. For example:',
+          code: `{
+                "use-natspec": [
+                  "warn",
+                  {
+                    "title": {
+                      "enabled": true,
+                      "ignore": {
+                        "contract": ["MyContract"],
+                        "*": ["LegacyContract"]
+                      }
+                    },
+                    "param": { "enabled": false }
+                  }
+                ]
+              }`,
+        },
+        {
+          description: 'The default configuration enables all checks with no ignore rules:',
+          code: `{
+                "title": { "enabled": true, "ignore": {} },
+                "author": { "enabled": true, "ignore": {} },
+                "notice": { "enabled": true, "ignore": {} },
+                "param": { "enabled": true, "ignore": {} },
+                "return": { "enabled": true, "ignore": {} }
+              }`,
+        },
+      ],
+      bad: [
+        {
+          description: 'Missing @notice and incorrect @param and @return tags',
+          code: `
+                /// @title Token contract
+                contract Token {
+                  /// @param wrongParam Not matching actual parameter
+                  function transfer(address to) public returns (bool) {
+                    return true;
+                  }
+                }
+                `,
+        },
+      ],
+    },
+    notes: [
+      {
+        note: 'If a function or return value has unnamed parameters (e.g. `function foo(uint256)`), the rule only checks the number of `@param` or `@return` tags, not their names.',
+      },
+      {
+        note: 'If a function or variable has `@inheritdoc`, the rule skips the validation.',
+      },
+      {
+        note: 'The rule supports both `///` and `/** */` style NatSpec comments.',
+      },
+      {
+        note: 'If a custom config is provided, it is merged with the default config. Only the overridden fields need to be specified.',
+      },
+    ],
+  },
+
+  recommended: true,
+  defaultSetup: ['warn', DEFAULT_CONFIG],
+
+  schema: META_SCHEMA,
+}
+
+class UseNatspecChecker extends BaseChecker {
+  constructor(reporter, config, tokens) {
+    super(reporter, ruleId, meta)
+    if (!this.enabled) return // if the rule is disabled, do nothing
+
+    this.tokens = tokens
+    const userConfig = config?.getObject(ruleId) || {}
+    this.tagConfig = _.merge({}, DEFAULT_CONFIG, userConfig)
+  }
+
+  ContractDefinition(node) {
+    this.checkNode(node, 'contract')
+  }
+
+  LibraryDefinition(node) {
+    this.checkNode(node, 'library')
+  }
+
+  InterfaceDefinition(node) {
+    this.checkNode(node, 'interface')
+  }
+
+  FunctionDefinition(node) {
+    const visibility = node.visibility || 'default'
+    const isPublicLike = visibility === 'public' || visibility === 'external'
+    const tags = [...NATSPEC_TARGETS.function]
+
+    if (!hasParameters(node)) tags.splice(tags.indexOf('param'), 1)
+    if (!hasReturn(node)) tags.splice(tags.indexOf('return'), 1)
+
+    this.checkNode(node, 'function', tags, isPublicLike)
+  }
+
+  EventDefinition(node) {
+    const tags = [...NATSPEC_TARGETS.event]
+    if (!hasParameters(node)) tags.splice(tags.indexOf('param'), 1)
+    this.checkNode(node, 'event', tags)
+  }
+
+  VariableDeclaration(node) {
+    if (!node.isStateVar || node.visibility !== 'public') return
+    this.checkNode(node, 'variable', NATSPEC_TARGETS.variable, true)
+  }
+
+  checkNode(node, type, overrideTags, requireNoticeIfPublic = false) {
+    const name = node.name || (node.id && node.id.name) || '<anonymous>'
+    const tagsRequired = overrideTags || NATSPEC_TARGETS[type] || []
+    const startOffset = node.range[0]
+    const comments = getLeadingNatSpecComments(startOffset, this.tokens)
+    const tags = extractNatSpecTags(comments)
+
+    // console.log('\n>>> CHECKING', type, name)
+    // console.log('COMMENTS:', comments)
+    // console.log('TAGS:', tags)
+
+    const hasInheritdoc = tags.includes('inheritdoc')
+    if (requireNoticeIfPublic && hasInheritdoc) return
+
+    for (const tag of tagsRequired) {
+      const rule = this.tagConfig[tag]
+      if (!rule?.enabled) continue
+      if (!shouldValidateTag(tag, type, name, this.tagConfig)) continue
+
+      if (!tags.includes(tag)) {
+        this.reporter.warn(node, ruleId, `Missing @${tag} tag in ${type} '${name}'`)
+      }
+
+      if (tag === 'param') {
+        const docParams = extractTagNames(comments, 'param')
+        const solidityParams = node.parameters || []
+
+        const namedParams = solidityParams
+          .map((p) => p.name)
+          .filter((name) => typeof name === 'string' && name.length > 0)
+
+        const allParamsHaveNames = namedParams.length === solidityParams.length
+
+        if (allParamsHaveNames) {
+          if (namedParams.length !== docParams.length || !arraysEqual(namedParams, docParams)) {
+            this.reporter.warn(
+              node,
+              ruleId,
+              `Mismatch in @param names for ${type} '${name}'. Expected: [${namedParams.join(
+                ', '
+              )}], Found: [${docParams.join(', ')}]`
+            )
+          }
+        } else if (solidityParams.length !== docParams.length) {
+          this.reporter.warn(
+            node,
+            ruleId,
+            `Mismatch in @param count for ${type} '${name}'. Expected: ${solidityParams.length}, Found: ${docParams.length}`
+          )
+        }
+      }
+
+      if (tag === 'return') {
+        const docReturns = extractTagNames(comments, 'return')
+        const solidityReturns = node.returnParameters || []
+
+        const namedReturns = solidityReturns
+          .map((p) => p.name)
+          .filter((name) => typeof name === 'string' && name.length > 0)
+
+        const allReturnsHaveNames = namedReturns.length === solidityReturns.length
+
+        if (allReturnsHaveNames) {
+          if (namedReturns.length !== docReturns.length || !arraysEqual(namedReturns, docReturns)) {
+            this.reporter.warn(
+              node,
+              ruleId,
+              `Mismatch in @return names for ${type} '${name}'. Expected: [${namedReturns.join(
+                ', '
+              )}], Found: [${docReturns.join(', ')}]`
+            )
+          }
+        } else if (solidityReturns.length !== docReturns.length) {
+          this.reporter.warn(
+            node,
+            ruleId,
+            `Mismatch in @return count for ${type} '${name}'. Expected: ${solidityReturns.length}, Found: ${docReturns.length}`
+          )
+        }
+      }
+    }
+  }
+}
+
+function getLeadingNatSpecComments(startOffset, tokens) {
+  const comments = []
+
+  for (let i = tokens.length - 1; i >= 0; i--) {
+    const token = tokens[i]
+
+    if (token.range[1] > startOffset) continue
+
+    const value = token.value?.trim()
+    if (
+      typeof value === 'string' &&
+      (value.startsWith('///') || value.startsWith('/**')) &&
+      value.includes('@') &&
+      hasAnyNatSpecTag(value)
+    ) {
+      comments.unshift(value)
+    }
+
+    // Si encontramos código real (no comentario ni puntuación), cortamos
+    if (!value || (!value.startsWith('///') && !value.startsWith('/**'))) {
+      if (token.type !== 'Punctuator') break
+    }
+  }
+
+  return comments
+}
+
+function extractTagNames(comments, tag) {
+  const names = []
+  const regex = new RegExp(`@${tag}\\s+(\\w+)`, 'g')
+
+  for (const comment of comments) {
+    let match = regex.exec(comment)
+    while (match !== null) {
+      names.push(match[1])
+      match = regex.exec(comment)
+    }
+  }
+
+  return names
+}
+
+function arraysEqual(arr1, arr2) {
+  if (arr1.length !== arr2.length) return false
+  return arr1.every((v, i) => v === arr2[i])
+}
+
+function hasAnyNatSpecTag(commentValue) {
+  for (const [, tag] of commentValue.matchAll(/@([a-zA-Z0-9_]+)/g)) {
+    if (VALID_TAGS.has(tag)) return true
+  }
+  return false
+}
+
+function extractNatSpecTags(comments) {
+  const tags = new Set()
+  for (const comment of comments) {
+    const matches = comment.matchAll(/@([a-zA-Z0-9_]+)/g)
+    for (const [, tag] of matches) {
+      tags.add(tag)
+    }
+  }
+  return [...tags]
+}
+
+function shouldValidateTag(tag, type, name, config) {
+  const tagConf = config[tag]
+  if (!tagConf || !tagConf.enabled) return false
+  const ignoreByType = tagConf.ignore?.[type] || []
+  const ignoreGlobal = tagConf.ignore?.['*'] || []
+  return !ignoreByType.includes(name) && !ignoreGlobal.includes(name)
+}
+
+function hasParameters(node) {
+  return node.parameters && node.parameters.length > 0
+}
+
+function hasReturn(node) {
+  return node.returnParameters && node.returnParameters.length > 0
+}
+
+module.exports = UseNatspecChecker
diff --git a/lib/rules/deprecations/constructor-syntax.js b/lib/rules/deprecations/constructor-syntax.js
index f2841ae..74defbb 100644
--- a/lib/rules/deprecations/constructor-syntax.js
+++ b/lib/rules/deprecations/constructor-syntax.js
@@ -9,7 +9,7 @@ const meta = {
     category: 'Best Practices Rules',
   },
 
-  recommended: false,
+  recommended: true,
   defaultSetup: 'warn',
 
   schema: null,
diff --git a/lib/rules/gas-consumption/gas-calldata-parameters.js b/lib/rules/gas-consumption/gas-calldata-parameters.js
index 4525bc8..5ece44c 100644
--- a/lib/rules/gas-consumption/gas-calldata-parameters.js
+++ b/lib/rules/gas-consumption/gas-calldata-parameters.js
@@ -38,7 +38,7 @@ const meta = {
     ],
   },
 
-  recommended: false,
+  recommended: true,
   defaultSetup: 'warn',
 
   schema: null,
diff --git a/lib/rules/gas-consumption/gas-increment-by-one.js b/lib/rules/gas-consumption/gas-increment-by-one.js
index 2fe38ee..8407506 100644
--- a/lib/rules/gas-consumption/gas-increment-by-one.js
+++ b/lib/rules/gas-consumption/gas-increment-by-one.js
@@ -9,9 +9,6 @@ const meta = {
     description: 'Suggest increments by one, like this ++i instead of other type',
     category: 'Gas Consumption Rules',
     notes: [
-      {
-        note: 'This rule only works for expressions like this: [ j = j + 1 ] but will fail if the code is written like this: [ j = 1 + j ]',
-      },
       {
         note: '[source 1](https://coinsbench.com/comprehensive-guide-tips-and-tricks-for-gas-optimization-in-solidity-5380db734404) of the rule initiative (Incrementing/Decrementing By 1)',
       },
@@ -22,7 +19,7 @@ const meta = {
   },
 
   
-  recommended: false,
+  recommended: true,
   defaultSetup: 'warn',
 
   schema: null,
@@ -57,25 +54,43 @@ class GasIncrementByOne extends BaseChecker {
   }
 
   isVariableIncrementOrDecrement(left, right, operator) {
-    const leftVar = this.extractVariableName(left)
-    let rightValue
+  const leftVar = this.extractVariableName(left)
 
-    // assignment and operation
-    if (operator === '+=' || operator === '-=') {
-      rightValue = right.type === 'NumberLiteral' && parseInt(right.number) === 1
-      return { result: leftVar && rightValue, varName: leftVar }
-    }
-    // regular assignment
-    else if (operator === '=') {
-      const rightVar = this.extractVariableName(right.left)
-      rightValue =
-        right.right && right.right.type === 'NumberLiteral' && parseInt(right.right.number) === 1
-      return { result: leftVar === rightVar && rightValue, varName: leftVar }
-    }
+  // assignment and operation
+  if (operator === '+=' || operator === '-=') {
+    const rightIsOne = right.type === 'NumberLiteral' && parseInt(right.number) === 1
+    return { result: leftVar && rightIsOne, varName: leftVar }
+  }
+
+  // regular assignment
+  if (operator === '=') {
+    if (right.type !== 'BinaryOperation') return { result: false }
+
+    const rightLeftVar = this.extractVariableName(right.left)
+    const rightRightVar = this.extractVariableName(right.right)
 
-    return false
+    const isLeftOne = right.left.type === 'NumberLiteral' && parseInt(right.left.number) === 1
+    const isRightOne = right.right.type === 'NumberLiteral' && parseInt(right.right.number) === 1
+
+    const isIncrement =
+      right.operator === '+' &&
+      (
+        (leftVar === rightLeftVar && isRightOne) || 
+        (leftVar === rightRightVar && isLeftOne)
+      )
+
+    const isDecrement =
+      right.operator === '-' &&
+      leftVar === rightLeftVar &&
+      isRightOne
+
+    return { result: isIncrement || isDecrement, varName: leftVar }
   }
 
+  return { result: false }
+}
+
+
   extractVariableName(node) {
     if (node.type === 'Identifier') {
       return node.name
@@ -96,53 +111,4 @@ class GasIncrementByOne extends BaseChecker {
   }
 }
 
-module.exports = GasIncrementByOne
-
-// SourceUnit(node) {
-//   this.findVariableUpdates(node)
-// }
-
-// findVariableUpdates(node) {
-//   if (node === null || typeof node !== 'object') {
-//     return
-//   }
-
-//   // Check for assignment in a function body
-//   if (node.type === 'FunctionDefinition') {
-//     this.findVariableUpdates(node.body)
-//     return
-//   }
-
-//   if (node.type === 'ExpressionStatement' && node.expression.type === 'BinaryOperation') {
-//     const expr = node.expression
-//     if (expr.operator === '=' || expr.operator === '+=' || expr.operator === '-=') {
-//       if (this.isVariableIncrementOrDecrement(expr.left, expr.right, expr.operator)) {
-//         this.reportError(node, this.extractVariableName(expr.left))
-//       }
-//     }
-//   }
-
-//   Object.values(node).forEach((value) => {
-//     if (typeof value === 'object') {
-//       this.findVariableUpdates(value)
-//     }
-//   })
-// }
-
-// isVariableIncrementOrDecrement(left, right, operator) {
-//   const leftVar = this.extractVariableName(left)
-//   let rightVar, rightValue
-
-//   if (operator === '+=' || operator === '-=') {
-//     // For compound assignment, the operation is directly on the variable
-//     rightValue = right.type === 'NumberLiteral' && parseInt(right.number) === 1
-//     return leftVar && rightValue
-//   } else if (operator === '=') {
-//     rightVar = this.extractVariableName(right.left)
-//     rightValue =
-//       right.right && right.right.type === 'NumberLiteral' && parseInt(right.right.number) === 1
-//     return leftVar === rightVar && rightValue
-//   }
-
-//   return false
-// }
+module.exports = GasIncrementByOne
\ No newline at end of file
diff --git a/lib/rules/gas-consumption/gas-indexed-events.js b/lib/rules/gas-consumption/gas-indexed-events.js
index 8e40588..ddd1615 100644
--- a/lib/rules/gas-consumption/gas-indexed-events.js
+++ b/lib/rules/gas-consumption/gas-indexed-events.js
@@ -15,7 +15,7 @@ const meta = {
     ],
   },
 
-  recommended: false,
+  recommended: true,
   defaultSetup: 'warn',
 
   schema: null,
diff --git a/lib/rules/gas-consumption/gas-small-strings.js b/lib/rules/gas-consumption/gas-small-strings.js
index a6990e3..6be5d04 100644
--- a/lib/rules/gas-consumption/gas-small-strings.js
+++ b/lib/rules/gas-consumption/gas-small-strings.js
@@ -5,7 +5,7 @@ const meta = {
   type: 'gas-consumption',
 
   docs: {
-    description: 'Keep strings smaller than 32 bytes',
+    description: 'Keep strings smaller than 32 bytes. Promote the use of custom errors',
     category: 'Gas Consumption Rules',
     notes: [
       {
@@ -14,7 +14,7 @@ const meta = {
     ],
   },
 
-  recommended: false,
+  recommended: true,
   defaultSetup: 'warn',
 
   schema: null,
diff --git a/lib/rules/gas-consumption/gas-strict-inequalities.js b/lib/rules/gas-consumption/gas-strict-inequalities.js
index ecb4357..23fa9ff 100644
--- a/lib/rules/gas-consumption/gas-strict-inequalities.js
+++ b/lib/rules/gas-consumption/gas-strict-inequalities.js
@@ -20,7 +20,7 @@ const meta = {
     ],
   },
 
-  recommended: false,
+  recommended: true,
   defaultSetup: 'warn',
 
   schema: null,
diff --git a/lib/rules/gas-consumption/gas-struct-packing.js b/lib/rules/gas-consumption/gas-struct-packing.js
index deaefae..d0bcdd4 100644
--- a/lib/rules/gas-consumption/gas-struct-packing.js
+++ b/lib/rules/gas-consumption/gas-struct-packing.js
@@ -24,8 +24,7 @@ const meta = {
     ],
   },
 
-  
-  recommended: false,
+  recommended: true,
   defaultSetup: 'warn',
 
   schema: null,
@@ -45,7 +44,7 @@ class GasStructPacking extends BaseChecker {
 
   isInefficientlyPacked(structNode) {
     if (!structNode || !Array.isArray(structNode.members) || structNode.members.length <= 1) {
-    // if (!structNode || !Array.isArray(structNode.members)) {
+      // if (!structNode || !Array.isArray(structNode.members)) {
       return false // Early return for structs with 1 or no members
     }
 
@@ -59,73 +58,42 @@ class GasStructPacking extends BaseChecker {
     return canBeImproved
   }
 
-  // Function to calculate the optimal slots needed for given members
-  calculateOptimalSlots(arr) {
-    let totalSize = 0
-    arr.forEach(({ size }) => {
-      totalSize += size
-    })
-    return Math.ceil(totalSize / 32)
-  }
-
   calculateCurrentSlotsUsed(members) {
-    let slotsUsed = 0;
-    let currentSlotSpace = 0;
-  
-    members.forEach(member => {
+    let slotsUsed = 0
+    let currentSlotSpace = 0
+
+    members.forEach((member) => {
       if (member.size === 32) {
         if (currentSlotSpace > 0) {
-          slotsUsed += 1; // Finish the current slot if it was partially filled
-          currentSlotSpace = 0;
+          slotsUsed += 1 // Finish the current slot if it was partially filled
+          currentSlotSpace = 0
         }
-        slotsUsed += 1; // This member occupies a full slot
+        slotsUsed += 1 // This member occupies a full slot
       } else {
         if (currentSlotSpace + member.size > 32) {
-          slotsUsed += 1; // Move to the next slot if adding this member exceeds the current slot
-          currentSlotSpace = member.size; // Start filling the next slot
+          slotsUsed += 1 // Move to the next slot if adding this member exceeds the current slot
+          currentSlotSpace = member.size // Start filling the next slot
         } else {
-          currentSlotSpace += member.size; // Add to the current slot space
+          currentSlotSpace += member.size // Add to the current slot space
         }
       }
-    });
-  
+    })
+
     // If there's any space used in the current slot after looping, it means another slot is partially filled
     if (currentSlotSpace > 0) {
-      slotsUsed += 1;
+      slotsUsed += 1
     }
-  
-    return slotsUsed;
+
+    return slotsUsed
   }
 
-  
   analyzePacking(members) {
-    // Separate members into large and small for analysis
-    // const largeMembers = members.filter((member) => member.size === 32)
-    const smallMembers = members.filter((member) => member.size < 32)
-
-    // Sort small members by size, descending, to optimize packing
-    smallMembers.sort((a, b) => b.size - a.size)
-
-    // Initial slots count: one slot per large member
-    let currentSlots = this.calculateCurrentSlotsUsed(members)
-
-    // Track remaining space in the current slot
-    let remainingSpace = 32
-    smallMembers.forEach((member) => {
-      if (member.size <= remainingSpace) {
-        // If the member fits in the current slot, subtract its size from the remaining space
-        remainingSpace -= member.size
-      } else {
-        // If not, start a new slot and adjust remaining space
-        remainingSpace = 32 - member.size
-      }
-    })
-
-    // Calculate optimal slots needed if perfectly packed
-    const optimalSlots = this.calculateOptimalSlots(members)
+    // calculateCurrentSlotsUsed
+    const currentSlots = this.calculateCurrentSlotsUsed(members)
 
-    // console.log(`\n\nCurrent slot usage: ${currentSlots}`)
-    // console.log(`Optimal (minimum possible) slot usage: ${optimalSlots}`)
+    // calculate optimal slots by rounding up the total size of members in descending order
+    const sortedMembers = [...members].sort((a, b) => b.size - a.size)
+    const optimalSlots = this.calculateCurrentSlotsUsed(sortedMembers)
 
     return currentSlots > optimalSlots
   }
diff --git a/lib/rules/miscellaneous/duplicated-imports.js b/lib/rules/miscellaneous/duplicated-imports.js
index 91da467..62fe5ae 100644
--- a/lib/rules/miscellaneous/duplicated-imports.js
+++ b/lib/rules/miscellaneous/duplicated-imports.js
@@ -33,7 +33,7 @@ const meta = {
     ],
   },
 
-  recommended: false,
+  recommended: true,
   defaultSetup: 'warn',
   fixable: true,
   schema: null,
diff --git a/package.json b/package.json
index 9a98df7..cc4fd42 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
 {
   "name": "solhint",
-  "version": "5.2.0",
+  "version": "6.0.0",
   "description": "Solidity Code Linter",
   "main": "lib/index.js",
   "keywords": [
diff --git a/scripts/generate-rule-docs.js b/scripts/generate-rule-docs.js
index 5501c21..1012287 100755
--- a/scripts/generate-rule-docs.js
+++ b/scripts/generate-rule-docs.js
@@ -158,12 +158,14 @@ ${table(optionsTable)}
 }
 
 function loadExampleConfig(rule) {
-  return `\`\`\`json
-{
-  "rules": {
-    "${rule.ruleId}": ${JSON.stringify(rule.meta.defaultSetup)}
+  const fullConfig = {
+    rules: {
+      [rule.ruleId]: rule.meta.defaultSetup,
+    },
   }
-}
+
+  return `\`\`\`json
+${JSON.stringify(fullConfig, null, 2)}
 \`\`\`
 `
 }


Test patch:
diff --git a/docs/rules/naming/foundry-test-functions.md b/docs/rules/naming/foundry-test-functions.md
index 37a9b01..1fa0272 100644
--- a/docs/rules/naming/foundry-test-functions.md
+++ b/docs/rules/naming/foundry-test-functions.md
@@ -24,7 +24,12 @@ This rule accepts an array of options:
 ```json
 {
   "rules": {
-    "foundry-test-functions": ["warn",["setUp"]]
+    "foundry-test-functions": [
+      "warn",
+      [
+        "setUp"
+      ]
+    ]
   }
 }
 ```
diff --git a/e2e/05-max-warnings/.solhint.json b/e2e/05-max-warnings/.solhint.json
index 3b8ee84..28e109d 100644
--- a/e2e/05-max-warnings/.solhint.json
+++ b/e2e/05-max-warnings/.solhint.json
@@ -1,3 +1,6 @@
 {
-  "extends": "solhint:all"
+  "extends": "solhint:all",
+  "rules": {
+    "use-natspec": "off"
+  }
 }
diff --git a/e2e/06-formatters/.solhint.json b/e2e/06-formatters/.solhint.json
index 3b8ee84..28e109d 100644
--- a/e2e/06-formatters/.solhint.json
+++ b/e2e/06-formatters/.solhint.json
@@ -1,3 +1,6 @@
 {
-  "extends": "solhint:all"
+  "extends": "solhint:all",
+  "rules": {
+    "use-natspec": "off"
+  }
 }
diff --git a/test/common/config-validator.js b/test/common/config-validator.js
index 97abcc2..710e581 100644
--- a/test/common/config-validator.js
+++ b/test/common/config-validator.js
@@ -155,6 +155,7 @@ describe('Better errors addition + rule disable on error', () => {
       .getCalls()
       .map((c) => c.args[0])
       .join('\n')
+
     assert.ok(
       logged.includes("invalid configuration for rule 'compiler-version'"),
       `Expected a warning for compiler-version but got:\n${logged}`
@@ -178,6 +179,7 @@ describe('Better errors addition + rule disable on error', () => {
       .getCalls()
       .map((c) => c.args[0])
       .join('\n')
+
     assert.ok(
       logged.includes("invalid configuration for rule 'max-line-length'"),
       `Expected a warning for max-line-length but got:\n${logged}`
@@ -201,6 +203,7 @@ describe('Better errors addition + rule disable on error', () => {
       .getCalls()
       .map((c) => c.args[0])
       .join('\n')
+
     assert.ok(
       logged.includes("invalid configuration for rule 'interface-starts-with-i'"),
       `Expected a warning for interface-starts-with-i but got:\n${logged}`
@@ -224,6 +227,7 @@ describe('Better errors addition + rule disable on error', () => {
       .getCalls()
       .map((c) => c.args[0])
       .join('\n')
+
     assert.ok(
       logged.includes("invalid configuration for rule 'interface-starts-with-i'"),
       `Expected a warning for interface-starts-with-i but got:\n${logged}`
@@ -247,6 +251,7 @@ describe('Better errors addition + rule disable on error', () => {
       .getCalls()
       .map((c) => c.args[0])
       .join('\n')
+
     assert.ok(
       logged.includes("[solhint] Warning: Rule 'interface-sta' doesn't exist"),
       `Expected a warning for interface-sta but got:\n${logged}`
@@ -271,6 +276,7 @@ describe('Better errors addition + rule disable on error', () => {
       .getCalls()
       .map((c) => c.args[0])
       .join('\n')
+
     assert.ok(
       logged.includes("invalid configuration for rule 'interface-starts-with-i'"),
       `Expected a warning for interface-starts-with-i but got:\n${logged}`
@@ -294,6 +300,7 @@ describe('Better errors addition + rule disable on error', () => {
       .getCalls()
       .map((c) => c.args[0])
       .join('\n')
+
     assert.ok(
       logged.includes("invalid configuration for rule 'interface-starts-with-i'"),
       `Expected a warning for interface-starts-with-i but got:\n${logged}`
@@ -317,6 +324,7 @@ describe('Better errors addition + rule disable on error', () => {
       .getCalls()
       .map((c) => c.args[0])
       .join('\n')
+
     assert.ok(
       logged.includes("invalid configuration for rule 'reason-string'"),
       `Expected a warning for reason-string but got:\n${logged}`
@@ -340,6 +348,7 @@ describe('Better errors addition + rule disable on error', () => {
       .getCalls()
       .map((c) => c.args[0])
       .join('\n')
+
     assert.ok(
       logged.includes("invalid configuration for rule 'immutable-vars-naming'"),
       `Expected a warning for immutable-vars-naming but got:\n${logged}`
@@ -363,6 +372,7 @@ describe('Better errors addition + rule disable on error', () => {
       .getCalls()
       .map((c) => c.args[0])
       .join('\n')
+
     assert.ok(
       logged.includes("invalid configuration for rule 'immutable-vars-naming'"),
       `Expected a warning for immutable-vars-naming but got:\n${logged}`
@@ -386,6 +396,7 @@ describe('Better errors addition + rule disable on error', () => {
       .getCalls()
       .map((c) => c.args[0])
       .join('\n')
+
     assert.ok(
       logged.includes("invalid configuration for rule 'explicit-types'"),
       `Expected a warning for explicit-types but got:\n${logged}`
@@ -409,6 +420,7 @@ describe('Better errors addition + rule disable on error', () => {
       .getCalls()
       .map((c) => c.args[0])
       .join('\n')
+
     assert.ok(
       logged.includes("invalid configuration for rule 'quotes'"),
       `Expected a warning for quotes but got:\n${logged}`
@@ -432,6 +444,7 @@ describe('Better errors addition + rule disable on error', () => {
       .getCalls()
       .map((c) => c.args[0])
       .join('\n')
+
     assert.ok(
       logged.includes("invalid configuration for rule 'foundry-test-functions'"),
       `Expected a warning for foundry-test-functions but got:\n${logged}`
@@ -455,6 +468,7 @@ describe('Better errors addition + rule disable on error', () => {
       .getCalls()
       .map((c) => c.args[0])
       .join('\n')
+
     assert.ok(
       logged.includes("invalid configuration for rule 'foundry-test-functions'"),
       `Expected a warning for foundry-test-functions but got:\n${logged}`
@@ -478,6 +492,7 @@ describe('Better errors addition + rule disable on error', () => {
       .getCalls()
       .map((c) => c.args[0])
       .join('\n')
+
     assert.ok(
       logged.includes("invalid configuration for rule 'foundry-test-functions'"),
       `Expected a warning for foundry-test-functions but got:\n${logged}`
@@ -488,125 +503,81 @@ describe('Better errors addition + rule disable on error', () => {
     sinon.assert.notCalled(reportWarnSpy)
   })
 
-  // array with string as config
-  // array with number as config
-  // no array just severity
-  // no array, empty severity
-  // misspelled rule name
-  // additional config value in array
-  // config error object instead of array
-  // config error object with missing key
-  // config error object with no value type
-  // config error object with wrong value type
-
-  // it('Invalid CFG - not execute: reason-string when no property is defined)', () => {
-  //   const report = linter.processStr(dummyCode, {
-  //     rules: { 'interface-starts-with-i': { severity: 'warn' } },
-  //   })
-
-  //   assert.equal(report.errorCount, 0)
-  //   assert.equal(report.warningCount, 0)
-  //   assert.deepEqual(report.messages, [])
-
-  //   const logged = warnSpy
-  //     .getCalls()
-  //     .map((c) => c.args[0])
-  //     .join('\n')
-  //   assert.ok(
-  //     logged.includes("invalid configuration for rule 'interface-starts-with-i'"),
-  //     `Expected a warning for interface-starts-with-i but got:\n${logged}`
-  //   )
-
-  //   assert.ok(warnSpy.called, 'console.warn should have been called')
-  //   sinon.assert.notCalled(reportErrorSpy)
-  //   sinon.assert.notCalled(reportWarnSpy)
-  // })
-
-  // it('Invalid CFG - not execute: explicit-types expects two predefined values)', () => {
-  //   const code = contractWith('uint256 public constant SNAKE_CASE = 1;')
-
-  //   const report = linter.processStr(code, {
-  //     rules: { 'explicit-types': ['error', 'implicito'] },
-  //   })
-  //   assert.equal(report.errorCount, 0)
-  //   assert.equal(report.warningCount, 0)
-  //   assert.deepEqual(report.messages, [])
-  // })
-
-  // it('should run rule with valid config (compiler-version string)', () => {
-  //   const report = linter.processStr(dummyCode, {
-  //     rules: { 'compiler-version': ['error', '^0.8.24'] },
-  //   })
-  //   assert.equal(report.warningCount, 0)
-  //   assert.ok(
-  //     warnMessages.length === 0,
-  //     `Did not expect warnings, but got:\n${warnMessages.join('\n')}`
-  //   )
-  // })
-
-  // it('should not run rule when config is invalid (reason-string expects number)', () => {
-  //   const report = linter.processStr(dummyCode, {
-  //     rules: { 'reason-string': ['warn', { maxLength: 'not-a-number' }] },
-  //   })
-  //   assert.equal(report.errorCount, 0)
-  //   assert.equal(report.warningCount, 0)
-  //   assert.deepEqual(report.messages, [])
-  //   assert.ok(
-  //     findConfigWarning('reason-string'),
-  //     `Expected config warning for reason-string. Got:\n${warnMessages.join('\n')}`
-  //   )
-  //   assert.ok(
-  //     warnMessages.some((msg) => msg.includes('→ config[1].maxLength: should be integer')),
-  //     `Expected fallback error includes "→ config[1].maxLength: should be integer", got:\n${warnMessages.join(
-  //       '\n'
-  //     )}`
-  //   )
-  // })
-
-  // it('should run rule with valid config (reason-string with number)', () => {
-  //   const report = linter.processStr(dummyCode, {
-  //     rules: { 'reason-string': ['warn', { maxLength: 32 }] },
-  //   })
-  //   assert.equal(report.warningCount, 0)
-  //   assert.ok(
-  //     warnMessages.length === 0,
-  //     `Did not expect warnings, but got:\n${warnMessages.join('\n')}`
-  //   )
-  // })
-
-  // it('should run rule with primitive config (code-complexity with number)', () => {
-  //   const report = linter.processStr(
-  //     contractWith('function a(uint x) public { if (x == 1) { if (x > 2) {} } }'),
-  //     {
-  //       rules: { 'code-complexity': ['warn', 1] },
-  //     }
-  //   )
-  //   assert(report.warningCount >= 1)
-  //   assert(report.messages.some((i) => i.ruleId === 'code-complexity'))
-  // })
-
-  // it('should run rule with array config (import-path-check)', () => {
-  //   const report = linter.processStr("import '../X.sol';", {
-  //     rules: { 'import-path-check': ['warn', ['[~dependenciesPath]']] },
-  //   })
-  //   assert.equal(report.errorCount, 0)
-  //   assert(report.warningCount >= 0)
-  //   assert.ok(
-  //     warnMessages.length === 0,
-  //     `Did not expect warnings, but got:\n${warnMessages.join('\n')}`
-  //   )
-  // })
-
-  // it('should warn if rule level is invalid', () => {
-  //   const report = linter.processStr(dummyCode, {
-  //     rules: { 'no-console': 'maybe' },
-  //   })
-  //   assert.equal(report.errorCount, 0)
-  //   assert.equal(report.warningCount, 0)
-  //   assert.deepEqual(report.messages, [])
-  //   assert.ok(
-  //     warnMessages.some((msg) => msg.includes("rule 'no-console' level is 'maybe'")),
-  //     `Expected warning for invalid severity, got:\n${warnMessages.join('\n')}`
-  //   )
-  // })
+  it('Invalid CFG - use-natspec with wrong type for "enabled"', () => {
+    const config = {
+      title: {
+        enabled: 'yes', // should be boolean
+      },
+    }
+
+    const report = linter.processStr(dummyCode, {
+      rules: { 'use-natspec': ['warn', config] },
+    })
+
+    assert.equal(report.errorCount, 0)
+    assert.equal(report.warningCount, 0)
+    assert.deepEqual(report.messages, [])
+
+    const logged = warnSpy
+      .getCalls()
+      .map((c) => c.args[0])
+      .join('\n')
+
+    assert.ok(logged.includes("invalid configuration for rule 'use-natspec'"), logged)
+    assert.ok(warnSpy.called)
+    sinon.assert.notCalled(reportErrorSpy)
+    sinon.assert.notCalled(reportWarnSpy)
+  })
+
+  it('Invalid CFG - use-natspec with ignore as non-object', () => {
+    const config = {
+      title: {
+        enabled: true,
+        ignore: 'contractName', // should be an object
+      },
+    }
+
+    const report = linter.processStr(dummyCode, {
+      rules: { 'use-natspec': ['warn', config] },
+    })
+
+    assert.equal(report.errorCount, 0)
+    assert.equal(report.warningCount, 0)
+    assert.deepEqual(report.messages, [])
+
+    const logged = warnSpy
+      .getCalls()
+      .map((c) => c.args[0])
+      .join('\n')
+
+    assert.ok(logged.includes("invalid configuration for rule 'use-natspec'"), logged)
+    assert.ok(warnSpy.called)
+  })
+
+  it('Invalid CFG - use-natspec with ignore.contract not as array', () => {
+    const config = {
+      title: {
+        enabled: true,
+        ignore: {
+          contract: 'WrongFormat', // must be array
+        },
+      },
+    }
+
+    const report = linter.processStr(dummyCode, {
+      rules: { 'use-natspec': ['warn', config] },
+    })
+
+    assert.equal(report.errorCount, 0)
+    assert.equal(report.warningCount, 0)
+    assert.deepEqual(report.messages, [])
+
+    const logged = warnSpy
+      .getCalls()
+      .map((c) => c.args[0])
+      .join('\n')
+
+    assert.ok(logged.includes("invalid configuration for rule 'use-natspec'"))
+    assert.ok(warnSpy.called)
+  })
 })
diff --git a/test/rules/best-practices/use-natspec.js b/test/rules/best-practices/use-natspec.js
new file mode 100644
index 0000000..247525f
--- /dev/null
+++ b/test/rules/best-practices/use-natspec.js
@@ -0,0 +1,547 @@
+const assert = require('assert')
+const linter = require('../../../lib/index')
+const { assertNoErrors, assertErrorMessage, assertErrorCount } = require('../../common/asserts')
+
+describe('Linter - use-natspec', () => {
+  it('should detect missing @title and @author and @notice in contract', () => {
+    const code = `
+      contract MyContract {
+      }
+    `
+
+    const report = linter.processStr(code, {
+      rules: { 'use-natspec': 'error' },
+    })
+
+    assertErrorCount(report, 3)
+    assert.ok(report.reports[0].message, "Missing @title tag in contract 'MyContract'")
+    assert.ok(report.reports[1].message, "Missing @author tag in contract 'MyContract'")
+    assert.ok(report.reports[2].message, "Missing @notice tag in contract 'MyContract'")
+  })
+
+  it('should not fail if @inheritdoc is present for public function', () => {
+    const code = `
+      /// @title A
+      /// @author B
+      /// @notice Some notice
+      contract C {
+        /// @inheritdoc D
+        function x() external {}
+      }
+    `
+
+    const report = linter.processStr(code, {
+      rules: { 'use-natspec': 'error' },
+    })
+
+    assertNoErrors(report)
+  })
+
+  it('should fail if @param names do not match Solidity param names', () => {
+    const code = `
+      /// @title A
+      /// @author B
+      /// @notice Some notice
+      contract C {
+        /// @notice test
+        /// @param wrongName description
+        function f(uint256 rightName) external {}
+      }
+    `
+
+    const report = linter.processStr(code, {
+      rules: { 'use-natspec': 'error' },
+    })
+
+    assertErrorMessage(
+      report,
+      "Mismatch in @param names for function 'f'. Expected: [rightName], Found: [wrongName]"
+    )
+  })
+
+  it('should not validate names when parameters are unnamed', () => {
+    const code = `
+      /// @title A
+      /// @author B
+      /// @notice Some notice
+      contract C {
+        /// @notice test
+        /// @param description
+        function f(uint256) external {}
+      }
+    `
+
+    const report = linter.processStr(code, {
+      rules: { 'use-natspec': 'error' },
+    })
+
+    assertNoErrors(report)
+  })
+
+  it('should validate @return with unnamed return value', () => {
+    const code = `
+      /// @title A
+      /// @author B
+      /// @notice Some notice
+      contract C {
+        /// @notice test
+        /// @return description
+        function f() external pure returns (uint256) {
+          return 1;
+        }
+      }
+    `
+
+    const report = linter.processStr(code, {
+      rules: { 'use-natspec': 'error' },
+    })
+
+    assertNoErrors(report)
+  })
+
+  it('should error when return is named and not documented', () => {
+    const code = `
+      /// @title A
+      /// @author B
+      /// @notice Some notice
+      contract C {
+        /// @notice test
+        function f() external pure returns (uint256 val) {
+          return val;
+        }
+      }
+    `
+
+    const report = linter.processStr(code, {
+      rules: { 'use-natspec': 'error' },
+    })
+
+    assert.ok(report.reports[0].message, "Missing @return tag in function 'f'")
+    assert.ok(
+      report.reports[1].message,
+      "Mismatch in @return names for function 'f'. Expected: [val], Found: []"
+    )
+  })
+
+  it('should handle multiple @param and @return correctly', () => {
+    const code = `
+      /// @title Example
+      /// @author User
+      /// @notice Does something
+      contract Test {
+        /// @notice Multi param
+        /// @param a First
+        /// @param b Second
+        /// @return x Description
+        /// @return y Description
+        function multi(uint a, uint b) external pure returns (uint x, uint y) {
+          return (a, b);
+        }
+      }
+    `
+
+    const report = linter.processStr(code, {
+      rules: { 'use-natspec': 'error' },
+    })
+
+    assertNoErrors(report)
+  })
+
+  it('should ignore variables that are not public', () => {
+    const code = `
+      /**
+       * @title Example
+       * @author User
+       * @notice Does something
+       */
+      contract MyContract {
+        uint256 internal secret;
+      }
+    `
+
+    const report = linter.processStr(code, {
+      rules: { 'use-natspec': 'error' },
+    })
+
+    assertNoErrors(report)
+  })
+
+  it('should require @notice for public state variables', () => {
+    const code = `
+      /**
+       * @title Example
+       * @author User
+       * @notice Does something
+       */
+      contract MyContract {
+        uint256 public value;
+      }
+    `
+
+    const report = linter.processStr(code, {
+      rules: { 'use-natspec': 'error' },
+    })
+
+    assertErrorMessage(report, "Missing @notice tag in variable 'value'")
+  })
+
+  it('should fail when natspec @param tags does not match Solidity parameters', () => {
+    const code = `
+      /// @title A
+      /// @author B
+      /// @notice test
+      contract C {
+        /// @notice does something
+        /// @param a description
+        /// @param b description
+        function test(uint a) external {}
+      }
+    `
+    const report = linter.processStr(code, {
+      rules: { 'use-natspec': 'error' },
+    })
+
+    assertErrorMessage(
+      report,
+      "Mismatch in @param names for function 'test'. Expected: [a], Found: [a, b]"
+    )
+  })
+
+  it('should fail when natspec @param tags quantity does not match Solidity parameters', () => {
+    const code = `
+      /// @title A
+      /// @author B
+      /// @notice test
+      contract C {
+        /// @notice does something
+        /// @param description
+        function test(uint, uint) external {}
+      }
+    `
+    const report = linter.processStr(code, {
+      rules: { 'use-natspec': 'error' },
+    })
+
+    assertErrorMessage(
+      report,
+      "Mismatch in @param count for function 'test'. Expected: 2, Found: 1"
+    )
+  })
+
+  it('should pass when unnamed Solidity params match @param count', () => {
+    const code = `
+      /// @title A
+      /// @author B
+      /// @notice test
+      contract C {
+        /// @notice does something
+        /// @param description
+        function test(uint) external {}
+      }
+    `
+    const report = linter.processStr(code, {
+      rules: { 'use-natspec': 'error' },
+    })
+
+    assertNoErrors(report)
+  })
+
+  it('should handle return parameters correctly when natspec does not describe them', () => {
+    const code = `
+      /// @title A
+      /// @author B
+      /// @notice test
+      contract C {
+        /// @notice does something
+        /// @return amount1 description
+        function test() external pure returns (uint256 amount1, uint256 amount2) {
+          return (1, amount);
+        }
+      }
+    `
+    const report = linter.processStr(code, {
+      rules: { 'use-natspec': 'error' },
+    })
+
+    assertErrorMessage(
+      report,
+      "Mismatch in @return names for function 'test'. Expected: [amount1, amount2], Found: [amount1]"
+    )
+  })
+
+  it('should fail when one named return is undocumented', () => {
+    const code = `
+      /// @title A
+      /// @author B
+      /// @notice test
+      contract C {
+        /// @notice does something
+        /// @return amount description
+        function test() external pure returns (uint256, uint256 amount) {
+          return (1, amount);
+        }
+      }
+    `
+    const report = linter.processStr(code, {
+      rules: { 'use-natspec': 'error' },
+    })
+
+    assertErrorMessage(
+      report,
+      "Mismatch in @return count for function 'test'. Expected: 2, Found: 1"
+    )
+  })
+
+  it('should ignore function if @inheritdoc is present', () => {
+    const code = `
+      /**
+       * @title Interface
+       * @author Me
+       * @notice Interface notice
+       */
+      interface I {
+        /// @notice Function x
+        /// @param p1 Parameter 1
+        function x() external;
+      }
+
+      /// @title Impl
+      /// @author Me
+      /// @notice Impl notice
+      contract C is I {
+        /// @inheritdoc I
+        function x() external {}
+      }
+    `
+    const report = linter.processStr(code, {
+      rules: { 'use-natspec': 'error' },
+    })
+
+    assertNoErrors(report)
+  })
+
+  it('should require title/author/notice in library', () => {
+    const code = `
+      library MyLib {}
+    `
+    const report = linter.processStr(code, {
+      rules: { 'use-natspec': 'error' },
+    })
+
+    assert.ok(report.reports[0].message, "Missing @title tag in library 'MyLib'")
+    assert.ok(report.reports[1].message, "Missing @author tag in library 'MyLib'")
+    assert.ok(report.reports[2].message, "Missing @notice tag in library 'MyLib'")
+  })
+
+  it('should require tags in interface', () => {
+    const code = `
+      interface MyInterface {}
+    `
+    const report = linter.processStr(code, {
+      rules: { 'use-natspec': 'error' },
+    })
+
+    assert.ok(report.reports[0].message, "Missing @title tag in interface 'MyInterface'")
+    assert.ok(report.reports[1].message, "Missing @author tag in interface 'MyInterface'")
+    assert.ok(report.reports[2].message, "Missing @notice tag in interface 'MyInterface'")
+  })
+
+  /// ///////////////////////////////////////////////////////////////
+  /// //////////////////// Configuration //////////////////////////////
+  /// ///////////////////////////////////////////////////////////////
+
+  describe('==> use-natspec - Configuration rule', () => {
+    it('should ignore @title for contract using per-type ignore list', () => {
+      const code = `
+                    contract IgnoreTitle {
+                    }
+                    `
+
+      const report = linter.processStr(code, {
+        rules: {
+          'use-natspec': [
+            'error',
+            {
+              title: {
+                enabled: true,
+                ignore: {
+                  contract: ['IgnoreTitle'],
+                },
+              },
+              author: {
+                enabled: true,
+              },
+              notice: {
+                enabled: true,
+              },
+            },
+          ],
+        },
+      })
+
+      assertErrorCount(report, 2)
+      assert.ok(report.reports[0].message, "Missing @author tag in contract 'IgnoreTitle'")
+      assert.ok(report.reports[1].message, "Missing @notice tag in contract 'IgnoreTitle'")
+    })
+
+    it('should ignore all tags globally for a contract and interface using "*" ignore', () => {
+      const code = `
+                    interface IgnoreAllTags {
+                    }
+                    contract LegacyContract {
+                    }
+                    `
+
+      const report = linter.processStr(code, {
+        rules: {
+          'use-natspec': [
+            'error',
+            {
+              title: { enabled: true, ignore: { '*': ['LegacyContract', 'IgnoreAllTags'] } },
+              author: { enabled: true, ignore: { '*': ['LegacyContract', 'IgnoreAllTags'] } },
+              notice: { enabled: true, ignore: { '*': ['LegacyContract', 'IgnoreAllTags'] } },
+            },
+          ],
+        },
+      })
+
+      assertNoErrors(report)
+    })
+
+    it('should ignore @notice tag for one variable using ignore.variable', () => {
+      const code = `
+                    /// @title Config Test
+                    /// @author User
+                    /// @notice This contract is for testing purposes
+                    contract ConfigTest {
+                      uint256 public ignoredVariable;
+                      uint256 public normalVariable;
+                    }
+                  `
+
+      const report = linter.processStr(code, {
+        rules: {
+          'use-natspec': [
+            'error',
+            {
+              notice: {
+                enabled: true,
+                ignore: {
+                  variable: ['ignoredVariable'],
+                },
+              },
+            },
+          ],
+        },
+      })
+
+      assertErrorCount(report, 1)
+      assertErrorMessage(report, "Missing @notice tag in variable 'normalVariable'")
+    })
+
+    it('should ignore @param for specific function using ignore.function', () => {
+      const code = `
+                    /**
+                     * @title Function Ignore Test
+                     * @author User
+                     * @notice This contract tests function-specific ignores
+                     */
+                    contract MyContract {
+                      /// @notice Some explanation
+                      function skipCheck(uint256 x) public {}
+                    }
+                  `
+
+      const report = linter.processStr(code, {
+        rules: {
+          'use-natspec': [
+            'error',
+            {
+              param: {
+                enabled: true,
+                ignore: {
+                  function: ['skipCheck'],
+                },
+              },
+            },
+          ],
+        },
+      })
+
+      assertNoErrors(report)
+    })
+
+    it('should ignore @return for specific function with named return', () => {
+      const code = `
+                    /**
+                     * @title Function Ignore Test
+                     * @author User
+                     * @notice This contract tests function-specific ignores
+                     */
+                    contract MyContract {
+                      /// @notice Something
+                      /// @param x input
+                      function getValue(uint x) public pure returns (uint256 result) {
+                        return x;
+                      }
+                    }
+                  `
+      const report = linter.processStr(code, {
+        rules: {
+          'use-natspec': [
+            'error',
+            {
+              return: {
+                enabled: true,
+                ignore: {
+                  function: ['getValue'],
+                },
+              },
+            },
+          ],
+        },
+      })
+
+      assertNoErrors(report)
+    })
+
+    it('should error on @author, ignore @title for specific contract and error on @return', () => {
+      const code = `
+                    contract HalfIgnored {
+
+                      function doSomething(uint256 amount) external returns(uint256) {}
+                    }
+                  `
+
+      const report = linter.processStr(code, {
+        rules: {
+          'use-natspec': [
+            'error',
+            {
+              title: {
+                enabled: true,
+                ignore: {
+                  contract: ['HalfIgnored'],
+                },
+              },
+              notice: {
+                enabled: false,
+              },
+              param: {
+                enabled: false,
+              },
+            },
+          ],
+        },
+      })
+
+      assertErrorCount(report, 3)
+      assert.ok(report.reports[0].message, "Missing @author tag in contract 'HalfIgnored'")
+      assert.ok(report.reports[1].message, "Missing @return tag in function 'doSomething'")
+      assert.ok(
+        report.reports[2].message,
+        "Mismatch in @return count for function 'doSomething'. Expected: 1, Found: 0"
+      )
+    })
+  })
+})
diff --git a/test/rules/gas-consumption/gas-custom-errors.js b/test/rules/gas-consumption/gas-custom-errors.js
index 7b0c435..f3cca98 100644
--- a/test/rules/gas-consumption/gas-custom-errors.js
+++ b/test/rules/gas-consumption/gas-custom-errors.js
@@ -198,7 +198,7 @@ describe('Linter - gas-custom-errors', () => {
 
     const report = linter.processStr(code, {
       extends: 'solhint:recommended',
-      rules: { 'compiler-version': 'off' },
+      rules: { 'compiler-version': 'off', 'use-natspec': 'off' },
     })
 
     assertWarnsCount(report, 2)
diff --git a/test/rules/gas-consumption/gas-increment-by-one.js b/test/rules/gas-consumption/gas-increment-by-one.js
index a5c5b79..4c460db 100644
--- a/test/rules/gas-consumption/gas-increment-by-one.js
+++ b/test/rules/gas-consumption/gas-increment-by-one.js
@@ -40,15 +40,19 @@ const RAISE_ERRORS = [
     statement: 'anotherVar -= 1;',
     varName: 'anotherVar',
   },
-  // {
-  //   statement:
-  //     'complexMapping[user].balances[asset] = complexMapping[user].balances[asset] + 1 + someFunctionCall();',
-  //   varName: 'complexMapping',
-  // },
-  // {
-  //   statement: 'anotherMapping[user][token].count = anotherMapping[user][token].count - 1 + 5;',
-  //   varName: 'anotherMapping',
-  // },
+  {
+    statement: 'complexMapping[user].balances[asset] = complexMapping[user].balances[asset] + 1;',
+    varName: 'complexMapping',
+  },
+  {
+    statement: 'amount = 1 + amount;',
+    varName: 'amount',
+  },
+  {
+    statement:
+      'complexMappingBackward[user].balances[asset] = 1 + complexMappingBackward[user].balances[asset];',
+    varName: 'complexMappingBackward',
+  },
 ]
 
 const NOT_RAISE_ERRORS = [
diff --git a/test/rules/gas-consumption/gas-named-return-values.js b/test/rules/gas-consumption/gas-named-return-values.js
index e08f924..4b603c8 100644
--- a/test/rules/gas-consumption/gas-named-return-values.js
+++ b/test/rules/gas-consumption/gas-named-return-values.js
@@ -76,6 +76,7 @@ describe('Linter - gas-named-return-values', () => {
         'compiler-version': 'off',
         'comprehensive-interface': 'off',
         'foundry-test-functions': 'off',
+        'use-natspec': 'off',
       },
     })
 
diff --git a/test/rules/gas-consumption/gas-struct-packing.js b/test/rules/gas-consumption/gas-struct-packing.js
index 4363777..b1bd299 100644
--- a/test/rules/gas-consumption/gas-struct-packing.js
+++ b/test/rules/gas-consumption/gas-struct-packing.js
@@ -1,4 +1,5 @@
 const assert = require('assert')
+const { contractWith, multiLine } = require('../../common/contract-builder')
 const linter = require('../../../lib/index')
 const TEST_CASES = require('../../fixtures/gas-consumption/gas-struct-packing-data')
 
@@ -33,23 +34,21 @@ describe('Linter - gas-struct-packing', () => {
     })
   }
 
-  // it(`should NOT raise error for `, () => {
-  //   const code = contractWith(
-  //     multiLine(
-  //       // // Large Types Followed by Small Types
-  //       'struct MyStruct2 {',
-  //       '   address addr1;',
-  //       '   address addr2;',
-  //       '   address addr3;',
-  //       '}'
-  //     )
-  //   )
-  //   const report = linter.processStr(code, {
-  //     rules: { 'gas-struct-packing': 'error' },
-  //   })
-
-  //   console.log(report)
-
-  //   // assert.equal(report.errorCount, 0)
-  // })
+  it(`REPORTED: should NOT raise error for MyStruct2`, () => {
+    const code = contractWith(
+      multiLine(
+        // // Large Types Followed by Small Types
+        'struct MyStruct2 {',
+        '   address addr1;',
+        '   address addr2;',
+        '   address addr3;',
+        '}'
+      )
+    )
+    const report = linter.processStr(code, {
+      rules: { 'gas-struct-packing': 'error' },
+    })
+
+    assert.equal(report.errorCount, 0)
+  })
 })
diff --git a/test/rules/miscellaneous/import-path-check.js b/test/rules/miscellaneous/import-path-check.js
index 1647998..62328aa 100644
--- a/test/rules/miscellaneous/import-path-check.js
+++ b/test/rules/miscellaneous/import-path-check.js
@@ -43,6 +43,7 @@ describe('import-path-check (mocked fs)', () => {
         'compiler-version': 'off',
         'no-empty-blocks': 'off',
         'no-global-import': 'off',
+        'use-natspec': 'off',
       },
     }
 
@@ -66,6 +67,7 @@ describe('import-path-check (mocked fs)', () => {
       rules: {
         'no-empty-blocks': 'off',
         'no-global-import': 'off',
+        'use-natspec': 'off',
       },
     }
     const report = linter.processStr(code, config, fileName)
diff --git a/test/rules/naming/foundry-test-functions.js b/test/rules/naming/foundry-test-functions.js
index ee5687f..18b6d1a 100644
--- a/test/rules/naming/foundry-test-functions.js
+++ b/test/rules/naming/foundry-test-functions.js
@@ -162,6 +162,7 @@ describe('Linter - foundry-test-functions', () => {
       extends: 'solhint:recommended',
       rules: {
         'compiler-version': 'off',
+        'use-natspec': 'off',
         'foundry-test-functions': ['error', ['setUp', 'finish']],
       },
     })
diff --git a/test/rules/naming/func-named-parameters.js b/test/rules/naming/func-named-parameters.js
index 4d700f7..a5f7504 100644
--- a/test/rules/naming/func-named-parameters.js
+++ b/test/rules/naming/func-named-parameters.js
@@ -71,6 +71,7 @@ describe('Linter - func-named-parameters', () => {
         'compiler-version': 'off',
         'comprehensive-interface': 'off',
         'foundry-test-functions': 'off',
+        'use-natspec': 'off',
       },
     })