Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Autofix contract name camelcase #532

Merged
merged 3 commits into from
Jan 1, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion docs/rules.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ title: "Rule Index of Solhint"
| Rule Id | Error | Recommended | Deprecated |
| ------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------- | ------------ | ----------- |
| [const-name-snakecase](./rules/naming/const-name-snakecase.md) | Constant name must be in capitalized SNAKE_CASE. (Does not check IMMUTABLES, use immutable-vars-naming) | $~~~~~~~~$✔️ | |
| [contract-name-camelcase](./rules/naming/contract-name-camelcase.md) | Contract name must be in CamelCase. | $~~~~~~~~$✔️ | |
| [contract-name-camelcase](./rules/naming/contract-name-camelcase.md) | Contract, Structs and Enums should be in CamelCase. | $~~~~~~~~$✔️ | |
| [event-name-camelcase](./rules/naming/event-name-camelcase.md) | Event name must be in CamelCase. | $~~~~~~~~$✔️ | |
| [foundry-test-functions](./rules/naming/foundry-test-functions.md) | Enforce naming convention on functions for Foundry test cases | | |
| [func-name-mixedcase](./rules/naming/func-name-mixedcase.md) | Function name must be in mixedCase. | $~~~~~~~~$✔️ | |
Expand Down
5 changes: 4 additions & 1 deletion docs/rules/naming/contract-name-camelcase.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ title: "contract-name-camelcase | Solhint"


## Description
Contract name must be in CamelCase.
Contract, Structs and Enums should be in CamelCase.

## Options
This rule accepts a string option of rule severity. Must be one of "error", "warn", "off". Default to warn.
Expand All @@ -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.
Expand Down
5 changes: 5 additions & 0 deletions e2e/08-autofix/contract-name-camelcase/.solhint.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"rules": {
"contract-name-camelcase": "error"
}
}
61 changes: 61 additions & 0 deletions e2e/08-autofix/contract-name-camelcase/Foo1.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.8.0;

struct not_Used_Struct1 {
uint256 three;
uint256 four;
}

enum __status1_ {
Pending,
Approved,
Rejected
}

struct NotUsedStruct2 {
uint256 three;
uint256 four;
}

enum Status2 {
Pending,
Approved,
Rejected
}

contract __generic {

struct not_UsedStruct3__ {
uint256 three;
uint256 four;
}

enum status3 {
Pending,
Approved,
Rejected
}

struct NotUsedStruct4 {
uint256 three;
uint256 four;
}

enum Status4 {
Pending,
Approved,
Rejected
}

constructor() {}

function function1() pure external {
uint af1 = 0;
af1;
}

function function2() external pure {
uint b;
b;
}
}
61 changes: 61 additions & 0 deletions e2e/08-autofix/contract-name-camelcase/Foo1AfterFix.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.8.0;

struct NotUsedStruct1 {
uint256 three;
uint256 four;
}

enum Status1 {
Pending,
Approved,
Rejected
}

struct NotUsedStruct2 {
uint256 three;
uint256 four;
}

enum Status2 {
Pending,
Approved,
Rejected
}

contract Generic {

struct NotUsedStruct3 {
uint256 three;
uint256 four;
}

enum Status3 {
Pending,
Approved,
Rejected
}

struct NotUsedStruct4 {
uint256 three;
uint256 four;
}

enum Status4 {
Pending,
Approved,
Rejected
}

constructor() {}

function function1() pure external {
uint af1 = 0;
af1;
}

function function2() external pure {
uint b;
b;
}
}
61 changes: 61 additions & 0 deletions e2e/08-autofix/contract-name-camelcase/Foo1BeforeFix.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.8.0;

struct not_Used_Struct1 {
uint256 three;
uint256 four;
}

enum __status1_ {
Pending,
Approved,
Rejected
}

struct NotUsedStruct2 {
uint256 three;
uint256 four;
}

enum Status2 {
Pending,
Approved,
Rejected
}

contract __generic {

struct not_UsedStruct3__ {
uint256 three;
uint256 four;
}

enum status3 {
Pending,
Approved,
Rejected
}

struct NotUsedStruct4 {
uint256 three;
uint256 four;
}

enum Status4 {
Pending,
Approved,
Rejected
}

constructor() {}

function function1() pure external {
uint af1 = 0;
af1;
}

function function2() external pure {
uint b;
b;
}
}
48 changes: 47 additions & 1 deletion e2e/autofix-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -418,7 +418,7 @@ describe('e2e', function () {
})
})

describe('autofix rule: avoid-suicide', () => {
describe('autofix rule: contract-name-camelcase', () => {
before(function () {
params = retrieveParams('avoid-suicide/')
currentConfig = `${params.path}${params.subpath}.solhint.json`
Expand Down Expand Up @@ -463,6 +463,52 @@ describe('e2e', function () {
expect(result).to.be.true
})
})

describe('autofix rule: avoid-suicide', () => {
before(function () {
params = retrieveParams('contract-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 = '5 problems (5 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
52 changes: 43 additions & 9 deletions lib/rules/naming/contract-name-camelcase.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,22 @@ const meta = {
type: 'naming',

docs: {
description: 'Contract name must be in CamelCase.',
description: 'Contract, Structs and Enums should 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,
}

Expand All @@ -23,25 +31,51 @@ class ContractNameCamelcaseChecker extends BaseChecker {
}

ContractDefinition(node) {
this.validateName(node)
this.validateName(node, 'contract')
}

EnumDefinition(node) {
this.validateName(node)
this.validateName(node, 'enum')
}

StructDefinition(node) {
this.validateName(node)
this.validateName(node, 'struct')
}

validateName(node) {
validateName(node, type) {
if (naming.isNotCamelCase(node.name)) {
this._error(node)
this._error(node, type)
}
}

_error(node) {
this.error(node, 'Contract name must be in CamelCase')
fixStatement(node, type) {
// 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 = type.length

const rangeStart = node.range[0] + typeLength + 1
const rangeEnd = node.range[0] + typeLength + originalNameLength

return (fixer) => fixer.replaceTextRange([rangeStart, rangeEnd], nameToPut)
}

_error(node, type) {
this.error(
node,
'Contract, Structs and Enums should be in CamelCase',
this.fixStatement(node, type)
)
}
}

Expand Down
Loading