diff --git a/.gitignore b/.gitignore
index 063bb153..8f526119 100644
--- a/.gitignore
+++ b/.gitignore
@@ -9,4 +9,5 @@ convertLib.sol
antlr4.jar
/docs/.sass-cache/
_temp/
+*solhintReport*.*
diff --git a/CHANGELOG.md b/CHANGELOG.md
index d93d717d..96c84c9e 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,26 @@
+## [4.0] - 2023-10-01
+
+### Updated
+- Enhance explicit types sensitivity [#493](https://github.com/protofire/solhint/pull/493) (Thanks to [@vladyan18](https://github.com/vladyan18))
+- Docs on `private-vars-leading-underscore` rule to clarify its functionality
+- Changelog and docs for `no-empty-blocks` rule to clarify its functionality
+- Require package with full path [#515](https://github.com/protofire/solhint/pull/515) (Thanks to [@zouguangxian](@https://github.com/zouguangxian))
+
+### Added
+- Check for updates on Solhint version to keep users up to date. There's an option to disable this check (`--disc`) [#506](https://github.com/protofire/solhint/pull/506)
+- `fix` option now shows the report on screen [#509](https://github.com/protofire/solhint/pull/509)
+- `save` option to store report on disk with the standard or the specified format [#509](https://github.com/protofire/solhint/pull/509)
+- Autofix for `explicit-types` rule [#504](https://github.com/protofire/solhint/pull/504)
+- Autofix for `no-console` rule [#513](https://github.com/protofire/solhint/pull/513)
+- Autofix for `private-vars-leading-underscore` rule [#511](https://github.com/protofire/solhint/pull/511)
+
+### Fixed
+- Generate docs script on Windows OS [#494](https://github.com/protofire/solhint/pull/494) (Thanks to [@vladyan18](https://github.com/vladyan18))
+- `one-contract-per-file` ignore interfaces [#514](https://github.com/protofire/solhint/pull/514) (Thanks to [@cruzdanilo](https://github.com/cruzdanilo))
+
+
+
+
## [3.6.2] - 2023-08-17
### Added
- New Rule: `one-contract-per-file` - Enforces the use of ONE contract per file [#487](https://github.com/protofire/solhint/pull/487)
@@ -31,12 +54,17 @@ If not explicitly added, this rule will not be executed.
### SPECIAL ATTENTION
- RULE: `compiler-version` default was updated from ^0.5.2 to ^0.8.0
+
+### Updated
+- Rule: `check-send-result` added config clarification in the new `Notes` section [#482](https://github.com/protofire/solhint/pull/482)
+- Rule: `compiler-version` default was updated from ^0.5.2 to ^0.8.0 [#483](https://github.com/protofire/solhint/pull/483)
+
### Added
- New Rule: Enforces the use of Custom Errors over Require and Revert statements [#475](https://github.com/protofire/solhint/pull/475)
- New Rule: Enforces the test_ prefix on a file for Foundry users [#476](https://github.com/protofire/solhint/pull/476)
- New Rule: Enforces the naming of function return values [#478](https://github.com/protofire/solhint/pull/478)
- `Notes` option on docs to add more information of each rule. See `foundry-test-functions`. [#476](https://github.com/protofire/solhint/pull/476)
-
+
### Fixed
- `func-named-parameters` - false positives on builtin functions [#472](https://github.com/protofire/solhint/pull/472)
- `ordering` - treat initializer weight same as constructor [#474](https://github.com/protofire/solhint/pull/474)
@@ -44,15 +72,12 @@ If not explicitly added, this rule will not be executed.
- `explicit-types` - default value is now taking into account when no value is specified in config [#481](https://github.com/protofire/solhint/pull/481)
- `compiler-version` - default value is now taking into account when no value is specified in config [#483](https://github.com/protofire/solhint/pull/483)
-### Updates
-- Rule: `check-send-result` added config clarification in the new `Notes` section [#482](https://github.com/protofire/solhint/pull/482)
-- Rule: `compiler-version` default was updated from ^0.5.2 to ^0.8.0 [#483](https://github.com/protofire/solhint/pull/483)
## [3.5.1] - 2023-08-04
### Updated
-- Support `ignoreConstructors` option for `no-empty-blocks` [#418](https://github.com/protofire/solhint/pull/418)
+- Ignores empty constructors when inheriting a base contract [#418](https://github.com/protofire/solhint/pull/418)
- Bump json5 from 2.1.3 to 2.2.3 [#376](https://github.com/protofire/solhint/pull/376)
- Bump json-schema and jsprim [#370](https://github.com/protofire/solhint/pull/370)
- Bump semver from 6.3.0 to 7.5.2 [#438](https://github.com/protofire/solhint/pull/438)
@@ -64,7 +89,6 @@ If not explicitly added, this rule will not be executed.
- Removed runtime dependencies on load-rules [#462](https://github.com/protofire/solhint/pull/462)
- Allowed $ symbol as part of naming [#465](https://github.com/protofire/solhint/issues/465)
- Disabled `no-empty-blocks` rule for receive() function [#466](https://github.com/protofire/solhint/pull/466)
-
### Added
- New Rule: No unused imports [#417](https://github.com/protofire/solhint/pull/417)
diff --git a/README.md b/README.md
index 0f010f69..27ffa79b 100644
--- a/README.md
+++ b/README.md
@@ -5,6 +5,7 @@
By Protofire
+[![Join Discord](https://img.shields.io/badge/join-Discord-red)](https://discord.gg/4TYGq3zpjs)
[![Donate with Ethereum](https://img.shields.io/badge/Donate-ETH-blue)](https://etherscan.io/address/0xA81705c8C247C413a19A244938ae7f4A0393944e)
[![NPM version](https://badge.fury.io/js/solhint.svg)](https://npmjs.org/package/solhint)
[![Coverage Status](https://coveralls.io/repos/github/protofire/solhint/badge.svg?branch=master)](
@@ -14,6 +15,7 @@ https://coveralls.io/github/protofire/solhint?branch=master)
This is an open source project for linting [Solidity](http://solidity.readthedocs.io/en/develop/) code. This project
provides both **Security** and **Style Guide** validations.
+[JOIN OUR DISCORD SERVER](https://discord.gg/4TYGq3zpjs)
## Installation
You can install Solhint using **npm**:
@@ -60,8 +62,11 @@ Options:
-c, --config [file_name] file to use as your .solhint.json
-q, --quiet report errors only - default: false
--ignore-path [file_name] file to use as your .solhintignore
- --fix automatically fix problems
+ --fix automatically fix problems and show report
+ --noPrompt do not suggest to backup files when any `fix` option is selected
--init create configuration file for solhint
+ --disc do not check for solhint updates
+ --save save report to file on current folder
-h, --help output usage information
Commands:
@@ -69,8 +74,17 @@ Commands:
stdin [options] linting of source code data provided to STDIN
list-rules display covered rules of current .solhint.json
```
-### Note
-The `--fix` option currently works only on "avoid-throw" and "avoid-sha3" rules
+### Notes
+- Solhint checks if there are newer versions. The `--disc` option avoids that check.
+- `--save` option will create a file named as `YYYYMMDDHHMMSS_solhintReport.txt` on current folder with default or specified format
+
+### Fix
+This option currently works on:
+- avoid-throw
+- avoid-sha3
+- no-console
+- explicit-types
+- private-vars-underscore
## Configuration
diff --git a/docs/rules.md b/docs/rules.md
index 66d12655..64185cd1 100644
--- a/docs/rules.md
+++ b/docs/rules.md
@@ -48,7 +48,7 @@ title: "Rule Index of Solhint"
| [modifier-name-mixedcase](./rules/naming/modifier-name-mixedcase.md) | Modifier name must be in mixedCase. | | |
| [named-parameters-mapping](./rules/naming/named-parameters-mapping.md) | Solidity v0.8.18 introduced named parameters on the mappings definition. | | |
| [named-return-values](./rules/naming/named-return-values.md) | Enforce the return values of a function to be named | | |
-| [private-vars-leading-underscore](./rules/naming/private-vars-leading-underscore.md) | Private and internal names must start with a single underscore. | | |
+| [private-vars-leading-underscore](./rules/naming/private-vars-leading-underscore.md) | Non-external functions and state variables should start with a single underscore. Others, shouldn't | | |
| [use-forbidden-name](./rules/naming/use-forbidden-name.md) | Avoid to use letters 'I', 'l', 'O' as identifiers. | $~~~~~~~~$✔️ | |
| [var-name-mixedcase](./rules/naming/var-name-mixedcase.md) | Variable name must be in mixedCase. (Does not check IMMUTABLES, use immutable-vars-naming) | $~~~~~~~~$✔️ | |
| [func-order](./rules/order/func-order.md) | Function order is incorrect. | | $~~~~~~~$✔️ |
diff --git a/docs/rules/best-practises/explicit-types.md b/docs/rules/best-practises/explicit-types.md
index 8448bc6e..2a472762 100644
--- a/docs/rules/best-practises/explicit-types.md
+++ b/docs/rules/best-practises/explicit-types.md
@@ -32,6 +32,8 @@ This rule accepts an array of options:
}
```
+### Notes
+- Solhint allows this rule to automatically fix the code with `--fix` option
## Examples
### 👍 Examples of **correct** code for this rule
@@ -48,6 +50,12 @@ uint256 public variableName
uint public variableName
```
+#### If explicit is selected
+
+```solidity
+uint256 public variableName = uint256(5)
+```
+
### 👎 Examples of **incorrect** code for this rule
#### If explicit is selected
@@ -62,6 +70,12 @@ uint public variableName
uint256 public variableName
```
+#### At any setting
+
+```solidity
+uint public variableName = uint256(5)
+```
+
## Version
This rule was introduced in [Solhint 3.5.1](https://github.com/protofire/solhint/tree/v3.5.1)
diff --git a/docs/rules/best-practises/no-empty-blocks.md b/docs/rules/best-practises/no-empty-blocks.md
index cbc7caec..d8a18752 100644
--- a/docs/rules/best-practises/no-empty-blocks.md
+++ b/docs/rules/best-practises/no-empty-blocks.md
@@ -26,6 +26,8 @@ This rule accepts a string option of rule severity. Must be one of "error", "war
}
```
+### Notes
+- The rule ignores an empty constructor by default as long as base contracts are being inherited. See "Empty Constructor" example.
## Examples
### 👍 Examples of **correct** code for this rule
diff --git a/docs/rules/best-practises/one-contract-per-file.md b/docs/rules/best-practises/one-contract-per-file.md
index 564bacaf..92135351 100644
--- a/docs/rules/best-practises/one-contract-per-file.md
+++ b/docs/rules/best-practises/one-contract-per-file.md
@@ -31,7 +31,7 @@ This rule accepts a string option of rule severity. Must be one of "error", "war
This rule does not have examples.
## Version
-This rule is introduced in the latest version.
+This rule was introduced in [Solhint 3.6.2](https://github.com/protofire/solhint/tree/v3.6.2)
## Resources
- [Rule source](https://github.com/protofire/solhint/tree/master/lib/rules/best-practises/one-contract-per-file.js)
diff --git a/docs/rules/naming/private-vars-leading-underscore.md b/docs/rules/naming/private-vars-leading-underscore.md
index 9e0028c9..bba9e897 100644
--- a/docs/rules/naming/private-vars-leading-underscore.md
+++ b/docs/rules/naming/private-vars-leading-underscore.md
@@ -9,15 +9,15 @@ title: "private-vars-leading-underscore | Solhint"
![Default Severity Badge warn](https://img.shields.io/badge/Default%20Severity-warn-yellow)
## Description
-Private and internal names must start with a single underscore.
+Non-external functions and state variables should start with a single underscore. Others, shouldn't
## Options
This rule accepts an array of options:
-| Index | Description | Default Value |
-| ----- | ------------------------------------------------------------------------------------------------------------------------------------- | ---------------- |
-| 0 | Rule severity. Must be one of "error", "warn", "off". | warn |
-| 1 | A JSON object with a single property "strict" specifying if the rule should apply to non state variables. Default: { strict: false }. | {"strict":false} |
+| Index | Description | Default Value |
+| ----- | ----------------------------------------------------------------------------------------------------------------------------------------- | ---------------- |
+| 0 | Rule severity. Must be one of "error", "warn", "off". | warn |
+| 1 | A JSON object with a single property "strict" specifying if the rule should apply to ALL non state variables. Default: { strict: false }. | {"strict":false} |
### Example Config
@@ -29,9 +29,64 @@ This rule accepts an array of options:
}
```
+### Notes
+- This rule considers functions and variables in Libraries as well
+- This rule skips external and public functions
+- This rule skips external and public state variables
+- See [here](https://docs.soliditylang.org/en/latest/style-guide.html#underscore-prefix-for-non-external-functions-and-variables) for further information
## Examples
-This rule does not have examples.
+### 👍 Examples of **correct** code for this rule
+
+#### Internal function with correct naming
+
+```solidity
+function _thisIsInternal() internal {}
+```
+
+#### Private function with correct naming
+
+```solidity
+function _thisIsPrivate() private {}
+```
+
+#### Internal state variable with correct naming
+
+```solidity
+uint256 internal _thisIsInternalVariable;
+```
+
+#### Internal state variable with correct naming (no visibility is considered internal)
+
+```solidity
+uint256 _thisIsInternalVariable;
+```
+
+### 👎 Examples of **incorrect** code for this rule
+
+#### Internal function with incorrect naming
+
+```solidity
+function thisIsInternal() internal {}
+```
+
+#### Private function with incorrect naming
+
+```solidity
+function thisIsPrivate() private {}
+```
+
+#### Internal state variable with incorrect naming
+
+```solidity
+uint256 internal thisIsInternalVariable;
+```
+
+#### Internal state variable with incorrect naming (no visibility is considered internal)
+
+```solidity
+uint256 thisIsInternalVariable;
+```
## Version
This rule was introduced in [Solhint 3.0.0-rc.3](https://github.com/protofire/solhint/tree/v3.0.0-rc.3)
diff --git a/docs/rules/security/avoid-sha3.md b/docs/rules/security/avoid-sha3.md
index 1d6f9637..7947a087 100644
--- a/docs/rules/security/avoid-sha3.md
+++ b/docs/rules/security/avoid-sha3.md
@@ -26,6 +26,8 @@ This rule accepts a string option of rule severity. Must be one of "error", "war
}
```
+### Notes
+- Solhint allows this rule to automatically fix the code with `--fix` option
## Examples
This rule does not have examples.
diff --git a/docs/rules/security/avoid-throw.md b/docs/rules/security/avoid-throw.md
index a9d90cc8..5b12fcd7 100644
--- a/docs/rules/security/avoid-throw.md
+++ b/docs/rules/security/avoid-throw.md
@@ -26,6 +26,8 @@ This rule accepts a string option of rule severity. Must be one of "error", "war
}
```
+### Notes
+- Solhint allows this rule to automatically fix the code with `--fix` option
## Examples
This rule does not have examples.
diff --git a/docs/writing-plugins.md b/docs/writing-plugins.md
index fe1fa111..457e7774 100644
--- a/docs/writing-plugins.md
+++ b/docs/writing-plugins.md
@@ -24,6 +24,7 @@ class MyNewRule {
...
}
+}
```
This is enough for the rule to work but, of course, this will do nothing. Rules are implemented using a visitor pattern: you implement methods that are called when a node in the AST is entered or exited. For example, let's make a rule that forbids naming contracts `Foo`:
diff --git a/e2e/06-formatters/helpers/helpers.js b/e2e/06-formatters/helpers/helpers.js
index 85d43c6e..8376310f 100644
--- a/e2e/06-formatters/helpers/helpers.js
+++ b/e2e/06-formatters/helpers/helpers.js
@@ -32,7 +32,6 @@ const foo1Output = [
severity: 'Warning',
message: "'TEST2' should start with _",
ruleId: 'private-vars-leading-underscore',
- fix: null,
filePath: 'contracts/Foo.sol',
},
{
diff --git a/e2e/08-autofix/commands/.solhint.json b/e2e/08-autofix/commands/.solhint.json
new file mode 100644
index 00000000..7bd7c6a2
--- /dev/null
+++ b/e2e/08-autofix/commands/.solhint.json
@@ -0,0 +1,5 @@
+{
+ "rules": {
+ "explicit-types": ["error", "explicit"]
+ }
+}
diff --git a/e2e/08-autofix/commands/Foo1.sol b/e2e/08-autofix/commands/Foo1.sol
new file mode 100644
index 00000000..78bcaec7
--- /dev/null
+++ b/e2e/08-autofix/commands/Foo1.sol
@@ -0,0 +1,22 @@
+// SPDX-License-Identifier: Apache-2.0
+pragma solidity ^0.8.4;
+
+import {ERC20Burnable} from '@openzeppelin/contracts/token/ERC20/extensions/ERC20Burnable.sol';
+
+contract Foo1 is ERC20Burnable {
+ uint public hola;
+ uint public hola2;
+ int public constant hola3 = 2;
+ ufixed hola4;
+ fixed internal hola5;
+
+ constructor() ERC20('MyToken', 'MTK') {}
+
+ // solhint-disable no-empty-blocks
+ function payableTrue() public payable {}
+
+ // solhint-disable no-empty-blocks
+ function payableFalse() public {}
+
+ function zarasa() {}
+}
diff --git a/e2e/08-autofix/commands/Foo1AfterFix.sol b/e2e/08-autofix/commands/Foo1AfterFix.sol
new file mode 100644
index 00000000..ca900d69
--- /dev/null
+++ b/e2e/08-autofix/commands/Foo1AfterFix.sol
@@ -0,0 +1,22 @@
+// SPDX-License-Identifier: Apache-2.0
+pragma solidity ^0.8.4;
+
+import {ERC20Burnable} from '@openzeppelin/contracts/token/ERC20/extensions/ERC20Burnable.sol';
+
+contract Foo1 is ERC20Burnable {
+ uint256 public hola;
+ uint256 public hola2;
+ int256 public constant hola3 = 2;
+ ufixed128x18 hola4;
+ fixed128x18 internal hola5;
+
+ constructor() ERC20('MyToken', 'MTK') {}
+
+ // solhint-disable no-empty-blocks
+ function payableTrue() public payable {}
+
+ // solhint-disable no-empty-blocks
+ function payableFalse() public {}
+
+ function zarasa() {}
+}
diff --git a/e2e/08-autofix/commands/Foo1BeforeFix.sol b/e2e/08-autofix/commands/Foo1BeforeFix.sol
new file mode 100644
index 00000000..78bcaec7
--- /dev/null
+++ b/e2e/08-autofix/commands/Foo1BeforeFix.sol
@@ -0,0 +1,22 @@
+// SPDX-License-Identifier: Apache-2.0
+pragma solidity ^0.8.4;
+
+import {ERC20Burnable} from '@openzeppelin/contracts/token/ERC20/extensions/ERC20Burnable.sol';
+
+contract Foo1 is ERC20Burnable {
+ uint public hola;
+ uint public hola2;
+ int public constant hola3 = 2;
+ ufixed hola4;
+ fixed internal hola5;
+
+ constructor() ERC20('MyToken', 'MTK') {}
+
+ // solhint-disable no-empty-blocks
+ function payableTrue() public payable {}
+
+ // solhint-disable no-empty-blocks
+ function payableFalse() public {}
+
+ function zarasa() {}
+}
diff --git a/e2e/08-autofix/explicit-types/.solhint.json b/e2e/08-autofix/explicit-types/.solhint.json
new file mode 100644
index 00000000..7bd7c6a2
--- /dev/null
+++ b/e2e/08-autofix/explicit-types/.solhint.json
@@ -0,0 +1,5 @@
+{
+ "rules": {
+ "explicit-types": ["error", "explicit"]
+ }
+}
diff --git a/e2e/08-autofix/explicit-types/Foo1.sol b/e2e/08-autofix/explicit-types/Foo1.sol
new file mode 100644
index 00000000..78bcaec7
--- /dev/null
+++ b/e2e/08-autofix/explicit-types/Foo1.sol
@@ -0,0 +1,22 @@
+// SPDX-License-Identifier: Apache-2.0
+pragma solidity ^0.8.4;
+
+import {ERC20Burnable} from '@openzeppelin/contracts/token/ERC20/extensions/ERC20Burnable.sol';
+
+contract Foo1 is ERC20Burnable {
+ uint public hola;
+ uint public hola2;
+ int public constant hola3 = 2;
+ ufixed hola4;
+ fixed internal hola5;
+
+ constructor() ERC20('MyToken', 'MTK') {}
+
+ // solhint-disable no-empty-blocks
+ function payableTrue() public payable {}
+
+ // solhint-disable no-empty-blocks
+ function payableFalse() public {}
+
+ function zarasa() {}
+}
diff --git a/e2e/08-autofix/explicit-types/Foo1AfterFix.sol b/e2e/08-autofix/explicit-types/Foo1AfterFix.sol
new file mode 100644
index 00000000..ca900d69
--- /dev/null
+++ b/e2e/08-autofix/explicit-types/Foo1AfterFix.sol
@@ -0,0 +1,22 @@
+// SPDX-License-Identifier: Apache-2.0
+pragma solidity ^0.8.4;
+
+import {ERC20Burnable} from '@openzeppelin/contracts/token/ERC20/extensions/ERC20Burnable.sol';
+
+contract Foo1 is ERC20Burnable {
+ uint256 public hola;
+ uint256 public hola2;
+ int256 public constant hola3 = 2;
+ ufixed128x18 hola4;
+ fixed128x18 internal hola5;
+
+ constructor() ERC20('MyToken', 'MTK') {}
+
+ // solhint-disable no-empty-blocks
+ function payableTrue() public payable {}
+
+ // solhint-disable no-empty-blocks
+ function payableFalse() public {}
+
+ function zarasa() {}
+}
diff --git a/e2e/08-autofix/explicit-types/Foo1BeforeFix.sol b/e2e/08-autofix/explicit-types/Foo1BeforeFix.sol
new file mode 100644
index 00000000..78bcaec7
--- /dev/null
+++ b/e2e/08-autofix/explicit-types/Foo1BeforeFix.sol
@@ -0,0 +1,22 @@
+// SPDX-License-Identifier: Apache-2.0
+pragma solidity ^0.8.4;
+
+import {ERC20Burnable} from '@openzeppelin/contracts/token/ERC20/extensions/ERC20Burnable.sol';
+
+contract Foo1 is ERC20Burnable {
+ uint public hola;
+ uint public hola2;
+ int public constant hola3 = 2;
+ ufixed hola4;
+ fixed internal hola5;
+
+ constructor() ERC20('MyToken', 'MTK') {}
+
+ // solhint-disable no-empty-blocks
+ function payableTrue() public payable {}
+
+ // solhint-disable no-empty-blocks
+ function payableFalse() public {}
+
+ function zarasa() {}
+}
diff --git a/e2e/08-autofix/no-console/.solhint.json b/e2e/08-autofix/no-console/.solhint.json
new file mode 100644
index 00000000..2ff50f91
--- /dev/null
+++ b/e2e/08-autofix/no-console/.solhint.json
@@ -0,0 +1,5 @@
+{
+ "rules": {
+ "no-console": "error"
+ }
+}
diff --git a/e2e/08-autofix/no-console/Foo1.sol b/e2e/08-autofix/no-console/Foo1.sol
new file mode 100644
index 00000000..93b6d395
--- /dev/null
+++ b/e2e/08-autofix/no-console/Foo1.sol
@@ -0,0 +1,37 @@
+pragma solidity 0.8.0;
+
+import 'hardhat/console.sol';
+import 'forge-std/console.sol';
+import 'forge-std/console2.sol';
+import 'forge-std/xxxxx.sol';
+
+contract Foo1 {
+ Console[] public consoleTest;
+ Console[] public console;
+
+ struct Console {
+ uint256 one;
+ uint256 two;
+ }
+
+ function a() public {
+ console.log('test');
+ // comment
+ console.logString('test logString');
+ uint256 declaration;
+ console.logBytes12('test logBytes12');
+ }
+
+ function b() public {
+ console2.log('test console 2');
+ // comment
+ console.logString('test logString');
+ uint256 declaration;
+ console.logBytes12('test');
+ }
+
+ function c() external {
+ consoleTest.push(0, 0);
+ console.push = (1, 1);
+ }
+}
diff --git a/e2e/08-autofix/no-console/Foo1AfterFix.sol b/e2e/08-autofix/no-console/Foo1AfterFix.sol
new file mode 100644
index 00000000..22075370
--- /dev/null
+++ b/e2e/08-autofix/no-console/Foo1AfterFix.sol
@@ -0,0 +1,37 @@
+pragma solidity 0.8.0;
+
+
+
+
+import 'forge-std/xxxxx.sol';
+
+contract Foo1 {
+ Console[] public consoleTest;
+ Console[] public console;
+
+ struct Console {
+ uint256 one;
+ uint256 two;
+ }
+
+ function a() public {
+
+ // comment
+
+ uint256 declaration;
+
+ }
+
+ function b() public {
+
+ // comment
+
+ uint256 declaration;
+
+ }
+
+ function c() external {
+ consoleTest.push(0, 0);
+ console.push = (1, 1);
+ }
+}
diff --git a/e2e/08-autofix/no-console/Foo1BeforeFix.sol b/e2e/08-autofix/no-console/Foo1BeforeFix.sol
new file mode 100644
index 00000000..93b6d395
--- /dev/null
+++ b/e2e/08-autofix/no-console/Foo1BeforeFix.sol
@@ -0,0 +1,37 @@
+pragma solidity 0.8.0;
+
+import 'hardhat/console.sol';
+import 'forge-std/console.sol';
+import 'forge-std/console2.sol';
+import 'forge-std/xxxxx.sol';
+
+contract Foo1 {
+ Console[] public consoleTest;
+ Console[] public console;
+
+ struct Console {
+ uint256 one;
+ uint256 two;
+ }
+
+ function a() public {
+ console.log('test');
+ // comment
+ console.logString('test logString');
+ uint256 declaration;
+ console.logBytes12('test logBytes12');
+ }
+
+ function b() public {
+ console2.log('test console 2');
+ // comment
+ console.logString('test logString');
+ uint256 declaration;
+ console.logBytes12('test');
+ }
+
+ function c() external {
+ consoleTest.push(0, 0);
+ console.push = (1, 1);
+ }
+}
diff --git a/e2e/08-autofix/private-vars-underscore/.solhint.json b/e2e/08-autofix/private-vars-underscore/.solhint.json
new file mode 100644
index 00000000..47ff676f
--- /dev/null
+++ b/e2e/08-autofix/private-vars-underscore/.solhint.json
@@ -0,0 +1,5 @@
+{
+ "rules": {
+ "private-vars-leading-underscore": ["error",{"strict":false}]
+ }
+}
diff --git a/e2e/08-autofix/private-vars-underscore/Foo1.sol b/e2e/08-autofix/private-vars-underscore/Foo1.sol
new file mode 100644
index 00000000..f8a07515
--- /dev/null
+++ b/e2e/08-autofix/private-vars-underscore/Foo1.sol
@@ -0,0 +1,83 @@
+pragma solidity 0.8.0;
+
+library libraryName {
+ uint256 internal lzarasa1;
+ uint256 internal lzarasa2 = 2;
+ uint256 internal _lzarasa3;
+ uint256 private lzarasa4;
+ uint256 private lzarasa5 = 5;
+ uint256 private _lzarasa6;
+
+ uint256 public _lzarasa7;
+ uint256 public _lzarasa8 = 8;
+ uint256 public lzarasa9;
+
+ function fofo1() public {}
+ function _fofo2() public {}
+ function _fofo3() internal {}
+ function fofo4() internal {}
+}
+
+contract Foo1 {
+ uint256 internal zarasa1;
+ uint256 internal zarasa2 = 2;
+ uint256 internal _zarasa3;
+ uint256 private zarasa4;
+ uint256 private zarasa5 = 5;
+ uint256 private _zarasa6;
+
+ uint256 public _zarasa7;
+ uint256 public _zarasa8 = 8;
+ uint256 public zarasa9;
+
+ address payable internal constant zarasa10 = '0x0';
+ address public constant _zarasa11 = '0x0';
+ uint256 public immutable _zarasa12 = 2;
+ uint256 _zarasa13;
+ uint256 zarasa14;
+
+ mapping(address => uint256) internal zarMapping1;
+ mapping(address => uint256) public _zarMapping2;
+ mapping(address => uint256) internal _zarMapping3;
+ mapping(address => uint256) public zarMapping4;
+
+ function fooA(uint bar) internal payable onlyOwner returns (uint256 barA) {
+ uint256 zarasaFunc;
+ }
+
+ function fooB(uint bar) private onlyOwner returns (uint256 _barB) {
+ uint256 zarasaFunc;
+ }
+
+ function fooC(uint bar) private onlyOwner returns (uint256 _barC) {
+ uint256 zarasaFunc;
+ }
+
+ function fooD(uint bar) external onlyOwner {
+ uint256 zarasaFunc;
+ }
+
+ function fooE(uint bar) public onlyOwner {
+ uint256 zarasaFunc;
+ }
+
+ function fooF(uint bar) onlyOwner returns (uint256 _barF) {
+ uint256 zarasaFunc;
+ }
+
+ function fooG(uint bar) onlyOwner {
+ uint256 zarasaFunc;
+ }
+
+ function _fooH(uint bar) external onlyOwner {
+ uint256 zarasaFunc;
+ }
+
+ function _fooI(uint bar) public onlyOwner {
+ uint256 zarasaFunc;
+ }
+
+ function _fooJ(uint bar) onlyOwner returns (uint256 barJ) {
+ uint256 zarasaFunc;
+ }
+}
diff --git a/e2e/08-autofix/private-vars-underscore/Foo1AfterFix.sol b/e2e/08-autofix/private-vars-underscore/Foo1AfterFix.sol
new file mode 100644
index 00000000..87b80ad6
--- /dev/null
+++ b/e2e/08-autofix/private-vars-underscore/Foo1AfterFix.sol
@@ -0,0 +1,83 @@
+pragma solidity 0.8.0;
+
+library libraryName {
+ uint256 internal _lzarasa1;
+ uint256 internal _lzarasa2 = 2;
+ uint256 internal _lzarasa3;
+ uint256 private _lzarasa4;
+ uint256 private _lzarasa5 = 5;
+ uint256 private _lzarasa6;
+
+ uint256 public lzarasa7;
+ uint256 public lzarasa8 = 8;
+ uint256 public lzarasa9;
+
+ function fofo1() public {}
+ function fofo2() public {}
+ function _fofo3() internal {}
+ function _fofo4() internal {}
+}
+
+contract Foo1 {
+ uint256 internal _zarasa1;
+ uint256 internal _zarasa2 = 2;
+ uint256 internal _zarasa3;
+ uint256 private _zarasa4;
+ uint256 private _zarasa5 = 5;
+ uint256 private _zarasa6;
+
+ uint256 public zarasa7;
+ uint256 public zarasa8 = 8;
+ uint256 public zarasa9;
+
+ address payable internal constant _zarasa10 = '0x0';
+ address public constant zarasa11 = '0x0';
+ uint256 public immutable zarasa12 = 2;
+ uint256 _zarasa13;
+ uint256 _zarasa14;
+
+ mapping(address => uint256) internal _zarMapping1;
+ mapping(address => uint256) public zarMapping2;
+ mapping(address => uint256) internal _zarMapping3;
+ mapping(address => uint256) public zarMapping4;
+
+ function _fooA(uint bar) internal payable onlyOwner returns (uint256 barA) {
+ uint256 zarasaFunc;
+ }
+
+ function _fooB(uint bar) private onlyOwner returns (uint256 _barB) {
+ uint256 zarasaFunc;
+ }
+
+ function _fooC(uint bar) private onlyOwner returns (uint256 _barC) {
+ uint256 zarasaFunc;
+ }
+
+ function fooD(uint bar) external onlyOwner {
+ uint256 zarasaFunc;
+ }
+
+ function fooE(uint bar) public onlyOwner {
+ uint256 zarasaFunc;
+ }
+
+ function _fooF(uint bar) onlyOwner returns (uint256 _barF) {
+ uint256 zarasaFunc;
+ }
+
+ function _fooG(uint bar) onlyOwner {
+ uint256 zarasaFunc;
+ }
+
+ function fooH(uint bar) external onlyOwner {
+ uint256 zarasaFunc;
+ }
+
+ function fooI(uint bar) public onlyOwner {
+ uint256 zarasaFunc;
+ }
+
+ function _fooJ(uint bar) onlyOwner returns (uint256 barJ) {
+ uint256 zarasaFunc;
+ }
+}
diff --git a/e2e/08-autofix/private-vars-underscore/Foo1BeforeFix.sol b/e2e/08-autofix/private-vars-underscore/Foo1BeforeFix.sol
new file mode 100644
index 00000000..f8a07515
--- /dev/null
+++ b/e2e/08-autofix/private-vars-underscore/Foo1BeforeFix.sol
@@ -0,0 +1,83 @@
+pragma solidity 0.8.0;
+
+library libraryName {
+ uint256 internal lzarasa1;
+ uint256 internal lzarasa2 = 2;
+ uint256 internal _lzarasa3;
+ uint256 private lzarasa4;
+ uint256 private lzarasa5 = 5;
+ uint256 private _lzarasa6;
+
+ uint256 public _lzarasa7;
+ uint256 public _lzarasa8 = 8;
+ uint256 public lzarasa9;
+
+ function fofo1() public {}
+ function _fofo2() public {}
+ function _fofo3() internal {}
+ function fofo4() internal {}
+}
+
+contract Foo1 {
+ uint256 internal zarasa1;
+ uint256 internal zarasa2 = 2;
+ uint256 internal _zarasa3;
+ uint256 private zarasa4;
+ uint256 private zarasa5 = 5;
+ uint256 private _zarasa6;
+
+ uint256 public _zarasa7;
+ uint256 public _zarasa8 = 8;
+ uint256 public zarasa9;
+
+ address payable internal constant zarasa10 = '0x0';
+ address public constant _zarasa11 = '0x0';
+ uint256 public immutable _zarasa12 = 2;
+ uint256 _zarasa13;
+ uint256 zarasa14;
+
+ mapping(address => uint256) internal zarMapping1;
+ mapping(address => uint256) public _zarMapping2;
+ mapping(address => uint256) internal _zarMapping3;
+ mapping(address => uint256) public zarMapping4;
+
+ function fooA(uint bar) internal payable onlyOwner returns (uint256 barA) {
+ uint256 zarasaFunc;
+ }
+
+ function fooB(uint bar) private onlyOwner returns (uint256 _barB) {
+ uint256 zarasaFunc;
+ }
+
+ function fooC(uint bar) private onlyOwner returns (uint256 _barC) {
+ uint256 zarasaFunc;
+ }
+
+ function fooD(uint bar) external onlyOwner {
+ uint256 zarasaFunc;
+ }
+
+ function fooE(uint bar) public onlyOwner {
+ uint256 zarasaFunc;
+ }
+
+ function fooF(uint bar) onlyOwner returns (uint256 _barF) {
+ uint256 zarasaFunc;
+ }
+
+ function fooG(uint bar) onlyOwner {
+ uint256 zarasaFunc;
+ }
+
+ function _fooH(uint bar) external onlyOwner {
+ uint256 zarasaFunc;
+ }
+
+ function _fooI(uint bar) public onlyOwner {
+ uint256 zarasaFunc;
+ }
+
+ function _fooJ(uint bar) onlyOwner returns (uint256 barJ) {
+ uint256 zarasaFunc;
+ }
+}
diff --git a/e2e/autofix-test.js b/e2e/autofix-test.js
new file mode 100644
index 00000000..1f11d184
--- /dev/null
+++ b/e2e/autofix-test.js
@@ -0,0 +1,276 @@
+const chai = require('chai')
+const { expect } = chai
+const fs = require('fs-extra')
+const os = require('os')
+const path = require('path')
+const shell = require('shelljs')
+const spawnSync = require('spawn-sync')
+
+const E2E = true
+
+let params
+let currentConfig
+let currentFile
+let beforeFixFile
+let afterFixFile
+
+function retrieveParams(subpath) {
+ if (E2E) {
+ return { command: 'solhint', param1: '', path: '', subpath }
+ } else {
+ return { command: 'node', param1: 'solhint', path: 'e2e/08-autofix/', subpath }
+ }
+}
+
+function compareTextFiles(file1Path, file2Path) {
+ const file1Content = fs.readFileSync(file1Path, 'utf-8')
+ const file2Content = fs.readFileSync(file2Path, 'utf-8')
+
+ return file1Content === file2Content
+}
+
+function copyFile(sourcePath, destinationPath) {
+ shell.cp(sourcePath, destinationPath)
+}
+
+function useFixture(dir) {
+ beforeEach(`switch to ${dir}`, function () {
+ const fixturePath = path.join(__dirname, dir)
+
+ const tmpDirContainer = os.tmpdir()
+ this.testDirPath = path.join(tmpDirContainer, `solhint-tests-${dir}`)
+
+ fs.ensureDirSync(this.testDirPath)
+ fs.emptyDirSync(this.testDirPath)
+
+ fs.copySync(fixturePath, this.testDirPath)
+
+ shell.cd(this.testDirPath)
+ })
+}
+
+describe('e2e', function () {
+ let result = false
+
+ describe('autofix tests', () => {
+ if (E2E) {
+ useFixture('08-autofix')
+ }
+
+ describe('autofix command line options', () => {
+ before(function () {
+ params = retrieveParams('commands/')
+ currentConfig = `${params.path}${params.subpath}.solhint.json`
+ currentFile = `${params.path}${params.subpath}Foo1.sol`
+ beforeFixFile = `${params.path}${params.subpath}Foo1BeforeFix.sol`
+ afterFixFile = `${params.path}${params.subpath}Foo1AfterFix.sol`
+ })
+
+ describe('--fix without noPrompt', () => {
+ after(function () {
+ if (!E2E) {
+ copyFile(beforeFixFile, currentFile)
+ }
+ })
+
+ it('should terminate with --fix and user choose NOT to continue', () => {
+ const solhintProcess = spawnSync(
+ `${params.command}`,
+ [`${params.param1}`, '-c', currentConfig, currentFile, '--fix', '--disc'],
+ {
+ input: 'n\n', // Provide 'n' as input
+ shell: true,
+ }
+ )
+
+ expect(solhintProcess.status).to.equal(0)
+ expect(solhintProcess.stdout.toString().includes('Process terminated by user'))
+ })
+
+ it('should compare Foo1 file with template beforeFix file and they should match 1a', () => {
+ result = compareTextFiles(currentFile, beforeFixFile)
+ expect(result).to.be.true
+ })
+
+ it('should fix with --fix and user choose YES to continue', () => {
+ const solhintProcess = spawnSync(
+ `${params.command}`,
+ [`${params.param1}`, '-c', currentConfig, currentFile, '--fix', '--disc'],
+ {
+ input: 'y\n', // Provide 'y' as input
+ shell: true,
+ }
+ )
+
+ expect(solhintProcess.status).to.equal(1)
+ expect(solhintProcess.stdout.toString().includes('5 problems (5 errors, 0 warnings)'))
+ })
+ })
+ it('should check FOO1 does not change after test 1a', () => {
+ result = compareTextFiles(currentFile, beforeFixFile)
+ expect(result).to.be.true
+ })
+
+ describe('--fix with noPrompt', () => {
+ after(function () {
+ if (!E2E) {
+ copyFile(beforeFixFile, currentFile)
+ }
+ })
+
+ it('should compare Foo1 file with template beforeFix file and they should match 1b', () => {
+ result = compareTextFiles(currentFile, beforeFixFile)
+ expect(result).to.be.true
+ })
+
+ it('should fix file when noPrompt 1b', () => {
+ const { code, stdout } = shell.exec(
+ `${params.command} ${params.param1} -c ${currentConfig} ${currentFile} --fix --disc --noPrompt`
+ )
+
+ expect(code).to.equal(1)
+
+ const reportLines = stdout.split('\n')
+ const finalLine = '5 problems (5 errors, 0 warnings)'
+ expect(reportLines[reportLines.length - 3]).to.contain(finalLine)
+
+ result = compareTextFiles(currentFile, afterFixFile)
+ expect(result).to.be.true
+ })
+ })
+
+ it('should check FOO1 does not change after test 1b', () => {
+ result = compareTextFiles(currentFile, beforeFixFile)
+ expect(result).to.be.true
+ })
+ })
+
+ describe('autofix rule: explicit-types', () => {
+ before(function () {
+ params = retrieveParams('explicit-types/')
+ currentConfig = `${params.path}${params.subpath}.solhint.json`
+ currentFile = `${params.path}${params.subpath}Foo1.sol`
+ beforeFixFile = `${params.path}${params.subpath}Foo1BeforeFix.sol`
+ afterFixFile = `${params.path}${params.subpath}Foo1AfterFix.sol`
+ })
+ describe('--fix with noPrompt', () => {
+ after(function () {
+ if (!E2E) {
+ copyFile(beforeFixFile, currentFile)
+ }
+ })
+
+ it('should compare Foo1 file with template BEFORE FIX file and they should match 2', () => {
+ result = compareTextFiles(currentFile, beforeFixFile)
+ expect(result).to.be.true
+ })
+
+ it('should compare Foo1 file with template AFTER FIX file and they should match 2', () => {
+ const { code, stdout } = shell.exec(
+ `${params.command} ${params.param1} -c ${currentConfig} ${currentFile} --fix --disc --noPrompt`
+ )
+
+ expect(code).to.equal(1)
+
+ const reportLines = stdout.split('\n')
+ const finalLine = '5 problems (5 errors, 0 warnings)'
+ expect(reportLines[reportLines.length - 3]).to.contain(finalLine)
+
+ result = compareTextFiles(currentFile, afterFixFile)
+ expect(result).to.be.true
+ })
+ })
+
+ it('should check FOO1 does not change after test 2', () => {
+ result = compareTextFiles(currentFile, beforeFixFile)
+ expect(result).to.be.true
+ })
+ })
+
+ describe('autofix rule: no-console', () => {
+ before(function () {
+ params = retrieveParams('no-console/')
+ currentConfig = `${params.path}${params.subpath}.solhint.json`
+ currentFile = `${params.path}${params.subpath}Foo1.sol`
+ beforeFixFile = `${params.path}${params.subpath}Foo1BeforeFix.sol`
+ afterFixFile = `${params.path}${params.subpath}Foo1AfterFix.sol`
+ })
+ describe('--fix with noPrompt', () => {
+ after(function () {
+ if (!E2E) {
+ copyFile(beforeFixFile, currentFile)
+ }
+ })
+
+ it('should compare Foo1 file with template BEFORE FIX file and they should match 3', () => {
+ result = compareTextFiles(currentFile, beforeFixFile)
+ expect(result).to.be.true
+ })
+
+ it('should compare Foo1 file with template AFTER FIX file and they should match 3', () => {
+ const { code, stdout } = shell.exec(
+ `${params.command} ${params.param1} -c ${currentConfig} ${currentFile} --fix --disc --noPrompt`
+ )
+
+ expect(code).to.equal(1)
+
+ const reportLines = stdout.split('\n')
+ const finalLine = '9 problems (9 errors, 0 warnings)'
+ expect(reportLines[reportLines.length - 3]).to.contain(finalLine)
+
+ result = compareTextFiles(currentFile, afterFixFile)
+ expect(result).to.be.true
+ })
+ })
+
+ it('should check FOO1 does not change after test 3', () => {
+ result = compareTextFiles(currentFile, beforeFixFile)
+ expect(result).to.be.true
+ })
+ })
+
+ describe('autofix rule: private-vars-leading-underscore', () => {
+ before(function () {
+ params = retrieveParams('private-vars-underscore/')
+ currentConfig = `${params.path}${params.subpath}.solhint.json`
+ currentFile = `${params.path}${params.subpath}Foo1.sol`
+ beforeFixFile = `${params.path}${params.subpath}Foo1BeforeFix.sol`
+ afterFixFile = `${params.path}${params.subpath}Foo1AfterFix.sol`
+ })
+ describe('--fix with noPrompt', () => {
+ after(function () {
+ if (!E2E) {
+ copyFile(beforeFixFile, currentFile)
+ }
+ })
+
+ it('should compare Foo1 file with template BEFORE FIX file and they should match 4', () => {
+ result = compareTextFiles(currentFile, beforeFixFile)
+ expect(result).to.be.true
+ })
+
+ it('should compare Foo1 file with template AFTER FIX file and they should match 4', () => {
+ const { code, stdout } = shell.exec(
+ `${params.command} ${params.param1} -c ${currentConfig} ${currentFile} --fix --disc --noPrompt`
+ )
+
+ expect(code).to.equal(1)
+
+ const reportLines = stdout.split('\n')
+ const finalLine = '27 problems (27 errors, 0 warnings)'
+ expect(reportLines[reportLines.length - 3]).to.contain(finalLine)
+
+ result = compareTextFiles(currentFile, afterFixFile)
+ expect(result).to.be.true
+ })
+ })
+ it('should check FOO1 does not change after test 4', () => {
+ result = compareTextFiles(currentFile, beforeFixFile)
+ expect(result).to.be.true
+ })
+ })
+ })
+})
+
+// FALTA LA COMPARACION DEL FIX CON EL TEMPLATE FIX
+// FALTA LA PRUEBA DEL STORE TO FILE
diff --git a/e2e/formatters-test.js b/e2e/formatters-test.js
index 07f60a50..668c19d5 100644
--- a/e2e/formatters-test.js
+++ b/e2e/formatters-test.js
@@ -63,7 +63,7 @@ describe('e2e', function () {
}
expect(code).to.equal(0)
- const finalLine = '3 problem/s (3 warning/s) '
+ const finalLine = '3 problem/s (3 warning/s)'
expect(reportLines[reportLines.length - 2]).to.equal(finalLine)
})
it('should make the output report with unix formatter for Foo and Foo2 and Foo3', () => {
@@ -112,7 +112,7 @@ describe('e2e', function () {
expect(strExpected).to.equal(strOutput)
expect(code).to.equal(0)
})
- it('should make the output report with unix formatter for Foo and Foo2 and Foo3', () => {
+ it('should make the output report with json formatter for Foo and Foo2 and Foo3', () => {
const { code, stdout } = shell.exec(
`solhint ${PATH}contracts/Foo.sol ${PATH}contracts/Foo2.sol ${PATH}contracts/Foo3.sol -f ${formatterType}`
)
@@ -157,7 +157,7 @@ describe('e2e', function () {
}
expect(code).to.equal(0)
- const finalLine = '3 problem/s (3 warning/s) '
+ const finalLine = '3 problem/s (3 warning/s)'
expect(reportLines[reportLines.length - 2]).to.equal(finalLine)
})
it('should make the output report with compact formatter for Foo and Foo2 and Foo3', () => {
diff --git a/e2e/package.json b/e2e/package.json
index 3f817075..282f9edc 100644
--- a/e2e/package.json
+++ b/e2e/package.json
@@ -4,7 +4,7 @@
"description": "E2E tests for solhint",
"main": "index.js",
"scripts": {
- "test": "mocha test.js formatters-test.js"
+ "test": "mocha test.js formatters-test.js autofix-test.js"
},
"author": "",
"license": "MIT",
@@ -14,6 +14,7 @@
"fs-extra": "^11.1.0",
"get-stream": "^6.0.0",
"mocha": "^10.2.0",
- "shelljs": "^0.8.5"
+ "shelljs": "^0.8.5",
+ "spawn-sync": "^2.0.0"
}
}
diff --git a/lib/config/config-file.js b/lib/config/config-file.js
index f101d8ff..3ae3c1d1 100644
--- a/lib/config/config-file.js
+++ b/lib/config/config-file.js
@@ -1,4 +1,5 @@
const fs = require('fs')
+const path = require('path')
const _ = require('lodash')
const { cosmiconfigSync } = require('cosmiconfig')
const { ConfigMissingError } = require('../common/errors')
@@ -64,8 +65,15 @@ const loadConfig = (configFile) => {
return searchedFor.config || createEmptyConfig()
}
-const configGetter = (path) =>
- path.startsWith('solhint:') ? getSolhintCoreConfig(path) : require(`solhint-config-${path}`)
+const isAbsolute = path.isAbsolute
+const configGetter = (path) => {
+ if (isAbsolute(path)) {
+ return require(path)
+ }
+ return path.startsWith('solhint:')
+ ? getSolhintCoreConfig(path)
+ : require(`solhint-config-${path}`)
+}
const applyExtends = (config, getter = configGetter) => {
if (!config.extends) {
diff --git a/lib/rules/best-practises/explicit-types.js b/lib/rules/best-practises/explicit-types.js
index ebf7b60d..3273b340 100644
--- a/lib/rules/best-practises/explicit-types.js
+++ b/lib/rules/best-practises/explicit-types.js
@@ -7,6 +7,7 @@ const ALL_TYPES = EXPLICIT_TYPES.concat(IMPLICIT_TYPES)
const VALID_CONFIGURATION_OPTIONS = ['explicit', 'implicit']
const DEFAULT_OPTION = 'explicit'
const DEFAULT_SEVERITY = 'warn'
+let typesToSearch
const ruleId = 'explicit-types'
const meta = {
@@ -35,6 +36,10 @@ const meta = {
description: 'If implicit is selected',
code: 'uint public variableName',
},
+ {
+ description: 'If explicit is selected',
+ code: 'uint256 public variableName = uint256(5)',
+ },
],
bad: [
{
@@ -45,13 +50,23 @@ const meta = {
description: 'If implicit is selected',
code: 'uint256 public variableName',
},
+ {
+ description: 'At any setting',
+ code: 'uint public variableName = uint256(5)',
+ },
],
},
+ notes: [
+ {
+ note: 'Solhint allows this rule to automatically fix the code with `--fix` option',
+ },
+ ],
},
isDefault: false,
recommended: true,
defaultSetup: [DEFAULT_SEVERITY, DEFAULT_OPTION],
+ fixable: true,
schema: {
type: 'string',
@@ -65,9 +80,29 @@ class ExplicitTypesChecker extends BaseChecker {
this.configOption =
(config && config.getStringFromArray(ruleId, DEFAULT_OPTION)) || DEFAULT_OPTION
this.isExplicit = this.configOption === 'explicit'
+
+ // if explicit, it will search for implicit and viceversa
+ if (this.isExplicit) {
+ typesToSearch = IMPLICIT_TYPES
+ } else {
+ typesToSearch = EXPLICIT_TYPES
+ }
}
VariableDeclaration(node) {
+ this.validateInNode(node)
+ }
+
+ VariableDeclarationStatement(node) {
+ if (!node.initialValue) return
+ this.validateInNode(node.initialValue)
+ }
+
+ ExpressionStatement(node) {
+ this.validateInNode(node)
+ }
+
+ validateInNode(node) {
if (!VALID_CONFIGURATION_OPTIONS.includes(this.configOption)) {
this.error(node, 'Error: Config error on [explicit-types]. See explicit-types.md.')
return
@@ -85,12 +120,7 @@ class ExplicitTypesChecker extends BaseChecker {
// if defined variables are not to be checked (example address type), return
if (varsToBeChecked && varsToBeChecked.length === 0) return
- // if explicit, it will search for implicit and viceversa
- if (this.isExplicit) {
- this.validateVariables(IMPLICIT_TYPES, node, varsToBeChecked)
- } else {
- this.validateVariables(EXPLICIT_TYPES, node, varsToBeChecked)
- }
+ this.validateVariables(typesToSearch, node, varsToBeChecked)
}
validateVariables(configType, node, varsToBeChecked) {
@@ -98,11 +128,24 @@ class ExplicitTypesChecker extends BaseChecker {
if (errorVars && errorVars.length > 0) {
for (const errorVar of errorVars) {
- this.error(node, `Rule is set with ${this.configOption} type [var/s: ${errorVar}]`)
+ this.error(
+ node,
+ `Rule is set with ${this.configOption} type [var/s: ${errorVar}]`,
+ this.fixStatement(node, errorVar)
+ )
}
}
}
+ fixStatement(typeNameNode, errorVar) {
+ const configFileIndex = typesToSearch.findIndex((arg) => arg === errorVar)
+ return (fixer) =>
+ fixer.replaceTextRange(
+ typeNameNode.typeName.range,
+ this.isExplicit ? EXPLICIT_TYPES[configFileIndex] : IMPLICIT_TYPES[configFileIndex]
+ )
+ }
+
findNamesOfElementaryTypeName(jsonObject, typeToFind) {
const names = []
diff --git a/lib/rules/best-practises/no-console.js b/lib/rules/best-practises/no-console.js
index e6b26af6..c6156fb3 100644
--- a/lib/rules/best-practises/no-console.js
+++ b/lib/rules/best-practises/no-console.js
@@ -28,6 +28,7 @@ const meta = {
isDefault: true,
recommended: true,
defaultSetup: 'error',
+ fixable: true,
schema: null,
}
@@ -38,13 +39,13 @@ class NoConsoleLogChecker extends BaseChecker {
ImportDirective(node) {
if (this.isConsoleImport(node)) {
- this.error(node, 'Unexpected import of console file')
+ this.error(node, 'Unexpected import of console file', this.fixStatement(node, 'import'))
}
}
FunctionCall(node) {
if (this.isConsoleLog(node)) {
- this.error(node, 'Unexpected console statement')
+ this.error(node, 'Unexpected console statement', this.fixStatement(node, 'call'))
}
}
@@ -64,6 +65,18 @@ class NoConsoleLogChecker extends BaseChecker {
node.path === 'forge-std/console2.sol'
)
}
+
+ fixStatement(node, type) {
+ const range = node.range
+ // to remove the ';'
+ if (type === 'call') range[1] += 1
+
+ // // to remove the ';' and the 'enter'
+ // if (type === 'call') range[1] += 2
+ // else range[1] += 1 // to remove the 'enter'
+
+ return (fixer) => fixer.removeRange(range)
+ }
}
module.exports = NoConsoleLogChecker
diff --git a/lib/rules/best-practises/no-empty-blocks.js b/lib/rules/best-practises/no-empty-blocks.js
index 2df7a238..a8c81bf7 100644
--- a/lib/rules/best-practises/no-empty-blocks.js
+++ b/lib/rules/best-practises/no-empty-blocks.js
@@ -25,6 +25,11 @@ const meta = {
},
],
},
+ notes: [
+ {
+ note: 'The rule ignores an empty constructor by default as long as base contracts are being inherited. See "Empty Constructor" example.',
+ },
+ ],
},
isDefault: false,
diff --git a/lib/rules/best-practises/one-contract-per-file.js b/lib/rules/best-practises/one-contract-per-file.js
index 200ab771..51cdf004 100644
--- a/lib/rules/best-practises/one-contract-per-file.js
+++ b/lib/rules/best-practises/one-contract-per-file.js
@@ -33,7 +33,7 @@ class OneContractPerFileChecker extends BaseChecker {
SourceUnit(node) {
const contractDefinitionCount = node.children.reduce((count, child) => {
- if (child.type === 'ContractDefinition') {
+ if (child.type === 'ContractDefinition' && child.kind !== 'interface') {
return count + 1
}
return count
diff --git a/lib/rules/naming/private-vars-leading-underscore.js b/lib/rules/naming/private-vars-leading-underscore.js
index dc418728..bcbfdff3 100644
--- a/lib/rules/naming/private-vars-leading-underscore.js
+++ b/lib/rules/naming/private-vars-leading-underscore.js
@@ -11,7 +11,8 @@ const meta = {
type: 'naming',
docs: {
- description: 'Private and internal names must start with a single underscore.',
+ description:
+ "Non-external functions and state variables should start with a single underscore. Others, shouldn't",
category: 'Style Guide Rules',
options: [
{
@@ -20,15 +21,70 @@ const meta = {
},
{
description:
- 'A JSON object with a single property "strict" specifying if the rule should apply to non state variables. Default: { strict: false }.',
+ 'A JSON object with a single property "strict" specifying if the rule should apply to ALL non state variables. Default: { strict: false }.',
default: JSON.stringify(DEFAULT_OPTION),
},
],
+ examples: {
+ good: [
+ {
+ description: 'Internal function with correct naming',
+ code: 'function _thisIsInternal() internal {}',
+ },
+ {
+ description: 'Private function with correct naming',
+ code: 'function _thisIsPrivate() private {}',
+ },
+ {
+ description: 'Internal state variable with correct naming',
+ code: 'uint256 internal _thisIsInternalVariable;',
+ },
+ {
+ description:
+ 'Internal state variable with correct naming (no visibility is considered internal)',
+ code: 'uint256 _thisIsInternalVariable;',
+ },
+ ],
+ bad: [
+ {
+ description: 'Internal function with incorrect naming',
+ code: 'function thisIsInternal() internal {}',
+ },
+ {
+ description: 'Private function with incorrect naming',
+ code: 'function thisIsPrivate() private {}',
+ },
+ {
+ description: 'Internal state variable with incorrect naming',
+ code: 'uint256 internal thisIsInternalVariable;',
+ },
+ {
+ description:
+ 'Internal state variable with incorrect naming (no visibility is considered internal)',
+ code: 'uint256 thisIsInternalVariable;',
+ },
+ ],
+ },
+ notes: [
+ {
+ note: 'This rule considers functions and variables in Libraries as well',
+ },
+ {
+ note: 'This rule skips external and public functions',
+ },
+ {
+ note: 'This rule skips external and public state variables',
+ },
+ {
+ note: 'See [here](https://docs.soliditylang.org/en/latest/style-guide.html#underscore-prefix-for-non-external-functions-and-variables) for further information',
+ },
+ ],
},
isDefault: false,
recommended: false,
defaultSetup: [DEFAULT_SEVERITY, DEFAULT_OPTION],
+ fixable: true,
schema: {
type: 'object',
@@ -47,15 +103,15 @@ class PrivateVarsLeadingUnderscoreChecker extends BaseChecker {
this.isStrict = config && config.getObjectPropertyBoolean(ruleId, 'strict', DEFAULT_STRICTNESS)
}
- ContractDefinition(node) {
- if (node.kind === 'library') {
- this.inLibrary = true
- }
- }
+ // ContractDefinition(node) {
+ // if (node.kind === 'library') {
+ // this.inLibrary = true
+ // }
+ // }
- 'ContractDefinition:exit'() {
- this.inLibrary = false
- }
+ // 'ContractDefinition:exit'() {
+ // this.inLibrary = false
+ // }
FunctionDefinition(node) {
if (!node.name) {
@@ -63,9 +119,10 @@ class PrivateVarsLeadingUnderscoreChecker extends BaseChecker {
}
const isPrivate = node.visibility === 'private'
- const isInternal = node.visibility === 'internal'
- const shouldHaveLeadingUnderscore = isPrivate || (!this.inLibrary && isInternal)
- this.validateName(node, shouldHaveLeadingUnderscore)
+ const isInternal = node.visibility === 'internal' || node.visibility === 'default'
+ // const shouldHaveLeadingUnderscore = isPrivate || (!this.inLibrary && isInternal)
+ const shouldHaveLeadingUnderscore = isPrivate || isInternal
+ this.validateName(node, shouldHaveLeadingUnderscore, 'function')
}
StateVariableDeclaration() {
@@ -80,7 +137,7 @@ class PrivateVarsLeadingUnderscoreChecker extends BaseChecker {
if (!this.inStateVariableDeclaration) {
// if strict is enabled, non-state vars should not start with leading underscore
if (this.isStrict) {
- this.validateName(node, false)
+ this.validateName(node, false, 'variable')
}
return
}
@@ -88,25 +145,43 @@ class PrivateVarsLeadingUnderscoreChecker extends BaseChecker {
const isPrivate = node.visibility === 'private'
const isInternal = node.visibility === 'internal' || node.visibility === 'default'
const shouldHaveLeadingUnderscore = isPrivate || isInternal
- this.validateName(node, shouldHaveLeadingUnderscore)
+ this.validateName(node, shouldHaveLeadingUnderscore, 'variable')
}
- validateName(node, shouldHaveLeadingUnderscore) {
+ validateName(node, shouldHaveLeadingUnderscore, type) {
if (node.name === null) {
return
}
if (naming.hasLeadingUnderscore(node.name) !== shouldHaveLeadingUnderscore) {
- this._error(node, node.name, shouldHaveLeadingUnderscore)
+ this._error(node, node.name, shouldHaveLeadingUnderscore, type)
}
}
- _error(node, name, shouldHaveLeadingUnderscore) {
+ _error(node, name, shouldHaveLeadingUnderscore, type) {
this.error(
node,
- `'${name}' ${shouldHaveLeadingUnderscore ? 'should' : 'should not'} start with _`
+ `'${name}' ${shouldHaveLeadingUnderscore ? 'should' : 'should not'} start with _`,
+ this.fixStatement(node, shouldHaveLeadingUnderscore, type)
)
}
+
+ fixStatement(node, shouldHaveLeadingUnderscore, type) {
+ let range
+
+ if (type === 'function') {
+ range = node.range
+ range[0] += 8
+ } else {
+ range = node.identifier.range
+ range[0] -= 1
+ }
+
+ return (fixer) =>
+ shouldHaveLeadingUnderscore
+ ? fixer.insertTextBeforeRange(range, ' _')
+ : fixer.removeRange([range[0] + 1, range[0] + 1])
+ }
}
module.exports = PrivateVarsLeadingUnderscoreChecker
diff --git a/lib/rules/security/avoid-sha3.js b/lib/rules/security/avoid-sha3.js
index 048679a8..a8d5c0bb 100644
--- a/lib/rules/security/avoid-sha3.js
+++ b/lib/rules/security/avoid-sha3.js
@@ -7,6 +7,11 @@ const meta = {
docs: {
description: `Use "keccak256" instead of deprecated "sha3".`,
category: 'Security Rules',
+ notes: [
+ {
+ note: 'Solhint allows this rule to automatically fix the code with `--fix` option',
+ },
+ ],
},
isDefault: false,
diff --git a/lib/rules/security/avoid-throw.js b/lib/rules/security/avoid-throw.js
index 30a942a0..72a9f4f1 100644
--- a/lib/rules/security/avoid-throw.js
+++ b/lib/rules/security/avoid-throw.js
@@ -7,6 +7,11 @@ const meta = {
docs: {
description: `"throw" is deprecated, avoid to use it.`,
category: 'Security Rules',
+ notes: [
+ {
+ note: 'Solhint allows this rule to automatically fix the code with `--fix` option',
+ },
+ ],
},
isDefault: false,
diff --git a/package-lock.json b/package-lock.json
index c5d4d4ea..633ddb03 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,12 +1,12 @@
{
"name": "solhint",
- "version": "3.5.1",
+ "version": "3.6.3",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"name": "solhint",
- "version": "3.5.1",
+ "version": "3.6.3",
"license": "MIT",
"dependencies": {
"@solidity-parser/parser": "^0.16.0",
@@ -20,6 +20,7 @@
"glob": "^8.0.3",
"ignore": "^5.2.4",
"js-yaml": "^4.1.0",
+ "latest-version": "^7.0.0",
"lodash": "^4.17.21",
"pluralize": "^8.0.0",
"semver": "^7.5.2",
@@ -699,6 +700,49 @@
"node": ">= 8"
}
},
+ "node_modules/@pnpm/config.env-replace": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/@pnpm/config.env-replace/-/config.env-replace-1.1.0.tgz",
+ "integrity": "sha512-htyl8TWnKL7K/ESFa1oW2UB5lVDxuF5DpM7tBi6Hu2LNL3mWkIzNLG6N4zoCUP1lCKNxWy/3iu8mS8MvToGd6w==",
+ "engines": {
+ "node": ">=12.22.0"
+ }
+ },
+ "node_modules/@pnpm/network.ca-file": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/@pnpm/network.ca-file/-/network.ca-file-1.0.2.tgz",
+ "integrity": "sha512-YcPQ8a0jwYU9bTdJDpXjMi7Brhkr1mXsXrUJvjqM2mQDgkRiz8jFaQGOdaLxgjtUfQgZhKy/O3cG/YwmgKaxLA==",
+ "dependencies": {
+ "graceful-fs": "4.2.10"
+ },
+ "engines": {
+ "node": ">=12.22.0"
+ }
+ },
+ "node_modules/@pnpm/npm-conf": {
+ "version": "2.2.2",
+ "resolved": "https://registry.npmjs.org/@pnpm/npm-conf/-/npm-conf-2.2.2.tgz",
+ "integrity": "sha512-UA91GwWPhFExt3IizW6bOeY/pQ0BkuNwKjk9iQW9KqxluGCrg4VenZ0/L+2Y0+ZOtme72EVvg6v0zo3AMQRCeA==",
+ "dependencies": {
+ "@pnpm/config.env-replace": "^1.1.0",
+ "@pnpm/network.ca-file": "^1.0.1",
+ "config-chain": "^1.1.11"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@sindresorhus/is": {
+ "version": "5.6.0",
+ "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-5.6.0.tgz",
+ "integrity": "sha512-TV7t8GKYaJWsn00tFDqBw8+Uqmr8A0fRU1tvTQhyZzGv0sJCGRQL3JGMI3ucuKo3XIZdUP+Lx7/gh2t3lewy7g==",
+ "engines": {
+ "node": ">=14.16"
+ },
+ "funding": {
+ "url": "https://github.com/sindresorhus/is?sponsor=1"
+ }
+ },
"node_modules/@solidity-parser/parser": {
"version": "0.16.0",
"resolved": "https://registry.npmjs.org/@solidity-parser/parser/-/parser-0.16.0.tgz",
@@ -707,6 +751,22 @@
"antlr4ts": "^0.5.0-alpha.4"
}
},
+ "node_modules/@szmarczak/http-timer": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-5.0.1.tgz",
+ "integrity": "sha512-+PmQX0PiAYPMeVYe237LJAYvOMYW1j2rH5YROyS3b4CTVJum34HfRvKvAzozHAQG0TnHNdUfY9nCeUyRAs//cw==",
+ "dependencies": {
+ "defer-to-connect": "^2.0.1"
+ },
+ "engines": {
+ "node": ">=14.16"
+ }
+ },
+ "node_modules/@types/http-cache-semantics": {
+ "version": "4.0.2",
+ "resolved": "https://registry.npmjs.org/@types/http-cache-semantics/-/http-cache-semantics-4.0.2.tgz",
+ "integrity": "sha512-FD+nQWA2zJjh4L9+pFXqWOi0Hs1ryBCfI+985NjluQ1p8EYtoLvjLOKidXBtZ4/IcxDX4o8/E8qDS3540tNliw=="
+ },
"node_modules/@types/json5": {
"version": "0.0.29",
"resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz",
@@ -1052,6 +1112,31 @@
"node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7"
}
},
+ "node_modules/cacheable-lookup": {
+ "version": "7.0.0",
+ "resolved": "https://registry.npmjs.org/cacheable-lookup/-/cacheable-lookup-7.0.0.tgz",
+ "integrity": "sha512-+qJyx4xiKra8mZrcwhjMRMUhD5NR1R8esPkzIYxX96JiecFoxAXFuz/GpR3+ev4PE1WamHip78wV0vcmPQtp8w==",
+ "engines": {
+ "node": ">=14.16"
+ }
+ },
+ "node_modules/cacheable-request": {
+ "version": "10.2.14",
+ "resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-10.2.14.tgz",
+ "integrity": "sha512-zkDT5WAF4hSSoUgyfg5tFIxz8XQK+25W/TLVojJTMKBaxevLBBtLxgqguAuVQB8PVW79FVjHcU+GJ9tVbDZ9mQ==",
+ "dependencies": {
+ "@types/http-cache-semantics": "^4.0.2",
+ "get-stream": "^6.0.1",
+ "http-cache-semantics": "^4.1.1",
+ "keyv": "^4.5.3",
+ "mimic-response": "^4.0.0",
+ "normalize-url": "^8.0.0",
+ "responselike": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=14.16"
+ }
+ },
"node_modules/caching-transform": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/caching-transform/-/caching-transform-4.0.0.tgz",
@@ -1241,6 +1326,15 @@
"integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==",
"dev": true
},
+ "node_modules/config-chain": {
+ "version": "1.1.13",
+ "resolved": "https://registry.npmjs.org/config-chain/-/config-chain-1.1.13.tgz",
+ "integrity": "sha512-qj+f8APARXHrM0hraqXYb2/bOVSV4PvJQlNZ/DVj0QrmNM2q2euizkeuVckQ57J+W0mRH6Hvi+k50M4Jul2VRQ==",
+ "dependencies": {
+ "ini": "^1.3.4",
+ "proto-list": "~1.2.1"
+ }
+ },
"node_modules/confusing-browser-globals": {
"version": "1.0.11",
"resolved": "https://registry.npmjs.org/confusing-browser-globals/-/confusing-browser-globals-1.0.11.tgz",
@@ -1366,6 +1460,39 @@
"node": ">=0.10.0"
}
},
+ "node_modules/decompress-response": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz",
+ "integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==",
+ "dependencies": {
+ "mimic-response": "^3.1.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/decompress-response/node_modules/mimic-response": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz",
+ "integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==",
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/deep-extend": {
+ "version": "0.6.0",
+ "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz",
+ "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==",
+ "engines": {
+ "node": ">=4.0.0"
+ }
+ },
"node_modules/deep-is": {
"version": "0.1.4",
"resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz",
@@ -1387,6 +1514,14 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
+ "node_modules/defer-to-connect": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/defer-to-connect/-/defer-to-connect-2.0.1.tgz",
+ "integrity": "sha512-4tvttepXG1VaYGrRibk5EwJd1t4udunSOVMdLSAL6mId1ix438oPwPZMALY41FCijukO1L0twNcGsdzS7dHgDg==",
+ "engines": {
+ "node": ">=10"
+ }
+ },
"node_modules/define-properties": {
"version": "1.1.4",
"resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.4.tgz",
@@ -2100,6 +2235,14 @@
"node": ">= 0.12"
}
},
+ "node_modules/form-data-encoder": {
+ "version": "2.1.4",
+ "resolved": "https://registry.npmjs.org/form-data-encoder/-/form-data-encoder-2.1.4.tgz",
+ "integrity": "sha512-yDYSgNMraqvnxiEXO4hi88+YZxaHC6QKzb5N84iRCTDeRO7ZALpir/lVmf/uXUhnwUr2O4HU8s/n6x+yNjQkHw==",
+ "engines": {
+ "node": ">= 14.17"
+ }
+ },
"node_modules/fromentries": {
"version": "1.3.2",
"resolved": "https://registry.npmjs.org/fromentries/-/fromentries-1.3.2.tgz",
@@ -2213,6 +2356,17 @@
"node": ">=8.0.0"
}
},
+ "node_modules/get-stream": {
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz",
+ "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==",
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
"node_modules/get-symbol-description": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.0.0.tgz",
@@ -2329,11 +2483,34 @@
"url": "https://github.com/sponsors/ljharb"
}
},
+ "node_modules/got": {
+ "version": "12.6.1",
+ "resolved": "https://registry.npmjs.org/got/-/got-12.6.1.tgz",
+ "integrity": "sha512-mThBblvlAF1d4O5oqyvN+ZxLAYwIJK7bpMxgYqPD9okW0C3qm5FFn7k811QrcuEBwaogR3ngOFoCfs6mRv7teQ==",
+ "dependencies": {
+ "@sindresorhus/is": "^5.2.0",
+ "@szmarczak/http-timer": "^5.0.1",
+ "cacheable-lookup": "^7.0.0",
+ "cacheable-request": "^10.2.8",
+ "decompress-response": "^6.0.0",
+ "form-data-encoder": "^2.1.2",
+ "get-stream": "^6.0.1",
+ "http2-wrapper": "^2.1.10",
+ "lowercase-keys": "^3.0.0",
+ "p-cancelable": "^3.0.0",
+ "responselike": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=14.16"
+ },
+ "funding": {
+ "url": "https://github.com/sindresorhus/got?sponsor=1"
+ }
+ },
"node_modules/graceful-fs": {
"version": "4.2.10",
"resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.10.tgz",
- "integrity": "sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==",
- "dev": true
+ "integrity": "sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA=="
},
"node_modules/grapheme-splitter": {
"version": "1.0.4",
@@ -2484,6 +2661,11 @@
"integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==",
"dev": true
},
+ "node_modules/http-cache-semantics": {
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.1.tgz",
+ "integrity": "sha512-er295DKPVsV82j5kw1Gjt+ADA/XYHsajl82cGNQG2eyoPkvgUhX+nDIyelzhIWbbsXP39EHcI6l5tYs2FYqYXQ=="
+ },
"node_modules/http-signature": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz",
@@ -2499,6 +2681,18 @@
"npm": ">=1.3.7"
}
},
+ "node_modules/http2-wrapper": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/http2-wrapper/-/http2-wrapper-2.2.0.tgz",
+ "integrity": "sha512-kZB0wxMo0sh1PehyjJUWRFEd99KC5TLjZ2cULC4f9iqJBAmKQQXEICjxl5iPJRwP40dpeHFqqhm7tYCvODpqpQ==",
+ "dependencies": {
+ "quick-lru": "^5.1.1",
+ "resolve-alpn": "^1.2.0"
+ },
+ "engines": {
+ "node": ">=10.19.0"
+ }
+ },
"node_modules/husky": {
"version": "8.0.3",
"resolved": "https://registry.npmjs.org/husky/-/husky-8.0.3.tgz",
@@ -2569,6 +2763,11 @@
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
"integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="
},
+ "node_modules/ini": {
+ "version": "1.3.8",
+ "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz",
+ "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew=="
+ },
"node_modules/internal-slot": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.4.tgz",
@@ -3107,6 +3306,11 @@
"node": ">=4"
}
},
+ "node_modules/json-buffer": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz",
+ "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ=="
+ },
"node_modules/json-parse-even-better-errors": {
"version": "2.3.1",
"resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz",
@@ -3162,6 +3366,28 @@
"node": ">=0.6.0"
}
},
+ "node_modules/keyv": {
+ "version": "4.5.4",
+ "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz",
+ "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==",
+ "dependencies": {
+ "json-buffer": "3.0.1"
+ }
+ },
+ "node_modules/latest-version": {
+ "version": "7.0.0",
+ "resolved": "https://registry.npmjs.org/latest-version/-/latest-version-7.0.0.tgz",
+ "integrity": "sha512-KvNT4XqAMzdcL6ka6Tl3i2lYeFDgXNCuIX+xNx6ZMVR1dFq+idXd9FLKNMOIx0t9mJ9/HudyX4oZWXZQ0UJHeg==",
+ "dependencies": {
+ "package-json": "^8.1.0"
+ },
+ "engines": {
+ "node": ">=14.16"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
"node_modules/lcov-parse": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/lcov-parse/-/lcov-parse-1.0.0.tgz",
@@ -3251,6 +3477,17 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
+ "node_modules/lowercase-keys": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-3.0.0.tgz",
+ "integrity": "sha512-ozCC6gdQ+glXOQsveKD0YsDy8DSQFjDTz4zyzEHNV5+JP5D62LmfDZ6o1cycFx9ouG940M5dE8C8CTewdj2YWQ==",
+ "engines": {
+ "node": "^12.20.0 || ^14.13.1 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
"node_modules/lru-cache": {
"version": "5.1.1",
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz",
@@ -3318,6 +3555,17 @@
"node": ">= 0.6"
}
},
+ "node_modules/mimic-response": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-4.0.0.tgz",
+ "integrity": "sha512-e5ISH9xMYU0DzrT+jl8q2ze9D6eWBto+I8CNpe+VI+K2J/F/k3PdkdTdz4wvGVH4NTpo+NRYTVIuMQEMMcsLqg==",
+ "engines": {
+ "node": "^12.20.0 || ^14.13.1 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
"node_modules/minimatch": {
"version": "3.1.2",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
@@ -3334,7 +3582,6 @@
"version": "1.2.7",
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.7.tgz",
"integrity": "sha512-bzfL1YUZsP41gmu/qjrEk0Q6i2ix/cVeAhbCbqH9u3zYutS1cLg00qhrD0M2MVdCcx4Sc0UpP2eBWo9rotpq6g==",
- "dev": true,
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
@@ -3513,6 +3760,17 @@
"node": ">=0.10.0"
}
},
+ "node_modules/normalize-url": {
+ "version": "8.0.0",
+ "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-8.0.0.tgz",
+ "integrity": "sha512-uVFpKhj5MheNBJRTiMZ9pE/7hD1QTeEvugSJW/OmLzAp78PB5O6adfMNTvmfKhXBkvCzC+rqifWcVYpGFwTjnw==",
+ "engines": {
+ "node": ">=14.16"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
"node_modules/nyc": {
"version": "15.1.0",
"resolved": "https://registry.npmjs.org/nyc/-/nyc-15.1.0.tgz",
@@ -3818,6 +4076,14 @@
"node": ">= 0.8.0"
}
},
+ "node_modules/p-cancelable": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-3.0.0.tgz",
+ "integrity": "sha512-mlVgR3PGuzlo0MmTdk4cXqXWlwQDLnONTAg6sm62XkMJEiRxN3GL3SffkYvqwonbkJBcrI7Uvv5Zh9yjvn2iUw==",
+ "engines": {
+ "node": ">=12.20"
+ }
+ },
"node_modules/p-limit": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz",
@@ -3884,6 +4150,23 @@
"node": ">=8"
}
},
+ "node_modules/package-json": {
+ "version": "8.1.1",
+ "resolved": "https://registry.npmjs.org/package-json/-/package-json-8.1.1.tgz",
+ "integrity": "sha512-cbH9IAIJHNj9uXi196JVsRlt7cHKak6u/e6AkL/bkRelZ7rlL3X1YKxsZwa36xipOEKAsdtmaG6aAJoM1fx2zA==",
+ "dependencies": {
+ "got": "^12.1.0",
+ "registry-auth-token": "^5.0.1",
+ "registry-url": "^6.0.0",
+ "semver": "^7.3.7"
+ },
+ "engines": {
+ "node": ">=14.16"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
"node_modules/parent-module": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz",
@@ -4097,6 +4380,11 @@
"node": ">=8"
}
},
+ "node_modules/proto-list": {
+ "version": "1.2.4",
+ "resolved": "https://registry.npmjs.org/proto-list/-/proto-list-1.2.4.tgz",
+ "integrity": "sha512-vtK/94akxsTMhe0/cbfpR+syPuszcuwhqVjJq26CuNDgFGj682oRBXOP5MJpv2r7JtE8MsiepGIqvvOTBwn2vA=="
+ },
"node_modules/psl": {
"version": "1.9.0",
"resolved": "https://registry.npmjs.org/psl/-/psl-1.9.0.tgz",
@@ -4140,6 +4428,17 @@
}
]
},
+ "node_modules/quick-lru": {
+ "version": "5.1.1",
+ "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-5.1.1.tgz",
+ "integrity": "sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA==",
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
"node_modules/randombytes": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz",
@@ -4149,6 +4448,28 @@
"safe-buffer": "^5.1.0"
}
},
+ "node_modules/rc": {
+ "version": "1.2.8",
+ "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz",
+ "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==",
+ "dependencies": {
+ "deep-extend": "^0.6.0",
+ "ini": "~1.3.0",
+ "minimist": "^1.2.0",
+ "strip-json-comments": "~2.0.1"
+ },
+ "bin": {
+ "rc": "cli.js"
+ }
+ },
+ "node_modules/rc/node_modules/strip-json-comments": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz",
+ "integrity": "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
"node_modules/readdirp": {
"version": "3.6.0",
"resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz",
@@ -4202,6 +4523,31 @@
"url": "https://github.com/sponsors/mysticatea"
}
},
+ "node_modules/registry-auth-token": {
+ "version": "5.0.2",
+ "resolved": "https://registry.npmjs.org/registry-auth-token/-/registry-auth-token-5.0.2.tgz",
+ "integrity": "sha512-o/3ikDxtXaA59BmZuZrJZDJv8NMDGSj+6j6XaeBmHw8eY1i1qd9+6H+LjVvQXx3HN6aRCGa1cUdJ9RaJZUugnQ==",
+ "dependencies": {
+ "@pnpm/npm-conf": "^2.1.0"
+ },
+ "engines": {
+ "node": ">=14"
+ }
+ },
+ "node_modules/registry-url": {
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/registry-url/-/registry-url-6.0.1.tgz",
+ "integrity": "sha512-+crtS5QjFRqFCoQmvGduwYWEBng99ZvmFvF+cUJkGYF1L1BfU8C6Zp9T7f5vPAwyLkUExpvK+ANVZmGU49qi4Q==",
+ "dependencies": {
+ "rc": "1.2.8"
+ },
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
"node_modules/release-zalgo": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/release-zalgo/-/release-zalgo-1.0.0.tgz",
@@ -4305,6 +4651,11 @@
"url": "https://github.com/sponsors/ljharb"
}
},
+ "node_modules/resolve-alpn": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/resolve-alpn/-/resolve-alpn-1.2.1.tgz",
+ "integrity": "sha512-0a1F4l73/ZFZOakJnQ3FvkJ2+gSTQWz/r2KE5OdDY0TxPm5h4GkqkWWfM47T7HsbnOtcJVEF4epCVy6u7Q3K+g=="
+ },
"node_modules/resolve-from": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz",
@@ -4313,6 +4664,20 @@
"node": ">=4"
}
},
+ "node_modules/responselike": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/responselike/-/responselike-3.0.0.tgz",
+ "integrity": "sha512-40yHxbNcl2+rzXvZuVkrYohathsSJlMTXKryG5y8uciHv1+xDLHQpgjG64JUO9nrEq2jGLH6IZ8BcZyw3wrweg==",
+ "dependencies": {
+ "lowercase-keys": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=14.16"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
"node_modules/reusify": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz",
@@ -5703,6 +6068,34 @@
"fastq": "^1.6.0"
}
},
+ "@pnpm/config.env-replace": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/@pnpm/config.env-replace/-/config.env-replace-1.1.0.tgz",
+ "integrity": "sha512-htyl8TWnKL7K/ESFa1oW2UB5lVDxuF5DpM7tBi6Hu2LNL3mWkIzNLG6N4zoCUP1lCKNxWy/3iu8mS8MvToGd6w=="
+ },
+ "@pnpm/network.ca-file": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/@pnpm/network.ca-file/-/network.ca-file-1.0.2.tgz",
+ "integrity": "sha512-YcPQ8a0jwYU9bTdJDpXjMi7Brhkr1mXsXrUJvjqM2mQDgkRiz8jFaQGOdaLxgjtUfQgZhKy/O3cG/YwmgKaxLA==",
+ "requires": {
+ "graceful-fs": "4.2.10"
+ }
+ },
+ "@pnpm/npm-conf": {
+ "version": "2.2.2",
+ "resolved": "https://registry.npmjs.org/@pnpm/npm-conf/-/npm-conf-2.2.2.tgz",
+ "integrity": "sha512-UA91GwWPhFExt3IizW6bOeY/pQ0BkuNwKjk9iQW9KqxluGCrg4VenZ0/L+2Y0+ZOtme72EVvg6v0zo3AMQRCeA==",
+ "requires": {
+ "@pnpm/config.env-replace": "^1.1.0",
+ "@pnpm/network.ca-file": "^1.0.1",
+ "config-chain": "^1.1.11"
+ }
+ },
+ "@sindresorhus/is": {
+ "version": "5.6.0",
+ "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-5.6.0.tgz",
+ "integrity": "sha512-TV7t8GKYaJWsn00tFDqBw8+Uqmr8A0fRU1tvTQhyZzGv0sJCGRQL3JGMI3ucuKo3XIZdUP+Lx7/gh2t3lewy7g=="
+ },
"@solidity-parser/parser": {
"version": "0.16.0",
"resolved": "https://registry.npmjs.org/@solidity-parser/parser/-/parser-0.16.0.tgz",
@@ -5711,6 +6104,19 @@
"antlr4ts": "^0.5.0-alpha.4"
}
},
+ "@szmarczak/http-timer": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-5.0.1.tgz",
+ "integrity": "sha512-+PmQX0PiAYPMeVYe237LJAYvOMYW1j2rH5YROyS3b4CTVJum34HfRvKvAzozHAQG0TnHNdUfY9nCeUyRAs//cw==",
+ "requires": {
+ "defer-to-connect": "^2.0.1"
+ }
+ },
+ "@types/http-cache-semantics": {
+ "version": "4.0.2",
+ "resolved": "https://registry.npmjs.org/@types/http-cache-semantics/-/http-cache-semantics-4.0.2.tgz",
+ "integrity": "sha512-FD+nQWA2zJjh4L9+pFXqWOi0Hs1ryBCfI+985NjluQ1p8EYtoLvjLOKidXBtZ4/IcxDX4o8/E8qDS3540tNliw=="
+ },
"@types/json5": {
"version": "0.0.29",
"resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz",
@@ -5965,6 +6371,25 @@
"update-browserslist-db": "^1.0.9"
}
},
+ "cacheable-lookup": {
+ "version": "7.0.0",
+ "resolved": "https://registry.npmjs.org/cacheable-lookup/-/cacheable-lookup-7.0.0.tgz",
+ "integrity": "sha512-+qJyx4xiKra8mZrcwhjMRMUhD5NR1R8esPkzIYxX96JiecFoxAXFuz/GpR3+ev4PE1WamHip78wV0vcmPQtp8w=="
+ },
+ "cacheable-request": {
+ "version": "10.2.14",
+ "resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-10.2.14.tgz",
+ "integrity": "sha512-zkDT5WAF4hSSoUgyfg5tFIxz8XQK+25W/TLVojJTMKBaxevLBBtLxgqguAuVQB8PVW79FVjHcU+GJ9tVbDZ9mQ==",
+ "requires": {
+ "@types/http-cache-semantics": "^4.0.2",
+ "get-stream": "^6.0.1",
+ "http-cache-semantics": "^4.1.1",
+ "keyv": "^4.5.3",
+ "mimic-response": "^4.0.0",
+ "normalize-url": "^8.0.0",
+ "responselike": "^3.0.0"
+ }
+ },
"caching-transform": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/caching-transform/-/caching-transform-4.0.0.tgz",
@@ -6102,6 +6527,15 @@
"integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==",
"dev": true
},
+ "config-chain": {
+ "version": "1.1.13",
+ "resolved": "https://registry.npmjs.org/config-chain/-/config-chain-1.1.13.tgz",
+ "integrity": "sha512-qj+f8APARXHrM0hraqXYb2/bOVSV4PvJQlNZ/DVj0QrmNM2q2euizkeuVckQ57J+W0mRH6Hvi+k50M4Jul2VRQ==",
+ "requires": {
+ "ini": "^1.3.4",
+ "proto-list": "~1.2.1"
+ }
+ },
"confusing-browser-globals": {
"version": "1.0.11",
"resolved": "https://registry.npmjs.org/confusing-browser-globals/-/confusing-browser-globals-1.0.11.tgz",
@@ -6200,6 +6634,26 @@
"integrity": "sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==",
"dev": true
},
+ "decompress-response": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz",
+ "integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==",
+ "requires": {
+ "mimic-response": "^3.1.0"
+ },
+ "dependencies": {
+ "mimic-response": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz",
+ "integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ=="
+ }
+ }
+ },
+ "deep-extend": {
+ "version": "0.6.0",
+ "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz",
+ "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA=="
+ },
"deep-is": {
"version": "0.1.4",
"resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz",
@@ -6215,6 +6669,11 @@
"strip-bom": "^4.0.0"
}
},
+ "defer-to-connect": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/defer-to-connect/-/defer-to-connect-2.0.1.tgz",
+ "integrity": "sha512-4tvttepXG1VaYGrRibk5EwJd1t4udunSOVMdLSAL6mId1ix438oPwPZMALY41FCijukO1L0twNcGsdzS7dHgDg=="
+ },
"define-properties": {
"version": "1.1.4",
"resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.4.tgz",
@@ -6764,6 +7223,11 @@
"mime-types": "^2.1.12"
}
},
+ "form-data-encoder": {
+ "version": "2.1.4",
+ "resolved": "https://registry.npmjs.org/form-data-encoder/-/form-data-encoder-2.1.4.tgz",
+ "integrity": "sha512-yDYSgNMraqvnxiEXO4hi88+YZxaHC6QKzb5N84iRCTDeRO7ZALpir/lVmf/uXUhnwUr2O4HU8s/n6x+yNjQkHw=="
+ },
"fromentries": {
"version": "1.3.2",
"resolved": "https://registry.npmjs.org/fromentries/-/fromentries-1.3.2.tgz",
@@ -6835,6 +7299,11 @@
"integrity": "sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==",
"dev": true
},
+ "get-stream": {
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz",
+ "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg=="
+ },
"get-symbol-description": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.0.0.tgz",
@@ -6920,11 +7389,28 @@
"get-intrinsic": "^1.1.3"
}
},
+ "got": {
+ "version": "12.6.1",
+ "resolved": "https://registry.npmjs.org/got/-/got-12.6.1.tgz",
+ "integrity": "sha512-mThBblvlAF1d4O5oqyvN+ZxLAYwIJK7bpMxgYqPD9okW0C3qm5FFn7k811QrcuEBwaogR3ngOFoCfs6mRv7teQ==",
+ "requires": {
+ "@sindresorhus/is": "^5.2.0",
+ "@szmarczak/http-timer": "^5.0.1",
+ "cacheable-lookup": "^7.0.0",
+ "cacheable-request": "^10.2.8",
+ "decompress-response": "^6.0.0",
+ "form-data-encoder": "^2.1.2",
+ "get-stream": "^6.0.1",
+ "http2-wrapper": "^2.1.10",
+ "lowercase-keys": "^3.0.0",
+ "p-cancelable": "^3.0.0",
+ "responselike": "^3.0.0"
+ }
+ },
"graceful-fs": {
"version": "4.2.10",
"resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.10.tgz",
- "integrity": "sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==",
- "dev": true
+ "integrity": "sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA=="
},
"grapheme-splitter": {
"version": "1.0.4",
@@ -7028,6 +7514,11 @@
"integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==",
"dev": true
},
+ "http-cache-semantics": {
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.1.tgz",
+ "integrity": "sha512-er295DKPVsV82j5kw1Gjt+ADA/XYHsajl82cGNQG2eyoPkvgUhX+nDIyelzhIWbbsXP39EHcI6l5tYs2FYqYXQ=="
+ },
"http-signature": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz",
@@ -7039,6 +7530,15 @@
"sshpk": "^1.7.0"
}
},
+ "http2-wrapper": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/http2-wrapper/-/http2-wrapper-2.2.0.tgz",
+ "integrity": "sha512-kZB0wxMo0sh1PehyjJUWRFEd99KC5TLjZ2cULC4f9iqJBAmKQQXEICjxl5iPJRwP40dpeHFqqhm7tYCvODpqpQ==",
+ "requires": {
+ "quick-lru": "^5.1.1",
+ "resolve-alpn": "^1.2.0"
+ }
+ },
"husky": {
"version": "8.0.3",
"resolved": "https://registry.npmjs.org/husky/-/husky-8.0.3.tgz",
@@ -7085,6 +7585,11 @@
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
"integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="
},
+ "ini": {
+ "version": "1.3.8",
+ "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz",
+ "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew=="
+ },
"internal-slot": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.4.tgz",
@@ -7459,6 +7964,11 @@
"integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==",
"dev": true
},
+ "json-buffer": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz",
+ "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ=="
+ },
"json-parse-even-better-errors": {
"version": "2.3.1",
"resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz",
@@ -7505,6 +8015,22 @@
"verror": "1.10.0"
}
},
+ "keyv": {
+ "version": "4.5.4",
+ "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz",
+ "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==",
+ "requires": {
+ "json-buffer": "3.0.1"
+ }
+ },
+ "latest-version": {
+ "version": "7.0.0",
+ "resolved": "https://registry.npmjs.org/latest-version/-/latest-version-7.0.0.tgz",
+ "integrity": "sha512-KvNT4XqAMzdcL6ka6Tl3i2lYeFDgXNCuIX+xNx6ZMVR1dFq+idXd9FLKNMOIx0t9mJ9/HudyX4oZWXZQ0UJHeg==",
+ "requires": {
+ "package-json": "^8.1.0"
+ }
+ },
"lcov-parse": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/lcov-parse/-/lcov-parse-1.0.0.tgz",
@@ -7573,6 +8099,11 @@
"is-unicode-supported": "^0.1.0"
}
},
+ "lowercase-keys": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-3.0.0.tgz",
+ "integrity": "sha512-ozCC6gdQ+glXOQsveKD0YsDy8DSQFjDTz4zyzEHNV5+JP5D62LmfDZ6o1cycFx9ouG940M5dE8C8CTewdj2YWQ=="
+ },
"lru-cache": {
"version": "5.1.1",
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz",
@@ -7623,6 +8154,11 @@
"mime-db": "1.52.0"
}
},
+ "mimic-response": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-4.0.0.tgz",
+ "integrity": "sha512-e5ISH9xMYU0DzrT+jl8q2ze9D6eWBto+I8CNpe+VI+K2J/F/k3PdkdTdz4wvGVH4NTpo+NRYTVIuMQEMMcsLqg=="
+ },
"minimatch": {
"version": "3.1.2",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
@@ -7635,8 +8171,7 @@
"minimist": {
"version": "1.2.7",
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.7.tgz",
- "integrity": "sha512-bzfL1YUZsP41gmu/qjrEk0Q6i2ix/cVeAhbCbqH9u3zYutS1cLg00qhrD0M2MVdCcx4Sc0UpP2eBWo9rotpq6g==",
- "dev": true
+ "integrity": "sha512-bzfL1YUZsP41gmu/qjrEk0Q6i2ix/cVeAhbCbqH9u3zYutS1cLg00qhrD0M2MVdCcx4Sc0UpP2eBWo9rotpq6g=="
},
"mocha": {
"version": "10.2.0",
@@ -7774,6 +8309,11 @@
"integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==",
"dev": true
},
+ "normalize-url": {
+ "version": "8.0.0",
+ "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-8.0.0.tgz",
+ "integrity": "sha512-uVFpKhj5MheNBJRTiMZ9pE/7hD1QTeEvugSJW/OmLzAp78PB5O6adfMNTvmfKhXBkvCzC+rqifWcVYpGFwTjnw=="
+ },
"nyc": {
"version": "15.1.0",
"resolved": "https://registry.npmjs.org/nyc/-/nyc-15.1.0.tgz",
@@ -8009,6 +8549,11 @@
"word-wrap": "^1.2.3"
}
},
+ "p-cancelable": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-3.0.0.tgz",
+ "integrity": "sha512-mlVgR3PGuzlo0MmTdk4cXqXWlwQDLnONTAg6sm62XkMJEiRxN3GL3SffkYvqwonbkJBcrI7Uvv5Zh9yjvn2iUw=="
+ },
"p-limit": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz",
@@ -8054,6 +8599,17 @@
"release-zalgo": "^1.0.0"
}
},
+ "package-json": {
+ "version": "8.1.1",
+ "resolved": "https://registry.npmjs.org/package-json/-/package-json-8.1.1.tgz",
+ "integrity": "sha512-cbH9IAIJHNj9uXi196JVsRlt7cHKak6u/e6AkL/bkRelZ7rlL3X1YKxsZwa36xipOEKAsdtmaG6aAJoM1fx2zA==",
+ "requires": {
+ "got": "^12.1.0",
+ "registry-auth-token": "^5.0.1",
+ "registry-url": "^6.0.0",
+ "semver": "^7.3.7"
+ }
+ },
"parent-module": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz",
@@ -8203,6 +8759,11 @@
"fromentries": "^1.2.0"
}
},
+ "proto-list": {
+ "version": "1.2.4",
+ "resolved": "https://registry.npmjs.org/proto-list/-/proto-list-1.2.4.tgz",
+ "integrity": "sha512-vtK/94akxsTMhe0/cbfpR+syPuszcuwhqVjJq26CuNDgFGj682oRBXOP5MJpv2r7JtE8MsiepGIqvvOTBwn2vA=="
+ },
"psl": {
"version": "1.9.0",
"resolved": "https://registry.npmjs.org/psl/-/psl-1.9.0.tgz",
@@ -8226,6 +8787,11 @@
"integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==",
"dev": true
},
+ "quick-lru": {
+ "version": "5.1.1",
+ "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-5.1.1.tgz",
+ "integrity": "sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA=="
+ },
"randombytes": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz",
@@ -8235,6 +8801,24 @@
"safe-buffer": "^5.1.0"
}
},
+ "rc": {
+ "version": "1.2.8",
+ "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz",
+ "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==",
+ "requires": {
+ "deep-extend": "^0.6.0",
+ "ini": "~1.3.0",
+ "minimist": "^1.2.0",
+ "strip-json-comments": "~2.0.1"
+ },
+ "dependencies": {
+ "strip-json-comments": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz",
+ "integrity": "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ=="
+ }
+ }
+ },
"readdirp": {
"version": "3.6.0",
"resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz",
@@ -8270,6 +8854,22 @@
"integrity": "sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg==",
"dev": true
},
+ "registry-auth-token": {
+ "version": "5.0.2",
+ "resolved": "https://registry.npmjs.org/registry-auth-token/-/registry-auth-token-5.0.2.tgz",
+ "integrity": "sha512-o/3ikDxtXaA59BmZuZrJZDJv8NMDGSj+6j6XaeBmHw8eY1i1qd9+6H+LjVvQXx3HN6aRCGa1cUdJ9RaJZUugnQ==",
+ "requires": {
+ "@pnpm/npm-conf": "^2.1.0"
+ }
+ },
+ "registry-url": {
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/registry-url/-/registry-url-6.0.1.tgz",
+ "integrity": "sha512-+crtS5QjFRqFCoQmvGduwYWEBng99ZvmFvF+cUJkGYF1L1BfU8C6Zp9T7f5vPAwyLkUExpvK+ANVZmGU49qi4Q==",
+ "requires": {
+ "rc": "1.2.8"
+ }
+ },
"release-zalgo": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/release-zalgo/-/release-zalgo-1.0.0.tgz",
@@ -8349,11 +8949,24 @@
"supports-preserve-symlinks-flag": "^1.0.0"
}
},
+ "resolve-alpn": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/resolve-alpn/-/resolve-alpn-1.2.1.tgz",
+ "integrity": "sha512-0a1F4l73/ZFZOakJnQ3FvkJ2+gSTQWz/r2KE5OdDY0TxPm5h4GkqkWWfM47T7HsbnOtcJVEF4epCVy6u7Q3K+g=="
+ },
"resolve-from": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz",
"integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g=="
},
+ "responselike": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/responselike/-/responselike-3.0.0.tgz",
+ "integrity": "sha512-40yHxbNcl2+rzXvZuVkrYohathsSJlMTXKryG5y8uciHv1+xDLHQpgjG64JUO9nrEq2jGLH6IZ8BcZyw3wrweg==",
+ "requires": {
+ "lowercase-keys": "^3.0.0"
+ }
+ },
"reusify": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz",
diff --git a/package.json b/package.json
index a0d0b55e..79a21f8a 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "solhint",
- "version": "3.6.2",
+ "version": "4.0.0",
"description": "Solidity Code Linter",
"main": "lib/index.js",
"keywords": [
@@ -35,6 +35,7 @@
"/solhint.js"
],
"author": "Ilya Drabenia ",
+ "contributors": ["Diego Bale "],
"license": "MIT",
"dependencies": {
"@solidity-parser/parser": "^0.16.0",
@@ -48,6 +49,7 @@
"glob": "^8.0.3",
"ignore": "^5.2.4",
"js-yaml": "^4.1.0",
+ "latest-version": "^7.0.0",
"lodash": "^4.17.21",
"pluralize": "^8.0.0",
"semver": "^7.5.2",
diff --git a/scripts/generate-rule-docs.js b/scripts/generate-rule-docs.js
index c170bb74..8b2c9953 100755
--- a/scripts/generate-rule-docs.js
+++ b/scripts/generate-rule-docs.js
@@ -191,23 +191,26 @@ function linkToVersion(version) {
}
function linkToSource(rule) {
- const link = rule.file.replace(path.resolve(path.join(__dirname, '..')), '')
+ const link = localPathToUri(rule.file)
return `https://github.com/protofire/solhint/tree/master${link}`
}
function linkToDocumentSource(rule) {
- const link = rule.file
- .replace(path.resolve(path.join(__dirname, '..')), '')
+ const link = localPathToUri(rule.file)
.replace('lib/rules', 'docs/rules')
.replace(/\.js$/, '.md')
return `https://github.com/protofire/solhint/tree/master${link}`
}
function linkToTestCase(rule) {
- const link = rule.file.replace(path.resolve(path.join(__dirname, '..', 'lib', 'rules')), '')
+ const link = localPathToUri(rule.file).replace('lib/rules/', '')
return `https://github.com/protofire/solhint/tree/master/test/rules${link}`
}
+function localPathToUri(file) {
+ return file.replace(path.resolve(path.join(__dirname, '..')), '').split(path.sep).join('/')
+}
+
function loadExamples(rule) {
if (!rule.meta.docs.examples) {
return 'This rule does not have examples.'
diff --git a/solhint.js b/solhint.js
index f1d64006..520a6e5c 100644
--- a/solhint.js
+++ b/solhint.js
@@ -1,9 +1,9 @@
#!/usr/bin/env node
-
const program = require('commander')
const _ = require('lodash')
const fs = require('fs')
const process = require('process')
+const readline = require('readline')
const linter = require('./lib/index')
const { loadConfig } = require('./lib/config/config-file')
@@ -27,8 +27,12 @@ function init() {
.option('-c, --config [file_name]', 'file to use as your .solhint.json')
.option('-q, --quiet', 'report errors only - default: false')
.option('--ignore-path [file_name]', 'file to use as your .solhintignore')
- .option('--fix', 'automatically fix problems')
+ .option('--fix', 'automatically fix problems. Skips fixes in report')
+ // .option('--fixShow', 'automatically fix problems. Show fixes in report')
+ .option('--noPrompt', 'do not suggest to backup files when any `fix` option is selected')
.option('--init', 'create configuration file for solhint')
+ .option('--disc', 'do not check for solhint updates')
+ .option('--save', 'save report to file on current folder')
.description('Linter for Solidity programming language')
.action(execMainAction)
@@ -53,10 +57,60 @@ function init() {
if (process.argv.length <= 2) {
program.help()
}
+
program.parse(process.argv)
}
+function askUserToContinue(callback) {
+ const rl = readline.createInterface({
+ input: process.stdin,
+ output: process.stdout,
+ })
+
+ rl.question(
+ '\nFIX option detected. Solhint will modify your files whenever it finds a fix for a rule error. Please BACKUP your contracts first. \nContinue ? (y/n) ',
+ (answer) => {
+ // Close the readline interface.
+ rl.close()
+
+ // Normalize and pass the user's answer to the callback function.
+ const normalizedAnswer = answer.trim().toLowerCase()
+ callback(normalizedAnswer)
+ }
+ )
+}
+
function execMainAction() {
+ // if ((program.opts().fix || program.opts().fixShow) && !program.opts().noPrompt) {
+ if (program.opts().fix && !program.opts().noPrompt) {
+ askUserToContinue((userAnswer) => {
+ if (userAnswer !== 'y') {
+ console.log('\nProcess terminated by user')
+ process.exit(0)
+ } else {
+ // User agreed, continue with the operation.
+ continueExecution()
+ }
+ })
+ } else {
+ // No need for user input, continue with the operation.
+ continueExecution()
+ }
+
+ function continueExecution() {
+ if (program.opts().disc) {
+ executeMainActionLogic()
+ } else {
+ // Call checkForUpdate and wait for it to complete using .then()
+ checkForUpdate().then(() => {
+ // This block runs after checkForUpdate is complete
+ executeMainActionLogic()
+ })
+ }
+ }
+}
+
+function executeMainActionLogic() {
if (program.opts().init) {
writeSampleConfigFile()
}
@@ -73,6 +127,7 @@ function execMainAction() {
const reportLists = program.args.filter(_.isString).map(processPath)
const reports = _.flatten(reportLists)
+ // if (program.opts().fix || program.opts().fixShow) {
if (program.opts().fix) {
for (const report of reports) {
const inputSrc = fs.readFileSync(report.filePath).toString()
@@ -85,8 +140,26 @@ function execMainAction() {
const { fixed, output } = applyFixes(fixes, inputSrc)
if (fixed) {
- report.reports = report.reports.filter((x) => !x.fix)
- fs.writeFileSync(report.filePath, output)
+ // // skip or not the report when fixed
+ // // This was filtering fixed rules so status code was not 1
+ // if (program.opts().fix) {
+ // report.reports = report.reports.filter((x) => !x.fix)
+ // } else {
+ // console.log('report.reports :>> ', report.reports)
+ report.reports.forEach((report) => {
+ if (report.fix !== null) {
+ report.message = `[FIXED] - ${report.message}`
+ }
+ })
+ // }
+
+ // fs.writeFileSync(report.filePath, output)
+ try {
+ fs.writeFileSync(report.filePath, output)
+ // fs.writeFileSync('no-console/Foo1Modified.sol', output)
+ } catch (error) {
+ console.error('An error occurred while writing the file:', error)
+ }
}
}
}
@@ -216,12 +289,40 @@ function printReports(reports, formatter) {
}
}
- console.log(formatter(reports), finalMessage || '')
+ const fullReport = formatter(reports) + (finalMessage || '')
+ console.log(fullReport)
+
+ if (program.opts().save) {
+ writeStringToFile(fullReport)
+ }
if (exitWithOne) process.exit(1)
return reports
}
+function writeStringToFile(data) {
+ const now = new Date()
+ const year = now.getFullYear()
+ const month = String(now.getMonth() + 1).padStart(2, '0') // Months are zero-based
+ const day = String(now.getDate()).padStart(2, '0')
+ const hour = String(now.getHours()).padStart(2, '0')
+ const minute = String(now.getMinutes()).padStart(2, '0')
+ const second = String(now.getSeconds()).padStart(2, '0')
+
+ const fileName = `${year}${month}${day}${hour}${minute}${second}_solhintReport.txt`
+
+ // Remove ANSI escape codes from the data
+ // eslint-disable-next-line no-control-regex
+ const cleanedData = data.replace(/\x1B\[[0-?]*[ -/]*[@-~]/g, '')
+
+ try {
+ fs.writeFileSync(fileName, cleanedData, 'utf-8') // Specify the encoding (UTF-16)
+ // console.log('File written successfully:', fileName)
+ } catch (err) {
+ console.error('Error writing to file:', err)
+ }
+}
+
function getFormatter(formatter) {
const formatterName = formatter || 'stylish'
try {
@@ -235,12 +336,20 @@ function getFormatter(formatter) {
}
function listRules() {
- if (process.argv.length !== 3) {
+ const args = process.argv.slice(2)
+ let configPath = '.solhint.json'
+ const configFileIndex = args.findIndex((arg) => arg === '-c' || arg === '--config')
+ if (configFileIndex !== -1) {
+ configPath = args[configFileIndex + 1]
+ if (!configPath || configPath.startsWith('-')) {
+ console.error('Error: Invalid configuration file path after -c or --config flag.')
+ process.exit(1)
+ }
+ } else if (args.length !== 1) {
console.log('Error!! no additional parameters after list-rules command')
process.exit(1)
}
- const configPath = '.solhint.json'
if (!fs.existsSync(configPath)) {
console.log('Error!! Configuration does not exists')
process.exit(1)
@@ -271,4 +380,27 @@ function exitWithCode(reports) {
process.exit(errorsCount > 0 ? 1 : 0)
}
+function checkForUpdate() {
+ // eslint-disable-next-line import/no-extraneous-dependencies
+ return import('latest-version')
+ .then((latestVersionModule) => {
+ const latestVersion = latestVersionModule.default
+ const currentVersion = require('./package.json').version
+
+ return latestVersion('solhint')
+ .then((latest) => {
+ if (currentVersion < latest) {
+ console.log('A new version of Solhint is available:', latest)
+ console.log('Please consider updating your Solhint package.')
+ }
+ })
+ .catch((error) => {
+ console.error('Error checking for updates:', error.message)
+ })
+ })
+ .catch((error) => {
+ console.error('Error importing latest-version:', error.message)
+ })
+}
+
init()
diff --git a/test/fixtures/best-practises/explicit-types.js b/test/fixtures/best-practises/explicit-types.js
index b63c4838..925b8791 100644
--- a/test/fixtures/best-practises/explicit-types.js
+++ b/test/fixtures/best-practises/explicit-types.js
@@ -119,6 +119,12 @@ const VAR_DECLARATIONS = {
errorsExplicit: 1,
},
+ fixedArrayCastInDeclaration: {
+ code: 'uint[] public arr = [uint(1),2,3];',
+ errorsImplicit: 0,
+ errorsExplicit: 2,
+ },
+
fixedArrayOfArrays: {
code: 'uint256[] public arr = [[1,2,3]];',
errorsImplicit: 1,
@@ -130,6 +136,42 @@ const VAR_DECLARATIONS = {
errorsImplicit: 1,
errorsExplicit: 1,
},
+
+ castInStateVariableDeclaration: {
+ code: 'uint256 public varUint256 = uint256(1); uint public varUint = uint(1);',
+ errorsImplicit: 2,
+ errorsExplicit: 2,
+ },
+
+ castInsideFunctionAtDeclarationUint256: {
+ code: 'function withUint256() external { uint256 varUint256 = uint256(1); }',
+ errorsImplicit: 2,
+ errorsExplicit: 0,
+ },
+
+ castInsideFunctionAtDeclarationUint: {
+ code: 'function withUint() external { uint varUint = uint(1); }',
+ errorsImplicit: 0,
+ errorsExplicit: 2,
+ },
+
+ castInsideFunctionUint: {
+ code: 'function withUint() external { uint varUint; varUint = uint(1);}',
+ errorsImplicit: 0,
+ errorsExplicit: 2,
+ },
+
+ castInsideFunctionUint256: {
+ code: 'function withUint256() external { uint256 varUint; varUint = uint256(1);}',
+ errorsImplicit: 2,
+ errorsExplicit: 0,
+ },
+
+ castInsideModifier: {
+ code: 'modifier withUint256() { _; uint256 varUint; varUint = uint256(1);}',
+ errorsImplicit: 2,
+ errorsExplicit: 0,
+ },
}
module.exports = VAR_DECLARATIONS
diff --git a/test/fixtures/best-practises/one-contract-per-file.js b/test/fixtures/best-practises/one-contract-per-file.js
index afc74921..69268371 100644
--- a/test/fixtures/best-practises/one-contract-per-file.js
+++ b/test/fixtures/best-practises/one-contract-per-file.js
@@ -33,4 +33,40 @@ const THREE_CONTRACTS = `
uint256 public constant TESTC = "testC";
}
`
-module.exports = { ONE_CONTRACT, TWO_CONTRACTS, THREE_CONTRACTS }
+
+const TWO_LIBRARIES = `
+ pragma solidity 0.8.0;
+
+ library A { }
+
+ library B { }
+ `
+
+const ONE_CONTRACT_WITH_INTERFACES = `
+ pragma solidity 0.8.0;
+
+ contract A { }
+
+ interface B { }
+
+ interface C { }
+ `
+
+const ONE_LIBRARY_WITH_INTERFACES = `
+ pragma solidity 0.8.0;
+
+ library A { }
+
+ interface B { }
+
+ interface C { }
+ `
+
+module.exports = {
+ ONE_CONTRACT,
+ TWO_CONTRACTS,
+ THREE_CONTRACTS,
+ TWO_LIBRARIES,
+ ONE_CONTRACT_WITH_INTERFACES,
+ ONE_LIBRARY_WITH_INTERFACES,
+}
diff --git a/test/rules/best-practises/no-console.js b/test/rules/best-practises/no-console.js
index 6b67b2eb..2ac75dca 100644
--- a/test/rules/best-practises/no-console.js
+++ b/test/rules/best-practises/no-console.js
@@ -35,7 +35,7 @@ describe('Linter - no-console', () => {
it('should raise console.logBytes12() is not allowed', () => {
const code = funcWith(`
- console.logString('test');
+ console.logBytes12('test');
`)
const report = linter.processStr(code, {
diff --git a/test/rules/best-practises/one-contract-per-file.js b/test/rules/best-practises/one-contract-per-file.js
index 54b5876c..86dc782f 100644
--- a/test/rules/best-practises/one-contract-per-file.js
+++ b/test/rules/best-practises/one-contract-per-file.js
@@ -13,6 +13,26 @@ describe('Linter - one-contract-per-file', () => {
assertNoWarnings(report)
})
+ it('should not raise error for ONE contract and multiple interfaces in the same file', () => {
+ const code = contracts.ONE_CONTRACT_WITH_INTERFACES
+
+ const report = linter.processStr(code, {
+ rules: { 'one-contract-per-file': 'error' },
+ })
+
+ assertNoWarnings(report)
+ })
+
+ it('should not raise error for ONE library and multiple interfaces in the same file', () => {
+ const code = contracts.ONE_LIBRARY_WITH_INTERFACES
+
+ const report = linter.processStr(code, {
+ rules: { 'one-contract-per-file': 'error' },
+ })
+
+ assertNoWarnings(report)
+ })
+
it('should raise error for TWO contracts in same file', () => {
const code = contracts.TWO_CONTRACTS
@@ -34,4 +54,15 @@ describe('Linter - one-contract-per-file', () => {
assertErrorCount(report, 1)
assertErrorMessage(report, 'Found more than One contract per file. 3 contracts found!')
})
+
+ it('should raise error for TWO libraries in same file', () => {
+ const code = contracts.TWO_LIBRARIES
+
+ const report = linter.processStr(code, {
+ rules: { 'one-contract-per-file': 'error' },
+ })
+
+ assertErrorCount(report, 1)
+ assertErrorMessage(report, 'Found more than One contract per file. 2 contracts found!')
+ })
})
diff --git a/test/rules/naming/private-vars-leading-underscore.js b/test/rules/naming/private-vars-leading-underscore.js
index 5a8d0e51..dbf02a20 100644
--- a/test/rules/naming/private-vars-leading-underscore.js
+++ b/test/rules/naming/private-vars-leading-underscore.js
@@ -4,60 +4,66 @@ const { contractWith, libraryWith } = require('../../common/contract-builder')
describe('Linter - private-vars-leading-underscore', () => {
const SHOULD_WARN_CASES = [
- // warn when private/internal names don't start with _
+ // warn when private/internal/default names don't start with _
contractWith('uint foo;'),
contractWith('uint private foo;'),
contractWith('uint internal foo;'),
+ contractWith('function foo() {}'),
contractWith('function foo() private {}'),
contractWith('function foo() internal {}'),
+ libraryWith('function foo() {}'),
libraryWith('function foo() private {}'),
+ libraryWith('function foo() internal {}'),
- // warn when public/external/default names start with _
+ // warn when public/external names start with _
contractWith('uint public _foo;'),
- contractWith('function _foo() {}'),
+ contractWith('uint external _foo;'),
contractWith('function _foo() public {}'),
contractWith('function _foo() external {}'),
- libraryWith('function _foo() {}'),
libraryWith('function _foo() public {}'),
- libraryWith('function _foo() internal {}'),
+ libraryWith('function _foo() external {}'),
]
+
const SHOULD_WARN_STRICT_CASES = [
- contractWith('function foo() { uint _bar; }'),
- contractWith('function foo(uint _bar) {}'),
- contractWith('function foo() returns (uint256 _bar) {}'),
- libraryWith('function foo() returns (uint256 _bar) {}'),
- libraryWith('function foo(uint _bar) {}'),
+ contractWith('function _foo() internal { uint _bar; }'),
+ contractWith('function foo(uint _bar) external {}'),
+ contractWith('function foo() public returns (uint256 _bar) {}'),
+ libraryWith('function _foo() returns (uint256 _bar) {}'),
+ libraryWith('function _foo(uint _bar) private {}'),
]
+
const SHOULD_NOT_WARN_CASES = [
- // don't warn when private/internal names start with _
+ // don't warn when private/internal/default names start with _
contractWith('uint _foo;'),
contractWith('uint private _foo;'),
contractWith('uint internal _foo;'),
+ contractWith('function _foo() {}'),
contractWith('function _foo() private {}'),
contractWith('function _foo() internal {}'),
+ libraryWith('function _foo() {}'),
+ libraryWith('function _foo() internal {}'),
libraryWith('function _foo() private {}'),
- // don't warn when public/external/default names don't start with _
+ // don't warn when public/external names don't start with _
contractWith('uint public foo;'),
- contractWith('function foo() {}'),
+ contractWith('uint public foo = 2;'),
contractWith('function foo() public {}'),
contractWith('function foo() external {}'),
- libraryWith('function foo() {}'),
libraryWith('function foo() public {}'),
- libraryWith('function foo() internal {}'),
+ libraryWith('function foo() external {}'),
// don't warn for constructors
contractWith('constructor() public {}'),
// other names (variables, parameters, returns) shouldn't be affected by this rule
- contractWith('function foo(uint bar) {}'),
- contractWith('function foo() { uint bar; }'),
- contractWith('function foo() returns (uint256) {}'),
- contractWith('function foo() returns (uint256 bar) {}'),
- libraryWith('function foo(uint bar) {}'),
- libraryWith('function foo() { uint bar; }'),
- libraryWith('function foo() returns (uint256) {}'),
- libraryWith('function foo() returns (uint256 bar) {}'),
+ contractWith('function foo(uint bar) external {}'),
+ contractWith('function foo() public { uint bar; }'),
+ contractWith('function _foo() returns (uint256) {}'),
+ contractWith('function _foo() returns (uint256 bar) {}'),
+ libraryWith('function foo(uint bar) external {}'),
+ libraryWith('function foo() public { uint bar; }'),
+ libraryWith('function _foo() returns (uint256) {}'),
+ libraryWith('function _foo() internal returns (uint256 bar) {}'),
]
SHOULD_WARN_CASES.forEach((code, index) => {
@@ -71,7 +77,7 @@ describe('Linter - private-vars-leading-underscore', () => {
})
SHOULD_WARN_STRICT_CASES.concat(SHOULD_NOT_WARN_CASES).forEach((code, index) => {
- it(`should not emit a warning (strict) (${index})`, () => {
+ it(`should not emit a warning (not strict) (${index})`, () => {
const report = linter.processStr(code, {
rules: { 'private-vars-leading-underscore': 'error' },
})
@@ -91,7 +97,7 @@ describe('Linter - private-vars-leading-underscore', () => {
})
SHOULD_WARN_STRICT_CASES.forEach((code, index) => {
- it(`should not emit a warning (strict) (${index})`, () => {
+ it(`should emit a warning (strict) (${index})`, () => {
const report = linter.processStr(code, {
rules: { 'private-vars-leading-underscore': ['error', { strict: true }] },
})