Comparing version 3.6.2 to 4.0.0
const fs = require('fs') | ||
const path = require('path') | ||
const _ = require('lodash') | ||
@@ -67,4 +68,11 @@ const { cosmiconfigSync } = require('cosmiconfig') | ||
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}`) | ||
} | ||
@@ -71,0 +79,0 @@ const applyExtends = (config, getter = configGetter) => { |
@@ -10,2 +10,3 @@ const BaseChecker = require('../base-checker') | ||
const DEFAULT_SEVERITY = 'warn' | ||
let typesToSearch | ||
@@ -39,2 +40,6 @@ const ruleId = 'explicit-types' | ||
}, | ||
{ | ||
description: 'If explicit is selected', | ||
code: 'uint256 public variableName = uint256(5)', | ||
}, | ||
], | ||
@@ -50,4 +55,13 @@ bad: [ | ||
}, | ||
{ | ||
description: 'At any setting', | ||
code: 'uint public variableName = uint256(5)', | ||
}, | ||
], | ||
}, | ||
notes: [ | ||
{ | ||
note: 'Solhint allows this rule to automatically fix the code with `--fix` option', | ||
}, | ||
], | ||
}, | ||
@@ -58,2 +72,3 @@ | ||
defaultSetup: [DEFAULT_SEVERITY, DEFAULT_OPTION], | ||
fixable: true, | ||
@@ -72,5 +87,25 @@ schema: { | ||
this.isExplicit = this.configOption === 'explicit' | ||
// if explicit, it will search for implicit and viceversa | ||
if (this.isExplicit) { | ||
typesToSearch = IMPLICIT_TYPES | ||
} else { | ||
typesToSearch = EXPLICIT_TYPES | ||
} | ||
} | ||
VariableDeclaration(node) { | ||
this.validateInNode(node) | ||
} | ||
VariableDeclarationStatement(node) { | ||
if (!node.initialValue) return | ||
this.validateInNode(node.initialValue) | ||
} | ||
ExpressionStatement(node) { | ||
this.validateInNode(node) | ||
} | ||
validateInNode(node) { | ||
if (!VALID_CONFIGURATION_OPTIONS.includes(this.configOption)) { | ||
@@ -93,8 +128,3 @@ this.error(node, 'Error: Config error on [explicit-types]. See explicit-types.md.') | ||
// 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) | ||
} | ||
@@ -107,3 +137,7 @@ | ||
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) | ||
) | ||
} | ||
@@ -113,2 +147,11 @@ } | ||
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) { | ||
@@ -115,0 +158,0 @@ const names = [] |
@@ -31,2 +31,3 @@ const BaseChecker = require('../base-checker') | ||
defaultSetup: 'error', | ||
fixable: true, | ||
schema: null, | ||
@@ -42,3 +43,3 @@ } | ||
if (this.isConsoleImport(node)) { | ||
this.error(node, 'Unexpected import of console file') | ||
this.error(node, 'Unexpected import of console file', this.fixStatement(node, 'import')) | ||
} | ||
@@ -49,3 +50,3 @@ } | ||
if (this.isConsoleLog(node)) { | ||
this.error(node, 'Unexpected console statement') | ||
this.error(node, 'Unexpected console statement', this.fixStatement(node, 'call')) | ||
} | ||
@@ -70,4 +71,16 @@ } | ||
} | ||
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 |
@@ -28,2 +28,7 @@ const BaseChecker = require('../base-checker') | ||
}, | ||
notes: [ | ||
{ | ||
note: 'The rule ignores an empty constructor by default as long as base contracts are being inherited. See "Empty Constructor" example.', | ||
}, | ||
], | ||
}, | ||
@@ -30,0 +35,0 @@ |
@@ -36,3 +36,3 @@ const BaseChecker = require('../base-checker') | ||
const contractDefinitionCount = node.children.reduce((count, child) => { | ||
if (child.type === 'ContractDefinition') { | ||
if (child.type === 'ContractDefinition' && child.kind !== 'interface') { | ||
return count + 1 | ||
@@ -39,0 +39,0 @@ } |
@@ -14,3 +14,4 @@ const BaseChecker = require('../base-checker') | ||
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', | ||
@@ -24,6 +25,60 @@ options: [ | ||
description: | ||
'A JSON object with a single property "strict" specifying if the rule should apply to non state variables. Default: { strict: false }.', | ||
'A JSON object with a single property "strict" specifying if the rule should apply to ALL non state variables. Default: { strict: false }.', | ||
default: JSON.stringify(DEFAULT_OPTION), | ||
}, | ||
], | ||
examples: { | ||
good: [ | ||
{ | ||
description: 'Internal function with correct naming', | ||
code: 'function _thisIsInternal() internal {}', | ||
}, | ||
{ | ||
description: 'Private function with correct naming', | ||
code: 'function _thisIsPrivate() private {}', | ||
}, | ||
{ | ||
description: 'Internal state variable with correct naming', | ||
code: 'uint256 internal _thisIsInternalVariable;', | ||
}, | ||
{ | ||
description: | ||
'Internal state variable with correct naming (no visibility is considered internal)', | ||
code: 'uint256 _thisIsInternalVariable;', | ||
}, | ||
], | ||
bad: [ | ||
{ | ||
description: 'Internal function with incorrect naming', | ||
code: 'function thisIsInternal() internal {}', | ||
}, | ||
{ | ||
description: 'Private function with incorrect naming', | ||
code: 'function thisIsPrivate() private {}', | ||
}, | ||
{ | ||
description: 'Internal state variable with incorrect naming', | ||
code: 'uint256 internal thisIsInternalVariable;', | ||
}, | ||
{ | ||
description: | ||
'Internal state variable with incorrect naming (no visibility is considered internal)', | ||
code: 'uint256 thisIsInternalVariable;', | ||
}, | ||
], | ||
}, | ||
notes: [ | ||
{ | ||
note: 'This rule considers functions and variables in Libraries as well', | ||
}, | ||
{ | ||
note: 'This rule skips external and public functions', | ||
}, | ||
{ | ||
note: 'This rule skips external and public state variables', | ||
}, | ||
{ | ||
note: 'See [here](https://docs.soliditylang.org/en/latest/style-guide.html#underscore-prefix-for-non-external-functions-and-variables) for further information', | ||
}, | ||
], | ||
}, | ||
@@ -34,2 +89,3 @@ | ||
defaultSetup: [DEFAULT_SEVERITY, DEFAULT_OPTION], | ||
fixable: true, | ||
@@ -53,11 +109,11 @@ schema: { | ||
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 | ||
// } | ||
@@ -70,5 +126,6 @@ FunctionDefinition(node) { | ||
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') | ||
} | ||
@@ -88,3 +145,3 @@ | ||
if (this.isStrict) { | ||
this.validateName(node, false) | ||
this.validateName(node, false, 'variable') | ||
} | ||
@@ -97,6 +154,6 @@ return | ||
const shouldHaveLeadingUnderscore = isPrivate || isInternal | ||
this.validateName(node, shouldHaveLeadingUnderscore) | ||
this.validateName(node, shouldHaveLeadingUnderscore, 'variable') | ||
} | ||
validateName(node, shouldHaveLeadingUnderscore) { | ||
validateName(node, shouldHaveLeadingUnderscore, type) { | ||
if (node.name === null) { | ||
@@ -107,14 +164,32 @@ 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 |
@@ -10,2 +10,7 @@ const BaseChecker = require('../base-checker') | ||
category: 'Security Rules', | ||
notes: [ | ||
{ | ||
note: 'Solhint allows this rule to automatically fix the code with `--fix` option', | ||
}, | ||
], | ||
}, | ||
@@ -12,0 +17,0 @@ |
@@ -10,2 +10,7 @@ const BaseChecker = require('../base-checker') | ||
category: 'Security Rules', | ||
notes: [ | ||
{ | ||
note: 'Solhint allows this rule to automatically fix the code with `--fix` option', | ||
}, | ||
], | ||
}, | ||
@@ -12,0 +17,0 @@ |
{ | ||
"name": "solhint", | ||
"version": "3.6.2", | ||
"version": "4.0.0", | ||
"description": "Solidity Code Linter", | ||
@@ -38,2 +38,3 @@ "main": "lib/index.js", | ||
"author": "Ilya Drabenia <ilya.drobenya@gmail.com>", | ||
"contributors": ["Diego Bale <diego.bale@protofire.io>"], | ||
"license": "MIT", | ||
@@ -52,2 +53,3 @@ "dependencies": { | ||
"js-yaml": "^4.1.0", | ||
"latest-version": "^7.0.0", | ||
"lodash": "^4.17.21", | ||
@@ -54,0 +56,0 @@ "pluralize": "^8.0.0", |
@@ -8,2 +8,3 @@ <p align="center"> | ||
[![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) | ||
@@ -18,2 +19,3 @@ [![NPM version](https://badge.fury.io/js/solhint.svg)](https://npmjs.org/package/solhint) | ||
[JOIN OUR DISCORD SERVER](https://discord.gg/4TYGq3zpjs) | ||
## Installation | ||
@@ -65,4 +67,7 @@ | ||
--ignore-path [file_name] file to use as your .solhintignore | ||
--fix automatically fix problems | ||
--fix automatically fix problems and show report | ||
--noPrompt do not suggest to backup files when any `fix` option is selected | ||
--init create configuration file for solhint | ||
--disc do not check for solhint updates | ||
--save save report to file on current folder | ||
-h, --help output usage information | ||
@@ -75,4 +80,13 @@ | ||
``` | ||
### Note | ||
The `--fix` option currently works only on "avoid-throw" and "avoid-sha3" rules | ||
### Notes | ||
- Solhint checks if there are newer versions. The `--disc` option avoids that check. | ||
- `--save` option will create a file named as `YYYYMMDDHHMMSS_solhintReport.txt` on current folder with default or specified format | ||
### Fix | ||
This option currently works on: | ||
- avoid-throw | ||
- avoid-sha3 | ||
- no-console | ||
- explicit-types | ||
- private-vars-underscore | ||
<br><br> | ||
@@ -79,0 +93,0 @@ ## Configuration |
146
solhint.js
#!/usr/bin/env node | ||
const program = require('commander') | ||
@@ -7,2 +6,3 @@ const _ = require('lodash') | ||
const process = require('process') | ||
const readline = require('readline') | ||
@@ -31,4 +31,8 @@ const linter = require('./lib/index') | ||
.option('--ignore-path [file_name]', 'file to use as your .solhintignore') | ||
.option('--fix', 'automatically fix problems') | ||
.option('--fix', 'automatically fix problems. Skips fixes in report') | ||
// .option('--fixShow', 'automatically fix problems. Show fixes in report') | ||
.option('--noPrompt', 'do not suggest to backup files when any `fix` option is selected') | ||
.option('--init', 'create configuration file for solhint') | ||
.option('--disc', 'do not check for solhint updates') | ||
.option('--save', 'save report to file on current folder') | ||
.description('Linter for Solidity programming language') | ||
@@ -58,6 +62,56 @@ .action(execMainAction) | ||
} | ||
program.parse(process.argv) | ||
} | ||
function askUserToContinue(callback) { | ||
const rl = readline.createInterface({ | ||
input: process.stdin, | ||
output: process.stdout, | ||
}) | ||
rl.question( | ||
'\nFIX option detected. Solhint will modify your files whenever it finds a fix for a rule error. Please BACKUP your contracts first. \nContinue ? (y/n) ', | ||
(answer) => { | ||
// Close the readline interface. | ||
rl.close() | ||
// Normalize and pass the user's answer to the callback function. | ||
const normalizedAnswer = answer.trim().toLowerCase() | ||
callback(normalizedAnswer) | ||
} | ||
) | ||
} | ||
function execMainAction() { | ||
// if ((program.opts().fix || program.opts().fixShow) && !program.opts().noPrompt) { | ||
if (program.opts().fix && !program.opts().noPrompt) { | ||
askUserToContinue((userAnswer) => { | ||
if (userAnswer !== 'y') { | ||
console.log('\nProcess terminated by user') | ||
process.exit(0) | ||
} else { | ||
// User agreed, continue with the operation. | ||
continueExecution() | ||
} | ||
}) | ||
} else { | ||
// No need for user input, continue with the operation. | ||
continueExecution() | ||
} | ||
function continueExecution() { | ||
if (program.opts().disc) { | ||
executeMainActionLogic() | ||
} else { | ||
// Call checkForUpdate and wait for it to complete using .then() | ||
checkForUpdate().then(() => { | ||
// This block runs after checkForUpdate is complete | ||
executeMainActionLogic() | ||
}) | ||
} | ||
} | ||
} | ||
function executeMainActionLogic() { | ||
if (program.opts().init) { | ||
@@ -79,2 +133,3 @@ writeSampleConfigFile() | ||
// if (program.opts().fix || program.opts().fixShow) { | ||
if (program.opts().fix) { | ||
@@ -92,4 +147,22 @@ for (const report of reports) { | ||
if (fixed) { | ||
report.reports = report.reports.filter((x) => !x.fix) | ||
fs.writeFileSync(report.filePath, output) | ||
// // skip or not the report when fixed | ||
// // This was filtering fixed rules so status code was not 1 | ||
// if (program.opts().fix) { | ||
// report.reports = report.reports.filter((x) => !x.fix) | ||
// } else { | ||
// console.log('report.reports :>> ', report.reports) | ||
report.reports.forEach((report) => { | ||
if (report.fix !== null) { | ||
report.message = `[FIXED] - ${report.message}` | ||
} | ||
}) | ||
// } | ||
// fs.writeFileSync(report.filePath, output) | ||
try { | ||
fs.writeFileSync(report.filePath, output) | ||
// fs.writeFileSync('no-console/Foo1Modified.sol', output) | ||
} catch (error) { | ||
console.error('An error occurred while writing the file:', error) | ||
} | ||
} | ||
@@ -224,4 +297,9 @@ } | ||
console.log(formatter(reports), finalMessage || '') | ||
const fullReport = formatter(reports) + (finalMessage || '') | ||
console.log(fullReport) | ||
if (program.opts().save) { | ||
writeStringToFile(fullReport) | ||
} | ||
if (exitWithOne) process.exit(1) | ||
@@ -231,2 +309,25 @@ 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) { | ||
@@ -245,3 +346,12 @@ const formatterName = formatter || 'stylish' | ||
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') | ||
@@ -251,3 +361,2 @@ process.exit(1) | ||
const configPath = '.solhint.json' | ||
if (!fs.existsSync(configPath)) { | ||
@@ -283,2 +392,25 @@ console.log('Error!! Configuration does not exists') | ||
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() |
@@ -122,2 +122,8 @@ const VAR_DECLARATIONS = { | ||
fixedArrayCastInDeclaration: { | ||
code: 'uint[] public arr = [uint(1),2,3];', | ||
errorsImplicit: 0, | ||
errorsExplicit: 2, | ||
}, | ||
fixedArrayOfArrays: { | ||
@@ -134,4 +140,40 @@ code: 'uint256[] public arr = [[1,2,3]];', | ||
}, | ||
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 |
@@ -36,2 +36,38 @@ const ONE_CONTRACT = ` | ||
` | ||
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, | ||
} |
@@ -38,3 +38,3 @@ const assert = require('assert') | ||
const code = funcWith(` | ||
console.logString('test'); | ||
console.logBytes12('test'); | ||
`) | ||
@@ -41,0 +41,0 @@ |
@@ -16,2 +16,22 @@ const { assertNoWarnings, assertErrorMessage, assertErrorCount } = require('../../common/asserts') | ||
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', () => { | ||
@@ -38,2 +58,13 @@ const code = contracts.TWO_CONTRACTS | ||
}) | ||
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!') | ||
}) | ||
}) |
@@ -7,43 +7,49 @@ const assert = require('assert') | ||
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 {}'), | ||
@@ -54,10 +60,10 @@ // don't warn for constructors | ||
// 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) {}'), | ||
] | ||
@@ -76,3 +82,3 @@ | ||
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, { | ||
@@ -97,3 +103,3 @@ rules: { 'private-vars-leading-underscore': 'error' }, | ||
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, { | ||
@@ -100,0 +106,0 @@ rules: { 'private-vars-leading-underscore': ['error', { strict: true }] }, |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Dynamic require
Supply chain riskDynamic require can indicate the package is performing dangerous or unsafe dynamic code execution.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
375235
10987
261
19
12
+ Addedlatest-version@^7.0.0
+ Added@pnpm/config.env-replace@1.1.0(transitive)
+ Added@pnpm/network.ca-file@1.0.2(transitive)
+ Added@pnpm/npm-conf@2.3.1(transitive)
+ Added@sindresorhus/is@5.6.0(transitive)
+ Added@szmarczak/http-timer@5.0.1(transitive)
+ Added@types/http-cache-semantics@4.0.4(transitive)
+ Addedcacheable-lookup@7.0.0(transitive)
+ Addedcacheable-request@10.2.14(transitive)
+ Addedconfig-chain@1.1.13(transitive)
+ Addeddecompress-response@6.0.0(transitive)
+ Addeddeep-extend@0.6.0(transitive)
+ Addeddefer-to-connect@2.0.1(transitive)
+ Addedform-data-encoder@2.1.4(transitive)
+ Addedget-stream@6.0.1(transitive)
+ Addedgot@12.6.1(transitive)
+ Addedgraceful-fs@4.2.10(transitive)
+ Addedhttp-cache-semantics@4.1.1(transitive)
+ Addedhttp2-wrapper@2.2.1(transitive)
+ Addedini@1.3.8(transitive)
+ Addedjson-buffer@3.0.1(transitive)
+ Addedkeyv@4.5.4(transitive)
+ Addedlatest-version@7.0.0(transitive)
+ Addedlowercase-keys@3.0.0(transitive)
+ Addedmimic-response@3.1.04.0.0(transitive)
+ Addedminimist@1.2.8(transitive)
+ Addednormalize-url@8.0.1(transitive)
+ Addedp-cancelable@3.0.0(transitive)
+ Addedpackage-json@8.1.1(transitive)
+ Addedproto-list@1.2.4(transitive)
+ Addedquick-lru@5.1.1(transitive)
+ Addedrc@1.2.8(transitive)
+ Addedregistry-auth-token@5.0.2(transitive)
+ Addedregistry-url@6.0.1(transitive)
+ Addedresolve-alpn@1.2.1(transitive)
+ Addedresponselike@3.0.0(transitive)
+ Addedstrip-json-comments@2.0.1(transitive)