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

Added autofix function for imports-on-top rule + Tests for this feature #156

Merged
merged 7 commits into from
Jan 2, 2018
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
55 changes: 43 additions & 12 deletions lib/rules/imports-on-top.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@

"use strict";

// Get platform specific newline character for applying fixes
const eol = require("os").EOL;


module.exports = {

Expand All @@ -16,42 +19,70 @@ module.exports = {
description: "Ensure that all import statements are on top of the file"
},

fixable: "code",

schema: []

},

create: function(context) {

/**
* If ImportStatement node is a direct child of Program node 'parent',
* then it is an element of parent.body array.
* The node should precede any other type of node in the array except for the pragma & pragma experimental directives
* and other import statements.
*/
* If ImportStatement node is a direct child of Program node 'parent',
* then it is an element of parent.body array.
* The node should precede any other type of node in the array except for the pragma & pragma experimental directives
* and other import statements.
* The way the fix() algorithm currently works - it only fixes position of 1 import statement in a file in 1 iteration.
* This may be improved in future.
*/
function inspectImportStatement(emitted) {
if (emitted.exit) {
return;
}

const {node} = emitted,
programNode = context.getSourceCode().getParent(node),
indexOfNode = programNode.body.indexOf(node),
nodesAllowedAbove = ["ExperimentalPragmaStatement", "PragmaStatement", "ImportStatement"];

for (let childNode of programNode.body) {
// If we've reached this import while traversing body, it means its position is fine.
if (node.start === childNode.start) {
return;
}
let lastValidNode = programNode.body[0]; // the first node could very well be an unacceptable one, taking care of it below

// For every node preceding the import node check if it's one of the allowed types.
for (let childNode of programNode.body.slice(0, indexOfNode)) {

// The moment we find 1 node not allowed above import, report and exit.
// TODO: write fix() for this issue:
// - Remove import from current position
// - Place it right before childNode.start
if (nodesAllowedAbove.indexOf(childNode.type) < 0) {
// - The fix will place it right after the last valid import node
if (!nodesAllowedAbove.includes(childNode.type)) {
return context.report({
node,
fix(fixer) {
// Add the import statement after the last valid node and remove the import statement
const sourceCode = context.getSourceCode();
const importStatement = sourceCode.getText(node);
let suffix;

// If the last valid node is import, we place the current import right below it.
// If its a pragma directive (whether solidity or experimental), add current import after 3 EOLs
if (lastValidNode.type === "ImportStatement") {
suffix = eol;
} else if (nodesAllowedAbove.includes(lastValidNode.type)) {
// Because we've already explicitly checks for import st. above, this one basically
// checks for the remaining, ie, pragma directives
suffix = eol.repeat(3);
} else {
// If lastValidNode is not an allowed one, it means we have to insert the import ABOVE the lvn
return [fixer.insertTextBefore(lastValidNode, importStatement + eol), fixer.remove(node)];
}

return [fixer.insertTextAfter(lastValidNode, suffix + importStatement), fixer.remove(node)];
},
message: "Import Statement must precede everything except pragma directives."
});
} else {
// All nodes allowed above an import statement are considered valid nodes
lastValidNode = childNode;
}
}
}
Expand Down
10 changes: 10 additions & 0 deletions test/lib/rules/imports-on-top/fixes/before-pragma.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
pragma solidity ^0.4.0;


contract Halo {
function foo () returns (uint) {
return 0;
}
}

import "nano.sol";
19 changes: 19 additions & 0 deletions test/lib/rules/imports-on-top/fixes/only-one-error.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
pragma solidity ^0.4.0;


import * as symbolName from "filename";


contract Halo {
function foo () returns (uint) {
return 0;
}
}

import "nano.sol";

library Foo {
function bar () returns (uint) {
return 1;
}
}
Loading