11const assert = require ( 'assert' )
22const linter = require ( '../../../lib/index' )
3+ const { multiLine } = require ( '../../common/contract-builder' )
34const contractWith = require ( '../../common/contract-builder' ) . contractWith
5+ const { assertErrorCount, assertNoErrors } = require ( '../../common/asserts' )
46
57describe ( 'Linter - func-name-mixedcase' , ( ) => {
68 it ( 'should raise incorrect func name error' , ( ) => {
@@ -10,7 +12,7 @@ describe('Linter - func-name-mixedcase', () => {
1012 rules : { 'func-name-mixedcase' : 'error' } ,
1113 } )
1214
13- assert . equal ( report . errorCount , 1 )
15+ assertErrorCount ( report , 1 )
1416 assert . ok ( report . messages [ 0 ] . message . includes ( 'mixedCase' ) )
1517 } )
1618
@@ -21,7 +23,7 @@ describe('Linter - func-name-mixedcase', () => {
2123 rules : { 'func-name-mixedcase' : 'error' } ,
2224 } )
2325
24- assert . equal ( report . errorCount , 0 )
26+ assertNoErrors ( report )
2527 } )
2628
2729 describe ( 'Function names with $ character' , ( ) => {
@@ -38,8 +40,235 @@ describe('Linter - func-name-mixedcase', () => {
3840 rules : { 'func-name-mixedcase' : 'error' } ,
3941 } )
4042
41- assert . equal ( report . errorCount , 0 )
43+ assertNoErrors ( report )
4244 } )
4345 }
4446 } )
47+
48+ describe ( 'Interface function names representing a constant' , ( ) => {
49+ it ( 'should NOT raise mixedCase name error' , ( ) => {
50+ const code = multiLine (
51+ '// SPDX-License-Identifier: Apache-2.0' ,
52+ 'pragma solidity ^0.8.0;' ,
53+ '' ,
54+ 'interface IA {' ,
55+ '/// @dev This is a constant state variable' ,
56+ 'function START_TIME() external view returns (uint256);' ,
57+ '}' ,
58+ '' ,
59+ 'contract A is IA {' ,
60+ ' /// @inheritdoc IA' ,
61+ ' uint256 public constant override START_TIME = 1;' ,
62+ '}'
63+ )
64+
65+ const report = linter . processStr ( code , {
66+ rules : { 'func-name-mixedcase' : 'error' } ,
67+ } )
68+
69+ assertNoErrors ( report )
70+ } )
71+
72+ it ( 'should fail when inside interface but returning multiple unnamed values' , ( ) => {
73+ const code = multiLine (
74+ 'pragma solidity ^0.8.20;' ,
75+ 'interface IBad {' ,
76+ ' function START_TIME() external view returns (uint256, uint256);' ,
77+ '}'
78+ )
79+
80+ const report = linter . processStr ( code , { rules : { 'func-name-mixedcase' : 'error' } } )
81+
82+ assertErrorCount ( report , 1 )
83+ assert . ok ( report . messages [ 0 ] . message . includes ( 'mixedCase' ) )
84+ } )
85+
86+ it ( 'should fail when SNAKE_CASE in interface is missing `view` (not a getter-like signature)' , ( ) => {
87+ const code = multiLine (
88+ 'pragma solidity ^0.8.20;' ,
89+ 'interface IBad {' ,
90+ ' function START_TIME() external returns (uint256);' ,
91+ '}'
92+ )
93+
94+ const report = linter . processStr ( code , { rules : { 'func-name-mixedcase' : 'error' } } )
95+
96+ assertErrorCount ( report , 1 )
97+ assert . ok ( report . messages [ 0 ] . message . includes ( 'mixedCase' ) )
98+ } )
99+
100+ it ( 'should fail when SNAKE_CASE interface function has parameters (not a getter-like signature)' , ( ) => {
101+ const code = multiLine (
102+ 'pragma solidity ^0.8.20;' ,
103+ 'interface IBad {' ,
104+ ' function START_TIME(address who) external view returns (uint256);' ,
105+ '}'
106+ )
107+
108+ const report = linter . processStr ( code , { rules : { 'func-name-mixedcase' : 'error' } } )
109+
110+ assertErrorCount ( report , 1 )
111+ assert . ok ( report . messages [ 0 ] . message . includes ( 'mixedCase' ) )
112+ } )
113+
114+ it ( 'should fail when SNAKE_CASE in non-interface contract functions' , ( ) => {
115+ const code = contractWith ( 'function START_TIME() public view returns (uint256) { return 1; }' )
116+
117+ const report = linter . processStr ( code , { rules : { 'func-name-mixedcase' : 'error' } } )
118+
119+ assertErrorCount ( report , 1 )
120+ assert . ok ( report . messages [ 0 ] . message . includes ( 'mixedCase' ) )
121+ } )
122+
123+ it ( 'should fail when inside interface but returning multiple named values' , ( ) => {
124+ const code = multiLine (
125+ 'pragma solidity ^0.8.20;' ,
126+ 'interface IBad {' ,
127+ ' function START_TIME() external view returns (uint256 a, uint256 b);' ,
128+ '}'
129+ )
130+
131+ const report = linter . processStr ( code , { rules : { 'func-name-mixedcase' : 'error' } } )
132+
133+ assertErrorCount ( report , 1 )
134+ assert . ok ( report . messages [ 0 ] . message . includes ( 'mixedCase' ) )
135+ } )
136+
137+ it ( 'allows $ in mixedCase names (regression of former tests)' , ( ) => {
138+ const code = contractWith ( 'function aFunc$1Nam23e () public {}' )
139+
140+ const report = linter . processStr ( code , { rules : { 'func-name-mixedcase' : 'error' } } )
141+
142+ assertNoErrors ( report )
143+ } )
144+
145+ it ( 'allows IERC20 as return (contract/interface type)' , ( ) => {
146+ const code = multiLine (
147+ 'pragma solidity ^0.8.20;' ,
148+ 'interface IERC20 { function totalSupply() external view returns (uint256); }' ,
149+ 'interface I {' ,
150+ ' function TOKEN() external view returns (IERC20);' ,
151+ '}'
152+ )
153+ const report = linter . processStr ( code , { rules : { 'func-name-mixedcase' : 'error' } } )
154+
155+ assertNoErrors ( report )
156+ } )
157+
158+ it ( 'allows UDVT like UD60x18 as return' , ( ) => {
159+ const code = multiLine (
160+ 'pragma solidity ^0.8.20;' ,
161+ 'type UD60x18 is uint256;' ,
162+ 'interface I {' ,
163+ ' function UNLOCK_PERCENTAGE() external view returns (UD60x18);' ,
164+ '}'
165+ )
166+ const report = linter . processStr ( code , { rules : { 'func-name-mixedcase' : 'error' } } )
167+
168+ assertNoErrors ( report )
169+ } )
170+
171+ it ( 'allows enum as return' , ( ) => {
172+ const code = multiLine (
173+ 'pragma solidity ^0.8.20;' ,
174+ 'enum Status { Init, Live, Done }' ,
175+ 'interface I {' ,
176+ ' function CURRENT_STATUS() external view returns (Status);' ,
177+ '}'
178+ )
179+ const report = linter . processStr ( code , { rules : { 'func-name-mixedcase' : 'error' } } )
180+
181+ assertNoErrors ( report )
182+ } )
183+
184+ it ( 'rejects struct as return (UserDefinedTypeName with storageLocation)' , ( ) => {
185+ const code = multiLine (
186+ 'pragma solidity ^0.8.20;' ,
187+ 'struct Info { uint256 a; uint256 b; }' ,
188+ 'interface I {' ,
189+ ' function DATA() external view returns (Info memory);' ,
190+ '}'
191+ )
192+ const report = linter . processStr ( code , { rules : { 'func-name-mixedcase' : 'error' } } )
193+
194+ assertErrorCount ( report , 1 )
195+ assert . ok ( report . messages [ 0 ] . message . includes ( 'mixedCase' ) )
196+ } )
197+
198+ it ( 'rejects array as return' , ( ) => {
199+ const code = multiLine (
200+ 'pragma solidity ^0.8.20;' ,
201+ 'interface I {' ,
202+ ' function VALUES() external view returns (uint256[] memory);' ,
203+ '}'
204+ )
205+ const report = linter . processStr ( code , { rules : { 'func-name-mixedcase' : 'error' } } )
206+ assertErrorCount ( report , 1 )
207+ assert . ok ( report . messages [ 0 ] . message . includes ( 'mixedCase' ) )
208+ } )
209+
210+ it ( 'rejects multiple returns (tuple)' , ( ) => {
211+ const code = multiLine (
212+ 'pragma solidity ^0.8.20;' ,
213+ 'interface I {' ,
214+ ' function START_TIME() external view returns (uint256, uint256);' ,
215+ '}'
216+ )
217+ const report = linter . processStr ( code , { rules : { 'func-name-mixedcase' : 'error' } } )
218+ assertErrorCount ( report , 1 )
219+ assert . ok ( report . messages [ 0 ] . message . includes ( 'mixedCase' ) )
220+ } )
221+
222+ it ( 'rejects pure (only view allowed for constant/immutable getters)' , ( ) => {
223+ const code = multiLine (
224+ 'pragma solidity ^0.8.20;' ,
225+ 'interface I {' ,
226+ ' function MAGIC_NUMBER() external pure returns (uint256);' ,
227+ '}'
228+ )
229+ const report = linter . processStr ( code , { rules : { 'func-name-mixedcase' : 'error' } } )
230+ assertErrorCount ( report , 1 )
231+ assert . ok ( report . messages [ 0 ] . message . includes ( 'mixedCase' ) )
232+ } )
233+
234+ it ( 'still allows mixedCase with $ (regression)' , ( ) => {
235+ const code = multiLine (
236+ 'pragma solidity ^0.8.20;' ,
237+ 'interface I {' ,
238+ ' function aFunc$1Nam23e() external view returns (uint256);' ,
239+ '}'
240+ )
241+ const report = linter . processStr ( code , { rules : { 'func-name-mixedcase' : 'error' } } )
242+
243+ assertNoErrors ( report )
244+ } )
245+
246+ it ( 'allows string as return (elementary type)' , ( ) => {
247+ const code = multiLine (
248+ 'pragma solidity ^0.8.20;' ,
249+ 'interface I {' ,
250+ ' function NAME() external view returns (string memory);' ,
251+ '}'
252+ )
253+ const report = linter . processStr ( code , { rules : { 'func-name-mixedcase' : 'error' } } )
254+
255+ assertNoErrors ( report )
256+ } )
257+
258+ it ( 'rejects IERC20 return when declared in a non-interface contract' , ( ) => {
259+ const code = multiLine (
260+ 'pragma solidity ^0.8.20;' ,
261+ 'interface IERC20 { function totalSupply() external view returns (uint256); }' ,
262+ 'contract C {' ,
263+ ' function TOKEN() external view returns (IERC20) {' ,
264+ ' return IERC20(address(0));' ,
265+ ' }' ,
266+ '}'
267+ )
268+ const report = linter . processStr ( code , { rules : { 'func-name-mixedcase' : 'error' } } )
269+ assertErrorCount ( report , 1 )
270+
271+ assert . ok ( report . messages [ 0 ] . message . includes ( 'mixedCase' ) )
272+ } )
273+ } )
45274} )
0 commit comments