From 3e3e71f1d91bc69fc74149482f9b1656c87a458b Mon Sep 17 00:00:00 2001 From: Vladislav Volosnikov Date: Sat, 19 Aug 2023 15:12:31 +0300 Subject: [PATCH 01/20] Enhance explicit-types sensitivity --- lib/rules/best-practises/explicit-types.js | 21 ++++++++++ .../fixtures/best-practises/explicit-types.js | 42 +++++++++++++++++++ 2 files changed, 63 insertions(+) diff --git a/lib/rules/best-practises/explicit-types.js b/lib/rules/best-practises/explicit-types.js index ebf7b60d..c57c3b39 100644 --- a/lib/rules/best-practises/explicit-types.js +++ b/lib/rules/best-practises/explicit-types.js @@ -35,6 +35,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,6 +49,10 @@ const meta = { description: 'If implicit is selected', code: 'uint256 public variableName', }, + { + description: 'If implicit is selected', + code: 'uint public variableName = uint256(5)', + }, ], }, }, @@ -68,6 +76,19 @@ class ExplicitTypesChecker extends BaseChecker { } 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 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 From ff776df2d081075a62a5d2d7c3de2eac1cb00426 Mon Sep 17 00:00:00 2001 From: Vladislav Volosnikov Date: Sat, 19 Aug 2023 15:18:14 +0300 Subject: [PATCH 02/20] Update docs --- docs/rules/best-practises/explicit-types.md | 12 ++++++++++++ lib/rules/best-practises/explicit-types.js | 2 +- 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/docs/rules/best-practises/explicit-types.md b/docs/rules/best-practises/explicit-types.md index 8448bc6e..cd698b24 100644 --- a/docs/rules/best-practises/explicit-types.md +++ b/docs/rules/best-practises/explicit-types.md @@ -48,6 +48,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 +68,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/lib/rules/best-practises/explicit-types.js b/lib/rules/best-practises/explicit-types.js index c57c3b39..010db857 100644 --- a/lib/rules/best-practises/explicit-types.js +++ b/lib/rules/best-practises/explicit-types.js @@ -50,7 +50,7 @@ const meta = { code: 'uint256 public variableName', }, { - description: 'If implicit is selected', + description: 'At any setting', code: 'uint public variableName = uint256(5)', }, ], From b9aa1a88321044a24be66b729d465dde7d213db9 Mon Sep 17 00:00:00 2001 From: Vladislav Volosnikov Date: Sat, 19 Aug 2023 16:00:22 +0300 Subject: [PATCH 03/20] Fix generate docs links --- scripts/generate-rule-docs.js | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) 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.' From 00ae9ce90eff934c2cdbe11076d863689df5fb50 Mon Sep 17 00:00:00 2001 From: dbale-altoros Date: Thu, 14 Sep 2023 10:25:34 -0300 Subject: [PATCH 04/20] fix changelog and no empty block doc --- CHANGELOG.md | 2 +- docs/rules/best-practises/no-empty-blocks.md | 2 ++ docs/rules/best-practises/one-contract-per-file.md | 2 +- lib/rules/best-practises/no-empty-blocks.js | 5 +++++ 4 files changed, 9 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d93d717d..f9e35d69 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -52,7 +52,7 @@ If not explicitly added, this rule will not be executed. ## [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) 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/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, From 7ee36badb1a4be3ae52e545a4804f0e134f40342 Mon Sep 17 00:00:00 2001 From: dbale-altoros Date: Mon, 18 Sep 2023 12:45:07 -0300 Subject: [PATCH 05/20] updated docs on rule --- .../naming/private-vars-leading-underscore.md | 56 ++++++++++++++++++- .../naming/private-vars-leading-underscore.js | 51 +++++++++++++++++ package.json | 2 +- 3 files changed, 107 insertions(+), 2 deletions(-) diff --git a/docs/rules/naming/private-vars-leading-underscore.md b/docs/rules/naming/private-vars-leading-underscore.md index 9e0028c9..1bb34921 100644 --- a/docs/rules/naming/private-vars-leading-underscore.md +++ b/docs/rules/naming/private-vars-leading-underscore.md @@ -29,9 +29,63 @@ This rule accepts an array of options: } ``` +### Notes +- 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/lib/rules/naming/private-vars-leading-underscore.js b/lib/rules/naming/private-vars-leading-underscore.js index dc418728..6c02bd05 100644 --- a/lib/rules/naming/private-vars-leading-underscore.js +++ b/lib/rules/naming/private-vars-leading-underscore.js @@ -24,6 +24,57 @@ const meta = { 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 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, diff --git a/package.json b/package.json index a0d0b55e..d6d0bc32 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "solhint", - "version": "3.6.2", + "version": "3.6.3", "description": "Solidity Code Linter", "main": "lib/index.js", "keywords": [ From 8418c47f9f6d9e31bd2c9886a8fcc68ce49218a4 Mon Sep 17 00:00:00 2001 From: dbale-altoros Date: Fri, 6 Oct 2023 14:05:26 -0300 Subject: [PATCH 06/20] fix: list-all-rules --- solhint.js | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/solhint.js b/solhint.js index f1d64006..6fb12aac 100644 --- a/solhint.js +++ b/solhint.js @@ -235,12 +235,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) From 38245b350aac024115632a05f1f8adc3379f8b01 Mon Sep 17 00:00:00 2001 From: dbale-altoros Date: Mon, 9 Oct 2023 12:43:18 -0300 Subject: [PATCH 07/20] autofix explicit-types rule --- README.md | 3 +- docs/rules/best-practises/explicit-types.md | 2 ++ docs/rules/security/avoid-sha3.md | 2 ++ docs/rules/security/avoid-throw.md | 2 ++ lib/rules/best-practises/explicit-types.js | 36 +++++++++++++++++---- lib/rules/security/avoid-sha3.js | 5 +++ lib/rules/security/avoid-throw.js | 5 +++ solhint.js | 17 ++++++++-- 8 files changed, 61 insertions(+), 11 deletions(-) diff --git a/README.md b/README.md index 0f010f69..c1d347a8 100644 --- a/README.md +++ b/README.md @@ -60,7 +60,8 @@ 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. Skip report + --fixShow automatically fix problems. Show report --init create configuration file for solhint -h, --help output usage information diff --git a/docs/rules/best-practises/explicit-types.md b/docs/rules/best-practises/explicit-types.md index cd698b24..18964a4e 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` or `--fixShow` option ## Examples ### 👍 Examples of **correct** code for this rule diff --git a/docs/rules/security/avoid-sha3.md b/docs/rules/security/avoid-sha3.md index 1d6f9637..6b009388 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` or `--fixShow` 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..b5a3022d 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` or `--fixShow` option ## Examples This rule does not have examples. diff --git a/lib/rules/best-practises/explicit-types.js b/lib/rules/best-practises/explicit-types.js index 010db857..266a38dd 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 = { @@ -55,11 +56,17 @@ const meta = { }, ], }, + notes: [ + { + note: 'Solhint allows this rule to automatically fix the code with `--fix` or `--fixShow` option', + }, + ], }, isDefault: false, recommended: true, defaultSetup: [DEFAULT_SEVERITY, DEFAULT_OPTION], + fixable: true, schema: { type: 'string', @@ -73,6 +80,13 @@ 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) { @@ -106,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) { @@ -119,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/security/avoid-sha3.js b/lib/rules/security/avoid-sha3.js index 048679a8..aa0127c5 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` or `--fixShow` option', + }, + ], }, isDefault: false, diff --git a/lib/rules/security/avoid-throw.js b/lib/rules/security/avoid-throw.js index 30a942a0..279233ff 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` or `--fixShow` option', + }, + ], }, isDefault: false, diff --git a/solhint.js b/solhint.js index 6fb12aac..ad68394a 100644 --- a/solhint.js +++ b/solhint.js @@ -27,7 +27,8 @@ 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('--init', 'create configuration file for solhint') .description('Linter for Solidity programming language') .action(execMainAction) @@ -73,7 +74,7 @@ function execMainAction() { const reportLists = program.args.filter(_.isString).map(processPath) const reports = _.flatten(reportLists) - if (program.opts().fix) { + if (program.opts().fix || program.opts().fixShow) { for (const report of reports) { const inputSrc = fs.readFileSync(report.filePath).toString() @@ -85,7 +86,17 @@ function execMainAction() { const { fixed, output } = applyFixes(fixes, inputSrc) if (fixed) { - report.reports = report.reports.filter((x) => !x.fix) + // skip or not the report when fixed + 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) } } From a40e85a94e2aa4bd08d623efa526e2c2077d0bc3 Mon Sep 17 00:00:00 2001 From: dbale-altoros Date: Wed, 11 Oct 2023 16:12:49 -0300 Subject: [PATCH 08/20] add: automatic update checker --- README.md | 8 +- package-lock.json | 631 +++++++++++++++++++++++++++++++++++++++++++++- package.json | 4 +- solhint.js | 57 +++++ 4 files changed, 688 insertions(+), 12 deletions(-) diff --git a/README.md b/README.md index c1d347a8..d9212e2b 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**: @@ -63,6 +65,7 @@ Options: --fix automatically fix problems. Skip report --fixShow automatically fix problems. Show report --init create configuration file for solhint + do not check for solhint updates -h, --help output usage information Commands: @@ -70,8 +73,9 @@ 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. +- `--fix` option currently works only on "avoid-throw" and "avoid-sha3" rules.

## Configuration 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 d6d0bc32..79a21f8a 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "solhint", - "version": "3.6.3", + "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/solhint.js b/solhint.js index ad68394a..f55cb65e 100644 --- a/solhint.js +++ b/solhint.js @@ -30,6 +30,7 @@ function init() { .option('--fix', 'automatically fix problems. Skips fixes in report') .option('--fixShow', 'automatically fix problems. Show fixes in report') .option('--init', 'create configuration file for solhint') + .option('--disc', 'do not check for solhint updates') .description('Linter for Solidity programming language') .action(execMainAction) @@ -54,10 +55,23 @@ function init() { if (process.argv.length <= 2) { program.help() } + program.parse(process.argv) } function execMainAction() { + if (!program.opts().disc) { + // Call checkForUpdate and wait for it to complete using .then() + checkForUpdate().then(() => { + // This block runs after checkForUpdate is complete + executeMainActionLogic() + }) + } else { + executeMainActionLogic() + } +} + +function executeMainActionLogic() { if (program.opts().init) { writeSampleConfigFile() } @@ -290,4 +304,47 @@ function exitWithCode(reports) { process.exit(errorsCount > 0 ? 1 : 0) } +// async function checkForUpdate() { +// try { +// // Dynamic import of latest-version +// // eslint-disable-next-line import/no-extraneous-dependencies +// const latestVersionModule = await import('latest-version') +// const latestVersion = latestVersionModule.default + +// const currentVersion = require('./package.json').version + +// const latest = await latestVersion('solhint') + +// if (currentVersion !== latest) { +// console.log('\nA new version of Solhint is available:', latest) +// console.log('Please consider updating your Solhint package.') +// } +// } catch (error) { +// console.log('Error checking for updates: ', error.message) +// } +// } + +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() From 5298a75ac762c09577a3c40dc2280be188dc72eb Mon Sep 17 00:00:00 2001 From: dbale-altoros Date: Wed, 18 Oct 2023 12:02:56 -0300 Subject: [PATCH 09/20] added save option and changelog --- .gitignore | 1 + CHANGELOG.md | 30 ++++++++++++++++++++----- README.md | 4 +++- e2e/formatters-test.js | 4 ++-- solhint.js | 51 +++++++++++++++++++++++++----------------- 5 files changed, 61 insertions(+), 29 deletions(-) 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 f9e35d69..7c285c7e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,22 @@ +## [4.0] - 2023-10-01 + +### Updated +- Enhance explicit types sensitivity [493](https://github.com/protofire/solhint/pull/493) (Thanks to @vladyan18) +- Docs on `private-vars-leading-underscore` rule to clarify its functionality +- Changelog and docs for `no-empty-blocks` rule to clarify its functionality + +### Added +- `fixShow` option to show report. `fix` option skips showing report on screen +- `save` option to store report on disk with the standard or the specified format +- Check for updates on Solhint version to keep users with the last versin available. There's an option to disable this check (`--disc`) +- Autofix for `explicit-types` rule [504](https://github.com/protofire/solhint/pull/504) + +### Fixed +- Generate docs script on Windows OS [494](https://github.com/protofire/solhint/pull/494) (Thanks to @vladyan18) + + + + ## [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 +50,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,9 +68,6 @@ 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) @@ -64,7 +85,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 d9212e2b..338d1fd1 100644 --- a/README.md +++ b/README.md @@ -65,7 +65,8 @@ Options: --fix automatically fix problems. Skip report --fixShow automatically fix problems. Show report --init create configuration file for solhint - do not check for solhint updates + --disc do not check for solhint updates + --save save report to file on current folder -h, --help output usage information Commands: @@ -76,6 +77,7 @@ Commands: ### Notes - Solhint checks if there are newer versions. The `--disc` option avoids that check. - `--fix` option currently works only on "avoid-throw" and "avoid-sha3" rules. +- `--save` option will create a file named as `YYYYMMDDHHMMSS_solhintReport.txt` on current folder with default or specified format

## Configuration diff --git a/e2e/formatters-test.js b/e2e/formatters-test.js index 07f60a50..5d854cb2 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', () => { @@ -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/solhint.js b/solhint.js index f55cb65e..4b932b85 100644 --- a/solhint.js +++ b/solhint.js @@ -31,6 +31,7 @@ function init() { .option('--fixShow', 'automatically fix problems. Show fixes in report') .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) @@ -241,12 +242,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 { @@ -304,26 +333,6 @@ function exitWithCode(reports) { process.exit(errorsCount > 0 ? 1 : 0) } -// async function checkForUpdate() { -// try { -// // Dynamic import of latest-version -// // eslint-disable-next-line import/no-extraneous-dependencies -// const latestVersionModule = await import('latest-version') -// const latestVersion = latestVersionModule.default - -// const currentVersion = require('./package.json').version - -// const latest = await latestVersion('solhint') - -// if (currentVersion !== latest) { -// console.log('\nA new version of Solhint is available:', latest) -// console.log('Please consider updating your Solhint package.') -// } -// } catch (error) { -// console.log('Error checking for updates: ', error.message) -// } -// } - function checkForUpdate() { // eslint-disable-next-line import/no-extraneous-dependencies return import('latest-version') From 9e5263c2b15c4303b48e3cdffc61a465f0e4d53b Mon Sep 17 00:00:00 2001 From: dbale-altoros Date: Wed, 18 Oct 2023 16:55:14 -0300 Subject: [PATCH 10/20] add autofix --- lib/rules/best-practises/no-console.js | 17 +++++++++++++++-- test/rules/best-practises/no-console.js | 2 +- 2 files changed, 16 insertions(+), 3 deletions(-) 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/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, { From 1242e6d43fd80eb2d8ea890e9afd8808d9fd128a Mon Sep 17 00:00:00 2001 From: dbale-altoros Date: Thu, 19 Oct 2023 15:17:37 -0300 Subject: [PATCH 11/20] add private var autofix and backup suggestion when fixing --- README.md | 10 ++- docs/rules.md | 2 +- .../naming/private-vars-leading-underscore.md | 11 ++-- e2e/06-formatters/helpers/helpers.js | 1 - e2e/formatters-test.js | 2 +- .../naming/private-vars-leading-underscore.js | 62 +++++++++++++------ solhint.js | 49 +++++++++++++-- .../naming/private-vars-leading-underscore.js | 56 +++++++++-------- 8 files changed, 134 insertions(+), 59 deletions(-) diff --git a/README.md b/README.md index 338d1fd1..1963c4c4 100644 --- a/README.md +++ b/README.md @@ -64,6 +64,7 @@ Options: --ignore-path [file_name] file to use as your .solhintignore --fix automatically fix problems. Skip report --fixShow automatically fix problems. 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 @@ -76,8 +77,15 @@ Commands: ``` ### Notes - Solhint checks if there are newer versions. The `--disc` option avoids that check. -- `--fix` option currently works only on "avoid-throw" and "avoid-sha3" rules. - `--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/naming/private-vars-leading-underscore.md b/docs/rules/naming/private-vars-leading-underscore.md index 1bb34921..6bcbaee6 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 @@ -30,6 +30,7 @@ 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 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/formatters-test.js b/e2e/formatters-test.js index 5d854cb2..668c19d5 100644 --- a/e2e/formatters-test.js +++ b/e2e/formatters-test.js @@ -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}` ) diff --git a/lib/rules/naming/private-vars-leading-underscore.js b/lib/rules/naming/private-vars-leading-underscore.js index 6c02bd05..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,7 +21,7 @@ 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), }, ], @@ -65,6 +66,9 @@ const meta = { ], }, notes: [ + { + note: 'This rule considers functions and variables in Libraries as well', + }, { note: 'This rule skips external and public functions', }, @@ -80,6 +84,7 @@ const meta = { isDefault: false, recommended: false, defaultSetup: [DEFAULT_SEVERITY, DEFAULT_OPTION], + fixable: true, schema: { type: 'object', @@ -98,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) { @@ -114,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() { @@ -131,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 } @@ -139,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/solhint.js b/solhint.js index 4b932b85..5f8d5b4a 100644 --- a/solhint.js +++ b/solhint.js @@ -4,6 +4,7 @@ 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') @@ -29,6 +30,7 @@ function init() { .option('--ignore-path [file_name]', 'file to use as your .solhintignore') .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') @@ -60,15 +62,50 @@ function init() { 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().disc) { - // Call checkForUpdate and wait for it to complete using .then() - checkForUpdate().then(() => { - // This block runs after checkForUpdate is complete - executeMainActionLogic() + if ((program.opts().fix || program.opts().fixShow) && !program.opts().noPrompt) { + askUserToContinue((userAnswer) => { + if (userAnswer !== 'y') { + console.log('\nProcess terminated by user') + } else { + // User agreed, continue with the operation. + continueExecution() + } }) } else { - executeMainActionLogic() + // 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() + }) + } } } 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 }] }, }) From bd6c675125e370a31ba011d455f8b09732395de2 Mon Sep 17 00:00:00 2001 From: dbale-altoros Date: Mon, 23 Oct 2023 13:47:16 -0300 Subject: [PATCH 12/20] e2e tests added --- CHANGELOG.md | 2 +- README.md | 1 - docs/rules/best-practises/explicit-types.md | 2 +- .../naming/private-vars-leading-underscore.md | 2 +- docs/rules/security/avoid-sha3.md | 2 +- docs/rules/security/avoid-throw.md | 2 +- .../contracts/00-generic/.solhint.json | 5 + e2e/08-autofix/contracts/00-generic/Foo1.sol | 22 ++ .../contracts/00-generic/Foo1AfterFix.sol | 22 ++ .../contracts/00-generic/Foo1BeforeFix.sol | 22 ++ e2e/08-autofix/contracts/Foo1.sol | 83 ++++++ .../contracts/explicit-types/.solhint.json | 5 + .../contracts/explicit-types/Foo1.sol | 22 ++ .../contracts/explicit-types/Foo1AfterFix.sol | 22 ++ .../explicit-types/Foo1BeforeFix.sol | 22 ++ .../contracts/no-console/.solhint.json | 5 + e2e/08-autofix/contracts/no-console/Foo1.sol | 37 +++ .../contracts/no-console/Foo1AfterFix.sol | 37 +++ .../contracts/no-console/Foo1BeforeFix.sol | 37 +++ .../private-vars-underscore/.solhint.json | 5 + .../private-vars-underscore/Foo1.sol | 83 ++++++ .../private-vars-underscore/Foo1AfterFix.sol | 83 ++++++ .../private-vars-underscore/Foo1BeforeFix.sol | 83 ++++++ e2e/autofix-test.js | 262 ++++++++++++++++++ e2e/package.json | 3 +- lib/rules/best-practises/explicit-types.js | 2 +- lib/rules/security/avoid-sha3.js | 2 +- lib/rules/security/avoid-throw.js | 2 +- solhint.js | 32 ++- 29 files changed, 885 insertions(+), 24 deletions(-) create mode 100644 e2e/08-autofix/contracts/00-generic/.solhint.json create mode 100644 e2e/08-autofix/contracts/00-generic/Foo1.sol create mode 100644 e2e/08-autofix/contracts/00-generic/Foo1AfterFix.sol create mode 100644 e2e/08-autofix/contracts/00-generic/Foo1BeforeFix.sol create mode 100644 e2e/08-autofix/contracts/Foo1.sol create mode 100644 e2e/08-autofix/contracts/explicit-types/.solhint.json create mode 100644 e2e/08-autofix/contracts/explicit-types/Foo1.sol create mode 100644 e2e/08-autofix/contracts/explicit-types/Foo1AfterFix.sol create mode 100644 e2e/08-autofix/contracts/explicit-types/Foo1BeforeFix.sol create mode 100644 e2e/08-autofix/contracts/no-console/.solhint.json create mode 100644 e2e/08-autofix/contracts/no-console/Foo1.sol create mode 100644 e2e/08-autofix/contracts/no-console/Foo1AfterFix.sol create mode 100644 e2e/08-autofix/contracts/no-console/Foo1BeforeFix.sol create mode 100644 e2e/08-autofix/contracts/private-vars-underscore/.solhint.json create mode 100644 e2e/08-autofix/contracts/private-vars-underscore/Foo1.sol create mode 100644 e2e/08-autofix/contracts/private-vars-underscore/Foo1AfterFix.sol create mode 100644 e2e/08-autofix/contracts/private-vars-underscore/Foo1BeforeFix.sol create mode 100644 e2e/autofix-test.js diff --git a/CHANGELOG.md b/CHANGELOG.md index 7c285c7e..7ae0b65b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,7 +6,7 @@ - Changelog and docs for `no-empty-blocks` rule to clarify its functionality ### Added -- `fixShow` option to show report. `fix` option skips showing report on screen +- `fix` option now shows the report on screen - `save` option to store report on disk with the standard or the specified format - Check for updates on Solhint version to keep users with the last versin available. There's an option to disable this check (`--disc`) - Autofix for `explicit-types` rule [504](https://github.com/protofire/solhint/pull/504) diff --git a/README.md b/README.md index 1963c4c4..64b23622 100644 --- a/README.md +++ b/README.md @@ -63,7 +63,6 @@ Options: -q, --quiet report errors only - default: false --ignore-path [file_name] file to use as your .solhintignore --fix automatically fix problems. Skip report - --fixShow automatically fix problems. 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 diff --git a/docs/rules/best-practises/explicit-types.md b/docs/rules/best-practises/explicit-types.md index 18964a4e..2a472762 100644 --- a/docs/rules/best-practises/explicit-types.md +++ b/docs/rules/best-practises/explicit-types.md @@ -33,7 +33,7 @@ This rule accepts an array of options: ``` ### Notes -- Solhint allows this rule to automatically fix the code with `--fix` or `--fixShow` option +- Solhint allows this rule to automatically fix the code with `--fix` option ## Examples ### 👍 Examples of **correct** code for this rule diff --git a/docs/rules/naming/private-vars-leading-underscore.md b/docs/rules/naming/private-vars-leading-underscore.md index 6bcbaee6..bba9e897 100644 --- a/docs/rules/naming/private-vars-leading-underscore.md +++ b/docs/rules/naming/private-vars-leading-underscore.md @@ -9,7 +9,7 @@ title: "private-vars-leading-underscore | Solhint" ![Default Severity Badge warn](https://img.shields.io/badge/Default%20Severity-warn-yellow) ## Description -Non-external functions and state variables should start with a single underscore. Others, shouldn't. +Non-external functions and state variables should start with a single underscore. Others, shouldn't ## Options This rule accepts an array of options: diff --git a/docs/rules/security/avoid-sha3.md b/docs/rules/security/avoid-sha3.md index 6b009388..7947a087 100644 --- a/docs/rules/security/avoid-sha3.md +++ b/docs/rules/security/avoid-sha3.md @@ -27,7 +27,7 @@ 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` or `--fixShow` option +- 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 b5a3022d..5b12fcd7 100644 --- a/docs/rules/security/avoid-throw.md +++ b/docs/rules/security/avoid-throw.md @@ -27,7 +27,7 @@ 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` or `--fixShow` option +- Solhint allows this rule to automatically fix the code with `--fix` option ## Examples This rule does not have examples. diff --git a/e2e/08-autofix/contracts/00-generic/.solhint.json b/e2e/08-autofix/contracts/00-generic/.solhint.json new file mode 100644 index 00000000..7bd7c6a2 --- /dev/null +++ b/e2e/08-autofix/contracts/00-generic/.solhint.json @@ -0,0 +1,5 @@ +{ + "rules": { + "explicit-types": ["error", "explicit"] + } +} diff --git a/e2e/08-autofix/contracts/00-generic/Foo1.sol b/e2e/08-autofix/contracts/00-generic/Foo1.sol new file mode 100644 index 00000000..78bcaec7 --- /dev/null +++ b/e2e/08-autofix/contracts/00-generic/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/contracts/00-generic/Foo1AfterFix.sol b/e2e/08-autofix/contracts/00-generic/Foo1AfterFix.sol new file mode 100644 index 00000000..ca900d69 --- /dev/null +++ b/e2e/08-autofix/contracts/00-generic/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/contracts/00-generic/Foo1BeforeFix.sol b/e2e/08-autofix/contracts/00-generic/Foo1BeforeFix.sol new file mode 100644 index 00000000..78bcaec7 --- /dev/null +++ b/e2e/08-autofix/contracts/00-generic/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/contracts/Foo1.sol b/e2e/08-autofix/contracts/Foo1.sol new file mode 100644 index 00000000..05167b67 --- /dev/null +++ b/e2e/08-autofix/contracts/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 fofo() public {} + function _fofo() public {} + function _fofo() internal {} + function fofo() 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/contracts/explicit-types/.solhint.json b/e2e/08-autofix/contracts/explicit-types/.solhint.json new file mode 100644 index 00000000..7bd7c6a2 --- /dev/null +++ b/e2e/08-autofix/contracts/explicit-types/.solhint.json @@ -0,0 +1,5 @@ +{ + "rules": { + "explicit-types": ["error", "explicit"] + } +} diff --git a/e2e/08-autofix/contracts/explicit-types/Foo1.sol b/e2e/08-autofix/contracts/explicit-types/Foo1.sol new file mode 100644 index 00000000..78bcaec7 --- /dev/null +++ b/e2e/08-autofix/contracts/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/contracts/explicit-types/Foo1AfterFix.sol b/e2e/08-autofix/contracts/explicit-types/Foo1AfterFix.sol new file mode 100644 index 00000000..ca900d69 --- /dev/null +++ b/e2e/08-autofix/contracts/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/contracts/explicit-types/Foo1BeforeFix.sol b/e2e/08-autofix/contracts/explicit-types/Foo1BeforeFix.sol new file mode 100644 index 00000000..78bcaec7 --- /dev/null +++ b/e2e/08-autofix/contracts/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/contracts/no-console/.solhint.json b/e2e/08-autofix/contracts/no-console/.solhint.json new file mode 100644 index 00000000..2ff50f91 --- /dev/null +++ b/e2e/08-autofix/contracts/no-console/.solhint.json @@ -0,0 +1,5 @@ +{ + "rules": { + "no-console": "error" + } +} diff --git a/e2e/08-autofix/contracts/no-console/Foo1.sol b/e2e/08-autofix/contracts/no-console/Foo1.sol new file mode 100644 index 00000000..93b6d395 --- /dev/null +++ b/e2e/08-autofix/contracts/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/contracts/no-console/Foo1AfterFix.sol b/e2e/08-autofix/contracts/no-console/Foo1AfterFix.sol new file mode 100644 index 00000000..22075370 --- /dev/null +++ b/e2e/08-autofix/contracts/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/contracts/no-console/Foo1BeforeFix.sol b/e2e/08-autofix/contracts/no-console/Foo1BeforeFix.sol new file mode 100644 index 00000000..93b6d395 --- /dev/null +++ b/e2e/08-autofix/contracts/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/contracts/private-vars-underscore/.solhint.json b/e2e/08-autofix/contracts/private-vars-underscore/.solhint.json new file mode 100644 index 00000000..47ff676f --- /dev/null +++ b/e2e/08-autofix/contracts/private-vars-underscore/.solhint.json @@ -0,0 +1,5 @@ +{ + "rules": { + "private-vars-leading-underscore": ["error",{"strict":false}] + } +} diff --git a/e2e/08-autofix/contracts/private-vars-underscore/Foo1.sol b/e2e/08-autofix/contracts/private-vars-underscore/Foo1.sol new file mode 100644 index 00000000..f8a07515 --- /dev/null +++ b/e2e/08-autofix/contracts/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/contracts/private-vars-underscore/Foo1AfterFix.sol b/e2e/08-autofix/contracts/private-vars-underscore/Foo1AfterFix.sol new file mode 100644 index 00000000..87b80ad6 --- /dev/null +++ b/e2e/08-autofix/contracts/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/contracts/private-vars-underscore/Foo1BeforeFix.sol b/e2e/08-autofix/contracts/private-vars-underscore/Foo1BeforeFix.sol new file mode 100644 index 00000000..f8a07515 --- /dev/null +++ b/e2e/08-autofix/contracts/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..cf69b386 --- /dev/null +++ b/e2e/autofix-test.js @@ -0,0 +1,262 @@ +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 + +function retrieveParams() { + if (E2E) { + return { command: 'solhint', param1: '' } + } else { + return { command: 'node', param1: 'solhint' } + } +} + +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) { + // Read the content from the source file + const content = fs.readFileSync(sourcePath) + + // Write the content to the destination file, overwriting it if it exists + fs.writeFileSync(destinationPath, content) +} + +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', () => { + const commands = retrieveParams() + let PATH = 'e2e/08-autofix/' + let SUBPATH = 'contracts/00-generic/' + let sourceFilePath = `${PATH}${SUBPATH}Foo1BeforeFix.sol` + let currentFile = `${PATH}${SUBPATH}Foo1.sol` + + describe('--fix without noPrompt', () => { + after(function () { + copyFile(sourceFilePath, currentFile) + }) + + it('should terminate with --fix and user choose NOT to continue', () => { + + shell.exec( + `pwd` + ) + + const solhintProcess = spawnSync( + `${commands.command}`, + [ + `${commands.param1}`, + '-c', + `${PATH}${SUBPATH}.solhint.json`, + `${PATH}${SUBPATH}Foo1.sol`, + '--fix', + '--disc', + ], + { + input: 'n\n', // Provide 'n' as input + } + ) + + expect(solhintProcess.status).to.equal(0) + expect(solhintProcess.stdout.toString().includes('Process terminated by user')) + }) + + it('should fix with --fix and user choose YES to continue', () => { + sourceFilePath = `${PATH}${SUBPATH}Foo1BeforeFix.sol` + currentFile = `${PATH}${SUBPATH}Foo1.sol` + + result = compareTextFiles(sourceFilePath, `${PATH}${SUBPATH}Foo1.sol`) + expect(result).to.be.true + + const solhintProcess = spawnSync( + `${commands.command}`, + [ + `${commands.param1}`, + '-c', + `${PATH}${SUBPATH}.solhint.json`, + currentFile, + '--fix', + '--disc', + ], + { + input: 'y\n', // Provide 'y' as input + } + ) + + expect(solhintProcess.status).to.equal(1) + expect(solhintProcess.stdout.toString().includes('5 problems (5 errors, 0 warnings)')) + }) + + it('should compare resulted file with template file and they should match 1', () => { + result = compareTextFiles(currentFile, `${PATH}${SUBPATH}Foo1AfterFix.sol`) + expect(result).to.be.true + }) + }) + + describe('--fix with noPrompt', () => { + after(function () { + copyFile(sourceFilePath, currentFile) + }) + it('should fix file when noPrompt', () => { + sourceFilePath = `${PATH}${SUBPATH}Foo1BeforeFix.sol` + currentFile = `${PATH}${SUBPATH}Foo1.sol` + + result = compareTextFiles(sourceFilePath, `${PATH}${SUBPATH}Foo1.sol`) + expect(result).to.be.true + + const { code, stdout } = shell.exec( + `${commands.command} ${commands.param1} -c ${PATH}${SUBPATH}.solhint.json ${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) + }) + + it('files should match', () => { + result = compareTextFiles(currentFile, `${PATH}${SUBPATH}Foo1AfterFix.sol`) + expect(result).to.be.true + }) + }) + + }) + + describe('autofix rule: explicit-types', () => { + describe('--fix with noPrompt', () => { + const commands = retrieveParams() + let PATH = 'e2e/08-autofix/' + let SUBPATH = 'contracts/explicit-types/' + let sourceFilePath = `${PATH}${SUBPATH}Foo1BeforeFix.sol` + let currentFile = `${PATH}${SUBPATH}Foo1.sol` + after(function () { + copyFile(sourceFilePath, currentFile) + }) + it('should fix file when noPrompt', () => { + sourceFilePath = `${PATH}${SUBPATH}Foo1BeforeFix.sol` + currentFile = `${PATH}${SUBPATH}Foo1.sol` + + result = compareTextFiles(sourceFilePath, `${PATH}${SUBPATH}Foo1.sol`) + expect(result).to.be.true + + const { code, stdout } = shell.exec( + `${commands.command} ${commands.param1} -c ${PATH}${SUBPATH}.solhint.json ${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) + }) + + it('files should match', () => { + result = compareTextFiles(currentFile, `${PATH}${SUBPATH}Foo1AfterFix.sol`) + expect(result).to.be.true + }) + }) + }) + + describe('autofix rule: no-console', () => { + describe('--fix with noPrompt', () => { + const commands = retrieveParams() + let PATH = 'e2e/08-autofix/' + let SUBPATH = 'contracts/no-console/' + let sourceFilePath = `${PATH}${SUBPATH}Foo1BeforeFix.sol` + let currentFile = `${PATH}${SUBPATH}Foo1.sol` + after(function () { + copyFile(sourceFilePath, currentFile) + }) + it('should fix file when noPrompt', () => { + sourceFilePath = `${PATH}${SUBPATH}Foo1BeforeFix.sol` + currentFile = `${PATH}${SUBPATH}Foo1.sol` + + result = compareTextFiles(sourceFilePath, `${PATH}${SUBPATH}Foo1.sol`) + expect(result).to.be.true + + const { code, stdout } = shell.exec( + `${commands.command} ${commands.param1} -c ${PATH}${SUBPATH}.solhint.json ${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) + }) + + it('files should match', () => { + result = compareTextFiles(currentFile, `${PATH}${SUBPATH}Foo1AfterFix.sol`) + expect(result).to.be.true + }) + }) + }) + + describe('autofix rule: private-vars-leading-underscore', () => { + describe('--fix with noPrompt', () => { + const commands = retrieveParams() + let PATH = 'e2e/08-autofix/' + let SUBPATH = 'contracts/private-vars-underscore/' + let sourceFilePath = `${PATH}${SUBPATH}Foo1BeforeFix.sol` + let currentFile = `${PATH}${SUBPATH}Foo1.sol` + after(function () { + copyFile(sourceFilePath, currentFile) + }) + it('should fix file when noPrompt', () => { + sourceFilePath = `${PATH}${SUBPATH}Foo1BeforeFix.sol` + currentFile = `${PATH}${SUBPATH}Foo1.sol` + + result = compareTextFiles(sourceFilePath, `${PATH}${SUBPATH}Foo1.sol`) + expect(result).to.be.true + + const { code, stdout } = shell.exec( + `${commands.command} ${commands.param1} -c ${PATH}${SUBPATH}.solhint.json ${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) + }) + + it('files should match', () => { + result = compareTextFiles(currentFile, `${PATH}${SUBPATH}Foo1AfterFix.sol`) + expect(result).to.be.true + }) + }) + }) + }) +}) diff --git a/e2e/package.json b/e2e/package.json index 3f817075..cf358363 100644 --- a/e2e/package.json +++ b/e2e/package.json @@ -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/rules/best-practises/explicit-types.js b/lib/rules/best-practises/explicit-types.js index 266a38dd..3273b340 100644 --- a/lib/rules/best-practises/explicit-types.js +++ b/lib/rules/best-practises/explicit-types.js @@ -58,7 +58,7 @@ const meta = { }, notes: [ { - note: 'Solhint allows this rule to automatically fix the code with `--fix` or `--fixShow` option', + note: 'Solhint allows this rule to automatically fix the code with `--fix` option', }, ], }, diff --git a/lib/rules/security/avoid-sha3.js b/lib/rules/security/avoid-sha3.js index aa0127c5..a8d5c0bb 100644 --- a/lib/rules/security/avoid-sha3.js +++ b/lib/rules/security/avoid-sha3.js @@ -9,7 +9,7 @@ const meta = { category: 'Security Rules', notes: [ { - note: 'Solhint allows this rule to automatically fix the code with `--fix` or `--fixShow` option', + note: 'Solhint allows this rule to automatically fix the code with `--fix` option', }, ], }, diff --git a/lib/rules/security/avoid-throw.js b/lib/rules/security/avoid-throw.js index 279233ff..72a9f4f1 100644 --- a/lib/rules/security/avoid-throw.js +++ b/lib/rules/security/avoid-throw.js @@ -9,7 +9,7 @@ const meta = { category: 'Security Rules', notes: [ { - note: 'Solhint allows this rule to automatically fix the code with `--fix` or `--fixShow` option', + note: 'Solhint allows this rule to automatically fix the code with `--fix` option', }, ], }, diff --git a/solhint.js b/solhint.js index 5f8d5b4a..bda55ea5 100644 --- a/solhint.js +++ b/solhint.js @@ -29,7 +29,7 @@ function init() { .option('-q, --quiet', 'report errors only - default: false') .option('--ignore-path [file_name]', 'file to use as your .solhintignore') .option('--fix', 'automatically fix problems. Skips fixes in report') - .option('--fixShow', 'automatically fix problems. Show 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') @@ -82,10 +82,12 @@ function askUserToContinue(callback) { } function execMainAction() { - if ((program.opts().fix || program.opts().fixShow) && !program.opts().noPrompt) { + // 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() @@ -126,7 +128,8 @@ function executeMainActionLogic() { const reportLists = program.args.filter(_.isString).map(processPath) const reports = _.flatten(reportLists) - if (program.opts().fix || program.opts().fixShow) { + // if (program.opts().fix || program.opts().fixShow) { + if (program.opts().fix) { for (const report of reports) { const inputSrc = fs.readFileSync(report.filePath).toString() @@ -138,17 +141,18 @@ function executeMainActionLogic() { const { fixed, output } = applyFixes(fixes, inputSrc) if (fixed) { - // skip or not the report when fixed - 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}` - } - }) - } + // // 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) } } From e7ece1d5e25ca956ed9ab5a19481fda29ca8c2d4 Mon Sep 17 00:00:00 2001 From: dbale-altoros Date: Mon, 23 Oct 2023 17:11:37 -0300 Subject: [PATCH 13/20] fixed readme --- README.md | 2 +- e2e/08-autofix/contracts/Foo1.sol | 83 ------------------------------- 2 files changed, 1 insertion(+), 84 deletions(-) delete mode 100644 e2e/08-autofix/contracts/Foo1.sol diff --git a/README.md b/README.md index 64b23622..27ffa79b 100644 --- a/README.md +++ b/README.md @@ -62,7 +62,7 @@ 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. Skip report + --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 diff --git a/e2e/08-autofix/contracts/Foo1.sol b/e2e/08-autofix/contracts/Foo1.sol deleted file mode 100644 index 05167b67..00000000 --- a/e2e/08-autofix/contracts/Foo1.sol +++ /dev/null @@ -1,83 +0,0 @@ -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 fofo() public {} - function _fofo() public {} - function _fofo() internal {} - function fofo() 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; - } -} From 9971ccaf68382819fc2a50ef0b5fd01728f683e8 Mon Sep 17 00:00:00 2001 From: dbale-altoros Date: Mon, 23 Oct 2023 17:20:39 -0300 Subject: [PATCH 14/20] fixed e2e package.json --- e2e/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/e2e/package.json b/e2e/package.json index cf358363..a9b7e733 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", From bd4e4ddb4f134f4d6d25f776b7f8dfabc625e54f Mon Sep 17 00:00:00 2001 From: danilo neves cruz Date: Mon, 23 Oct 2023 20:10:28 -0300 Subject: [PATCH 15/20] =?UTF-8?q?=F0=9F=90=9B=20one-contract-per-file:=20i?= =?UTF-8?q?gnore=20interfaces?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/rules/best-practises/one-contract-per-file.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 From 06808b3599fead320827f1f7de3b37e89e63f37b Mon Sep 17 00:00:00 2001 From: Zou Guangxian Date: Tue, 24 Oct 2023 21:31:01 +0800 Subject: [PATCH 16/20] feat: support require the package with fullname supports require.resolve(package) in extends field --- lib/config/config-file.js | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/lib/config/config-file.js b/lib/config/config-file.js index f101d8ff..b3aace6c 100644 --- a/lib/config/config-file.js +++ b/lib/config/config-file.js @@ -64,8 +64,13 @@ 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) { From 79b653d5027a36b806d2dec2709e3bbce34717b7 Mon Sep 17 00:00:00 2001 From: dbale-altoros Date: Mon, 23 Oct 2023 17:44:05 -0300 Subject: [PATCH 17/20] e2e tests config --- .../00-generic => commands}/.solhint.json | 0 .../00-generic => commands}/Foo1.sol | 0 .../00-generic => commands}/Foo1AfterFix.sol | 0 .../00-generic => commands}/Foo1BeforeFix.sol | 0 .../explicit-types/.solhint.json | 0 .../{contracts => }/explicit-types/Foo1.sol | 0 .../explicit-types/Foo1AfterFix.sol | 0 .../explicit-types/Foo1BeforeFix.sol | 0 .../{contracts => }/no-console/.solhint.json | 0 .../{contracts => }/no-console/Foo1.sol | 0 .../no-console/Foo1AfterFix.sol | 0 .../no-console/Foo1BeforeFix.sol | 0 .../private-vars-underscore/.solhint.json | 0 .../private-vars-underscore/Foo1.sol | 0 .../private-vars-underscore/Foo1AfterFix.sol | 0 .../private-vars-underscore/Foo1BeforeFix.sol | 0 e2e/autofix-test.js | 210 ++++++++++-------- e2e/package.json | 2 +- solhint.js | 10 +- 19 files changed, 121 insertions(+), 101 deletions(-) rename e2e/08-autofix/{contracts/00-generic => commands}/.solhint.json (100%) rename e2e/08-autofix/{contracts/00-generic => commands}/Foo1.sol (100%) rename e2e/08-autofix/{contracts/00-generic => commands}/Foo1AfterFix.sol (100%) rename e2e/08-autofix/{contracts/00-generic => commands}/Foo1BeforeFix.sol (100%) rename e2e/08-autofix/{contracts => }/explicit-types/.solhint.json (100%) rename e2e/08-autofix/{contracts => }/explicit-types/Foo1.sol (100%) rename e2e/08-autofix/{contracts => }/explicit-types/Foo1AfterFix.sol (100%) rename e2e/08-autofix/{contracts => }/explicit-types/Foo1BeforeFix.sol (100%) rename e2e/08-autofix/{contracts => }/no-console/.solhint.json (100%) rename e2e/08-autofix/{contracts => }/no-console/Foo1.sol (100%) rename e2e/08-autofix/{contracts => }/no-console/Foo1AfterFix.sol (100%) rename e2e/08-autofix/{contracts => }/no-console/Foo1BeforeFix.sol (100%) rename e2e/08-autofix/{contracts => }/private-vars-underscore/.solhint.json (100%) rename e2e/08-autofix/{contracts => }/private-vars-underscore/Foo1.sol (100%) rename e2e/08-autofix/{contracts => }/private-vars-underscore/Foo1AfterFix.sol (100%) rename e2e/08-autofix/{contracts => }/private-vars-underscore/Foo1BeforeFix.sol (100%) diff --git a/e2e/08-autofix/contracts/00-generic/.solhint.json b/e2e/08-autofix/commands/.solhint.json similarity index 100% rename from e2e/08-autofix/contracts/00-generic/.solhint.json rename to e2e/08-autofix/commands/.solhint.json diff --git a/e2e/08-autofix/contracts/00-generic/Foo1.sol b/e2e/08-autofix/commands/Foo1.sol similarity index 100% rename from e2e/08-autofix/contracts/00-generic/Foo1.sol rename to e2e/08-autofix/commands/Foo1.sol diff --git a/e2e/08-autofix/contracts/00-generic/Foo1AfterFix.sol b/e2e/08-autofix/commands/Foo1AfterFix.sol similarity index 100% rename from e2e/08-autofix/contracts/00-generic/Foo1AfterFix.sol rename to e2e/08-autofix/commands/Foo1AfterFix.sol diff --git a/e2e/08-autofix/contracts/00-generic/Foo1BeforeFix.sol b/e2e/08-autofix/commands/Foo1BeforeFix.sol similarity index 100% rename from e2e/08-autofix/contracts/00-generic/Foo1BeforeFix.sol rename to e2e/08-autofix/commands/Foo1BeforeFix.sol diff --git a/e2e/08-autofix/contracts/explicit-types/.solhint.json b/e2e/08-autofix/explicit-types/.solhint.json similarity index 100% rename from e2e/08-autofix/contracts/explicit-types/.solhint.json rename to e2e/08-autofix/explicit-types/.solhint.json diff --git a/e2e/08-autofix/contracts/explicit-types/Foo1.sol b/e2e/08-autofix/explicit-types/Foo1.sol similarity index 100% rename from e2e/08-autofix/contracts/explicit-types/Foo1.sol rename to e2e/08-autofix/explicit-types/Foo1.sol diff --git a/e2e/08-autofix/contracts/explicit-types/Foo1AfterFix.sol b/e2e/08-autofix/explicit-types/Foo1AfterFix.sol similarity index 100% rename from e2e/08-autofix/contracts/explicit-types/Foo1AfterFix.sol rename to e2e/08-autofix/explicit-types/Foo1AfterFix.sol diff --git a/e2e/08-autofix/contracts/explicit-types/Foo1BeforeFix.sol b/e2e/08-autofix/explicit-types/Foo1BeforeFix.sol similarity index 100% rename from e2e/08-autofix/contracts/explicit-types/Foo1BeforeFix.sol rename to e2e/08-autofix/explicit-types/Foo1BeforeFix.sol diff --git a/e2e/08-autofix/contracts/no-console/.solhint.json b/e2e/08-autofix/no-console/.solhint.json similarity index 100% rename from e2e/08-autofix/contracts/no-console/.solhint.json rename to e2e/08-autofix/no-console/.solhint.json diff --git a/e2e/08-autofix/contracts/no-console/Foo1.sol b/e2e/08-autofix/no-console/Foo1.sol similarity index 100% rename from e2e/08-autofix/contracts/no-console/Foo1.sol rename to e2e/08-autofix/no-console/Foo1.sol diff --git a/e2e/08-autofix/contracts/no-console/Foo1AfterFix.sol b/e2e/08-autofix/no-console/Foo1AfterFix.sol similarity index 100% rename from e2e/08-autofix/contracts/no-console/Foo1AfterFix.sol rename to e2e/08-autofix/no-console/Foo1AfterFix.sol diff --git a/e2e/08-autofix/contracts/no-console/Foo1BeforeFix.sol b/e2e/08-autofix/no-console/Foo1BeforeFix.sol similarity index 100% rename from e2e/08-autofix/contracts/no-console/Foo1BeforeFix.sol rename to e2e/08-autofix/no-console/Foo1BeforeFix.sol diff --git a/e2e/08-autofix/contracts/private-vars-underscore/.solhint.json b/e2e/08-autofix/private-vars-underscore/.solhint.json similarity index 100% rename from e2e/08-autofix/contracts/private-vars-underscore/.solhint.json rename to e2e/08-autofix/private-vars-underscore/.solhint.json diff --git a/e2e/08-autofix/contracts/private-vars-underscore/Foo1.sol b/e2e/08-autofix/private-vars-underscore/Foo1.sol similarity index 100% rename from e2e/08-autofix/contracts/private-vars-underscore/Foo1.sol rename to e2e/08-autofix/private-vars-underscore/Foo1.sol diff --git a/e2e/08-autofix/contracts/private-vars-underscore/Foo1AfterFix.sol b/e2e/08-autofix/private-vars-underscore/Foo1AfterFix.sol similarity index 100% rename from e2e/08-autofix/contracts/private-vars-underscore/Foo1AfterFix.sol rename to e2e/08-autofix/private-vars-underscore/Foo1AfterFix.sol diff --git a/e2e/08-autofix/contracts/private-vars-underscore/Foo1BeforeFix.sol b/e2e/08-autofix/private-vars-underscore/Foo1BeforeFix.sol similarity index 100% rename from e2e/08-autofix/contracts/private-vars-underscore/Foo1BeforeFix.sol rename to e2e/08-autofix/private-vars-underscore/Foo1BeforeFix.sol diff --git a/e2e/autofix-test.js b/e2e/autofix-test.js index cf69b386..1f11d184 100644 --- a/e2e/autofix-test.js +++ b/e2e/autofix-test.js @@ -8,11 +8,17 @@ const spawnSync = require('spawn-sync') const E2E = true -function retrieveParams() { +let params +let currentConfig +let currentFile +let beforeFixFile +let afterFixFile + +function retrieveParams(subpath) { if (E2E) { - return { command: 'solhint', param1: '' } + return { command: 'solhint', param1: '', path: '', subpath } } else { - return { command: 'node', param1: 'solhint' } + return { command: 'node', param1: 'solhint', path: 'e2e/08-autofix/', subpath } } } @@ -24,11 +30,7 @@ function compareTextFiles(file1Path, file2Path) { } function copyFile(sourcePath, destinationPath) { - // Read the content from the source file - const content = fs.readFileSync(sourcePath) - - // Write the content to the destination file, overwriting it if it exists - fs.writeFileSync(destinationPath, content) + shell.cp(sourcePath, destinationPath) } function useFixture(dir) { @@ -56,35 +58,28 @@ describe('e2e', function () { } describe('autofix command line options', () => { - const commands = retrieveParams() - let PATH = 'e2e/08-autofix/' - let SUBPATH = 'contracts/00-generic/' - let sourceFilePath = `${PATH}${SUBPATH}Foo1BeforeFix.sol` - let currentFile = `${PATH}${SUBPATH}Foo1.sol` + 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 () { - copyFile(sourceFilePath, currentFile) + if (!E2E) { + copyFile(beforeFixFile, currentFile) + } }) it('should terminate with --fix and user choose NOT to continue', () => { - - shell.exec( - `pwd` - ) - const solhintProcess = spawnSync( - `${commands.command}`, - [ - `${commands.param1}`, - '-c', - `${PATH}${SUBPATH}.solhint.json`, - `${PATH}${SUBPATH}Foo1.sol`, - '--fix', - '--disc', - ], + `${params.command}`, + [`${params.param1}`, '-c', currentConfig, currentFile, '--fix', '--disc'], { input: 'n\n', // Provide 'n' as input + shell: true, } ) @@ -92,51 +87,45 @@ describe('e2e', function () { expect(solhintProcess.stdout.toString().includes('Process terminated by user')) }) - it('should fix with --fix and user choose YES to continue', () => { - sourceFilePath = `${PATH}${SUBPATH}Foo1BeforeFix.sol` - currentFile = `${PATH}${SUBPATH}Foo1.sol` - - result = compareTextFiles(sourceFilePath, `${PATH}${SUBPATH}Foo1.sol`) + 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( - `${commands.command}`, - [ - `${commands.param1}`, - '-c', - `${PATH}${SUBPATH}.solhint.json`, - currentFile, - '--fix', - '--disc', - ], + `${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 compare resulted file with template file and they should match 1', () => { - result = compareTextFiles(currentFile, `${PATH}${SUBPATH}Foo1AfterFix.sol`) - expect(result).to.be.true - }) + }) + 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 () { - copyFile(sourceFilePath, currentFile) + if (!E2E) { + copyFile(beforeFixFile, currentFile) + } }) - it('should fix file when noPrompt', () => { - sourceFilePath = `${PATH}${SUBPATH}Foo1BeforeFix.sol` - currentFile = `${PATH}${SUBPATH}Foo1.sol` - result = compareTextFiles(sourceFilePath, `${PATH}${SUBPATH}Foo1.sol`) + 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( - `${commands.command} ${commands.param1} -c ${PATH}${SUBPATH}.solhint.json ${currentFile} --fix --disc --noPrompt` + `${params.command} ${params.param1} -c ${currentConfig} ${currentFile} --fix --disc --noPrompt` ) expect(code).to.equal(1) @@ -144,35 +133,41 @@ describe('e2e', function () { const reportLines = stdout.split('\n') const finalLine = '5 problems (5 errors, 0 warnings)' expect(reportLines[reportLines.length - 3]).to.contain(finalLine) - }) - it('files should match', () => { - result = compareTextFiles(currentFile, `${PATH}${SUBPATH}Foo1AfterFix.sol`) + 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', () => { - const commands = retrieveParams() - let PATH = 'e2e/08-autofix/' - let SUBPATH = 'contracts/explicit-types/' - let sourceFilePath = `${PATH}${SUBPATH}Foo1BeforeFix.sol` - let currentFile = `${PATH}${SUBPATH}Foo1.sol` after(function () { - copyFile(sourceFilePath, currentFile) + if (!E2E) { + copyFile(beforeFixFile, currentFile) + } }) - it('should fix file when noPrompt', () => { - sourceFilePath = `${PATH}${SUBPATH}Foo1BeforeFix.sol` - currentFile = `${PATH}${SUBPATH}Foo1.sol` - result = compareTextFiles(sourceFilePath, `${PATH}${SUBPATH}Foo1.sol`) + 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( - `${commands.command} ${commands.param1} -c ${PATH}${SUBPATH}.solhint.json ${currentFile} --fix --disc --noPrompt` + `${params.command} ${params.param1} -c ${currentConfig} ${currentFile} --fix --disc --noPrompt` ) expect(code).to.equal(1) @@ -180,34 +175,41 @@ describe('e2e', function () { const reportLines = stdout.split('\n') const finalLine = '5 problems (5 errors, 0 warnings)' expect(reportLines[reportLines.length - 3]).to.contain(finalLine) - }) - it('files should match', () => { - result = compareTextFiles(currentFile, `${PATH}${SUBPATH}Foo1AfterFix.sol`) + 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', () => { - const commands = retrieveParams() - let PATH = 'e2e/08-autofix/' - let SUBPATH = 'contracts/no-console/' - let sourceFilePath = `${PATH}${SUBPATH}Foo1BeforeFix.sol` - let currentFile = `${PATH}${SUBPATH}Foo1.sol` after(function () { - copyFile(sourceFilePath, currentFile) + if (!E2E) { + copyFile(beforeFixFile, currentFile) + } }) - it('should fix file when noPrompt', () => { - sourceFilePath = `${PATH}${SUBPATH}Foo1BeforeFix.sol` - currentFile = `${PATH}${SUBPATH}Foo1.sol` - result = compareTextFiles(sourceFilePath, `${PATH}${SUBPATH}Foo1.sol`) + 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( - `${commands.command} ${commands.param1} -c ${PATH}${SUBPATH}.solhint.json ${currentFile} --fix --disc --noPrompt` + `${params.command} ${params.param1} -c ${currentConfig} ${currentFile} --fix --disc --noPrompt` ) expect(code).to.equal(1) @@ -215,34 +217,41 @@ describe('e2e', function () { const reportLines = stdout.split('\n') const finalLine = '9 problems (9 errors, 0 warnings)' expect(reportLines[reportLines.length - 3]).to.contain(finalLine) - }) - it('files should match', () => { - result = compareTextFiles(currentFile, `${PATH}${SUBPATH}Foo1AfterFix.sol`) + 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', () => { - const commands = retrieveParams() - let PATH = 'e2e/08-autofix/' - let SUBPATH = 'contracts/private-vars-underscore/' - let sourceFilePath = `${PATH}${SUBPATH}Foo1BeforeFix.sol` - let currentFile = `${PATH}${SUBPATH}Foo1.sol` after(function () { - copyFile(sourceFilePath, currentFile) + if (!E2E) { + copyFile(beforeFixFile, currentFile) + } }) - it('should fix file when noPrompt', () => { - sourceFilePath = `${PATH}${SUBPATH}Foo1BeforeFix.sol` - currentFile = `${PATH}${SUBPATH}Foo1.sol` - result = compareTextFiles(sourceFilePath, `${PATH}${SUBPATH}Foo1.sol`) + 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( - `${commands.command} ${commands.param1} -c ${PATH}${SUBPATH}.solhint.json ${currentFile} --fix --disc --noPrompt` + `${params.command} ${params.param1} -c ${currentConfig} ${currentFile} --fix --disc --noPrompt` ) expect(code).to.equal(1) @@ -250,13 +259,18 @@ describe('e2e', function () { const reportLines = stdout.split('\n') const finalLine = '27 problems (27 errors, 0 warnings)' expect(reportLines[reportLines.length - 3]).to.contain(finalLine) - }) - it('files should match', () => { - result = compareTextFiles(currentFile, `${PATH}${SUBPATH}Foo1AfterFix.sol`) + 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/package.json b/e2e/package.json index a9b7e733..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 autofix-test.js" + "test": "mocha test.js formatters-test.js autofix-test.js" }, "author": "", "license": "MIT", diff --git a/solhint.js b/solhint.js index bda55ea5..520a6e5c 100644 --- a/solhint.js +++ b/solhint.js @@ -1,5 +1,4 @@ #!/usr/bin/env node - const program = require('commander') const _ = require('lodash') const fs = require('fs') @@ -153,7 +152,14 @@ function executeMainActionLogic() { } }) // } - fs.writeFileSync(report.filePath, output) + + // 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) + } } } } From ffb4a166425fe2139dd7aa8488ed40825087b99e Mon Sep 17 00:00:00 2001 From: danilo neves cruz Date: Thu, 26 Oct 2023 12:29:01 -0300 Subject: [PATCH 18/20] =?UTF-8?q?=E2=9C=85=20one-contract-per-file:=20test?= =?UTF-8?q?=20libraries=20and=20interfaces?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../best-practises/one-contract-per-file.js | 38 ++++++++++++++++++- .../best-practises/one-contract-per-file.js | 31 +++++++++++++++ 2 files changed, 68 insertions(+), 1 deletion(-) 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/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!') + }) }) From cdcccee9615effdcda87d8613b7237cf8122cbb6 Mon Sep 17 00:00:00 2001 From: Zou Guangxian Date: Fri, 27 Oct 2023 23:36:17 +0800 Subject: [PATCH 19/20] fix: eslint issues --- lib/config/config-file.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/lib/config/config-file.js b/lib/config/config-file.js index b3aace6c..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') @@ -69,7 +70,9 @@ const configGetter = (path) => { if (isAbsolute(path)) { return require(path) } - return path.startsWith('solhint:') ? getSolhintCoreConfig(path) : require(`solhint-config-${path}`) + return path.startsWith('solhint:') + ? getSolhintCoreConfig(path) + : require(`solhint-config-${path}`) } const applyExtends = (config, getter = configGetter) => { From c952a8fa560a49512e278b307a1e9627dda3e009 Mon Sep 17 00:00:00 2001 From: dbale-altoros Date: Fri, 27 Oct 2023 14:49:31 -0300 Subject: [PATCH 20/20] pre release changes --- CHANGELOG.md | 16 ++++++++++------ docs/writing-plugins.md | 1 + 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7ae0b65b..96c84c9e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,18 +1,22 @@ ## [4.0] - 2023-10-01 ### Updated -- Enhance explicit types sensitivity [493](https://github.com/protofire/solhint/pull/493) (Thanks to @vladyan18) +- 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 -- `fix` option now shows the report on screen -- `save` option to store report on disk with the standard or the specified format -- Check for updates on Solhint version to keep users with the last versin available. There's an option to disable this check (`--disc`) -- Autofix for `explicit-types` rule [504](https://github.com/protofire/solhint/pull/504) +- 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) +- 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)) 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`: