diff --git a/docs/rules/naming/event-name-camelcase.md b/docs/rules/naming/event-name-camelcase.md index 70207821..a6c00b63 100644 --- a/docs/rules/naming/event-name-camelcase.md +++ b/docs/rules/naming/event-name-camelcase.md @@ -26,6 +26,9 @@ This rule accepts a string option of rule severity. Must be one of "error", "war } ``` +### Notes +- Solhint allows this rule to automatically fix the code with `--fix` option +- The FIX will only change first letter and remove underscores ## Examples This rule does not have examples. diff --git a/e2e/08-autofix/event-name-camelcase/.solhint.json b/e2e/08-autofix/event-name-camelcase/.solhint.json new file mode 100644 index 00000000..7f809bb3 --- /dev/null +++ b/e2e/08-autofix/event-name-camelcase/.solhint.json @@ -0,0 +1,5 @@ +{ + "rules": { + "event-name-camelcase": "error" + } +} diff --git a/e2e/08-autofix/event-name-camelcase/Foo1.sol b/e2e/08-autofix/event-name-camelcase/Foo1.sol new file mode 100644 index 00000000..8a1b5cca --- /dev/null +++ b/e2e/08-autofix/event-name-camelcase/Foo1.sol @@ -0,0 +1,33 @@ +// SPDX-License-Identifier: Apache-2.0 +pragma solidity ^0.8.0; + +contract Generic { + + event not_UsedEvent1(uint256 indexed amount, address account); + + event __not_UsedEvent___2(uint256 indexed amount, address indexed account); + + event not___UsedEvent3(uint256 amount, address account); + + event notUsedEvent4(uint256 indexed amount, address indexed account); + + event notUsedEvent5____(uint256 indexed amount, address account); + + event __not_UsedEvent6__(uint256 indexed amount, address account); + + event OkEvent1(uint256 indexed amount, address account); + + event OkEventOk2(uint256 indexed amount, address indexed account); + + constructor() {} + + function function1() external pure { + uint af1 = 0; + af1; + } + + function function2() external pure { + uint b; + b; + } +} diff --git a/e2e/08-autofix/event-name-camelcase/Foo1AfterFix.sol b/e2e/08-autofix/event-name-camelcase/Foo1AfterFix.sol new file mode 100644 index 00000000..8d6820c2 --- /dev/null +++ b/e2e/08-autofix/event-name-camelcase/Foo1AfterFix.sol @@ -0,0 +1,33 @@ +// SPDX-License-Identifier: Apache-2.0 +pragma solidity ^0.8.0; + +contract Generic { + + event NotUsedEvent1(uint256 indexed amount, address account); + + event NotUsedEvent2(uint256 indexed amount, address indexed account); + + event NotUsedEvent3(uint256 amount, address account); + + event NotUsedEvent4(uint256 indexed amount, address indexed account); + + event NotUsedEvent5(uint256 indexed amount, address account); + + event NotUsedEvent6(uint256 indexed amount, address account); + + event OkEvent1(uint256 indexed amount, address account); + + event OkEventOk2(uint256 indexed amount, address indexed account); + + constructor() {} + + function function1() external pure { + uint af1 = 0; + af1; + } + + function function2() external pure { + uint b; + b; + } +} diff --git a/e2e/08-autofix/event-name-camelcase/Foo1BeforeFix.sol b/e2e/08-autofix/event-name-camelcase/Foo1BeforeFix.sol new file mode 100644 index 00000000..8a1b5cca --- /dev/null +++ b/e2e/08-autofix/event-name-camelcase/Foo1BeforeFix.sol @@ -0,0 +1,33 @@ +// SPDX-License-Identifier: Apache-2.0 +pragma solidity ^0.8.0; + +contract Generic { + + event not_UsedEvent1(uint256 indexed amount, address account); + + event __not_UsedEvent___2(uint256 indexed amount, address indexed account); + + event not___UsedEvent3(uint256 amount, address account); + + event notUsedEvent4(uint256 indexed amount, address indexed account); + + event notUsedEvent5____(uint256 indexed amount, address account); + + event __not_UsedEvent6__(uint256 indexed amount, address account); + + event OkEvent1(uint256 indexed amount, address account); + + event OkEventOk2(uint256 indexed amount, address indexed account); + + constructor() {} + + function function1() external pure { + uint af1 = 0; + af1; + } + + function function2() external pure { + uint b; + b; + } +} diff --git a/e2e/autofix-test.js b/e2e/autofix-test.js index b2c3686a..ae2c82d9 100644 --- a/e2e/autofix-test.js +++ b/e2e/autofix-test.js @@ -418,7 +418,7 @@ describe('e2e', function () { }) }) - describe('autofix rule: contract-name-camelcase', () => { + describe('autofix rule: avoid-suicide', () => { before(function () { params = retrieveParams('avoid-suicide/') currentConfig = `${params.path}${params.subpath}.solhint.json` @@ -464,7 +464,7 @@ describe('e2e', function () { }) }) - describe('autofix rule: avoid-suicide', () => { + describe('autofix rule: contract-name-camelcase', () => { before(function () { params = retrieveParams('contract-name-camelcase/') currentConfig = `${params.path}${params.subpath}.solhint.json` @@ -509,6 +509,52 @@ describe('e2e', function () { expect(result).to.be.true }) }) + + describe('autofix rule: event-name-camelcase', () => { + before(function () { + params = retrieveParams('event-name-camelcase/') + 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` + }) + after(function () { + if (!E2E) { + copyFile(beforeFixFile, currentFile) + } + }) + + describe('--fix with noPrompt', () => { + it('should compare Foo1 file with template BEFORE FIX file and they should match (8)', () => { + result = compareTextFiles(currentFile, beforeFixFile) + expect(result).to.be.true + }) + + it('should execute and compare Foo1 with template AFTER FIX and they should match (8)', () => { + ;({ code, stdout } = shell.exec( + `${params.command} ${params.param1} -c ${currentConfig} ${currentFile} --fix --disc --noPrompt` + )) + + result = compareTextFiles(currentFile, afterFixFile) + expect(result).to.be.true + }) + + it('should execute and exit with code 1 (8)', () => { + expect(code).to.equal(1) + }) + + it('should get the right report (8)', () => { + const reportLines = stdout.split('\n') + const finalLine = '6 problems (6 errors, 0 warnings)' + expect(reportLines[reportLines.length - 3]).to.contain(finalLine) + }) + }) + + it('should check FOO1 does not change after test (8)', () => { + result = compareTextFiles(currentFile, beforeFixFile) + expect(result).to.be.true + }) + }) }) // FALTA LA PRUEBA DEL STORE TO FILE diff --git a/lib/rules/naming/event-name-camelcase.js b/lib/rules/naming/event-name-camelcase.js index 5bcbbb1c..aecfca95 100644 --- a/lib/rules/naming/event-name-camelcase.js +++ b/lib/rules/naming/event-name-camelcase.js @@ -8,12 +8,20 @@ const meta = { docs: { description: 'Event name must be in CamelCase.', category: 'Style Guide Rules', + notes: [ + { + note: 'Solhint allows this rule to automatically fix the code with `--fix` option', + }, + { + note: 'The FIX will only change first letter and remove underscores', + }, + ], }, isDefault: false, recommended: true, defaultSetup: 'warn', - + fixable: true, schema: null, } @@ -22,9 +30,31 @@ class EventNameCamelcaseChecker extends BaseChecker { super(reporter, ruleId, meta) } + fixStatement(node) { + // Remove leading and trailing underscores + let nameToPut = node.name.replace(/^[_]+|[_]+$/g, '') + + // Replace '-' with space and split the string into an array + let words = nameToPut.replace(/-/g, ' ').split('_') + + // Capitalize the first letter of each word + words = words.map((word) => word.charAt(0).toUpperCase() + word.slice(1)) + + // Join the words back into a single string + nameToPut = words.join('') + + const originalNameLength = node.name.length + const typeLength = 'event'.length + + const rangeStart = node.range[0] + typeLength + 1 + const rangeEnd = node.range[0] + typeLength + originalNameLength + + return (fixer) => fixer.replaceTextRange([rangeStart, rangeEnd], nameToPut) + } + EventDefinition(node) { if (naming.isNotCamelCase(node.name)) { - this.error(node, 'Event name must be in CamelCase') + this.error(node, 'Event name must be in CamelCase', this.fixStatement(node)) } } }