Repository: protofire/solhint
Pull request #474 (2023-08-07 20:30:02)

Fix: [ordering] rule - Treat initializer as same weight as constructor


Patch:
diff --git a/lib/rules/order/ordering.js b/lib/rules/order/ordering.js
index 494f3ad..6d0e8a0 100644
--- a/lib/rules/order/ordering.js
+++ b/lib/rules/order/ordering.js
@@ -122,6 +122,13 @@ function sourceUnitPartOrder(node) {
   throw new Error('Unrecognized source unit part, please report this issue')
 }
 
+function isInitializerModifier(modifiers, targetName, targetArguments) {
+  // search the modifiers array with the name === 'initializer'
+  return modifiers.some(
+    (modifier) => modifier.name === targetName && modifier.arguments === targetArguments
+  )
+}
+
 function contractPartOrder(node) {
   if (node.type === 'UsingForDeclaration') {
     return [0, 'using for declaration']
@@ -154,8 +161,13 @@ function contractPartOrder(node) {
     return [40, 'modifier definition']
   }
 
-  if (node.isConstructor) {
-    return [50, 'constructor']
+  if (
+    node.isConstructor ||
+    (node.type === 'FunctionDefinition' &&
+      node.name === 'initialize' &&
+      isInitializerModifier(node.modifiers, 'initializer', null))
+  ) {
+    return [50, 'constructor/initializer']
   }
 
   if (isReceiveFunction(node)) {


Test patch:
diff --git a/test/rules/order/ordering.js b/test/rules/order/ordering.js
index d83d73f..1ff356e 100644
--- a/test/rules/order/ordering.js
+++ b/test/rules/order/ordering.js
@@ -427,4 +427,63 @@ describe('Linter - ordering', () => {
 
     assert.equal(report.errorCount, 0)
   })
+
+  it('should raise error when INITIALIZER is NOT well located', () => {
+    const code = `
+      // SPDX-License-Identifier: MIT
+      pragma solidity ^0.8.0;
+      import "@openzeppelin/contracts/ownership/Ownable.sol";
+      uint256 constant oneNiceConstant = 1;
+      struct OneNiceStruct { uint256 a; uint256 b; }            
+      enum MyEnum { A, B }
+      error Unauthorized();
+      function freeFunction() pure returns (uint256) {
+        return 1;
+      }     
+      contract OneNiceContract {
+        function initialize() initializer {
+          oneNiceConstant;
+        }
+        struct OneNiceStruct { uint256 a; uint256 b; }            
+      }
+    `
+
+    const report = linter.processStr(code, {
+      rules: { ordering: 'error' },
+    })
+
+    assert.equal(report.errorCount, 1)
+    assert.ok(
+      report.messages[0].message.includes(
+        'Function order is incorrect, struct definition can not go after constructor/initializer'
+      )
+    )
+  })
+
+  it('should NOT raise error when INITIALIZER is well located', () => {
+    const code = `
+      // SPDX-License-Identifier: MIT
+      pragma solidity ^0.8.0;
+      import "@openzeppelin/contracts/ownership/Ownable.sol";
+      uint256 constant oneNiceConstant = 1;
+      enum MyEnum { A, B }
+      struct OneNiceStruct { uint256 a; uint256 b; }            
+      error Unauthorized();
+            function freeFunction() pure returns (uint256) {
+        return 1;
+      }     
+      contract OneNiceContract {
+        struct OneNiceStruct { uint256 a; uint256 b; }            
+        function initialize() initializer {
+          oneNiceConstant;
+        }
+      }
+    `
+
+    const report = linter.processStr(code, {
+      rules: { ordering: 'error' },
+    })
+
+    assert.equal(report.errorCount, 0)
+  })
 })