Skip to content

Commit

Permalink
Re-modernize and cleanup with node 4-compatible features
Browse files Browse the repository at this point in the history
  • Loading branch information
Will Binns-Smith committed Jul 7, 2017
1 parent a611a27 commit 38bd88c
Show file tree
Hide file tree
Showing 2 changed files with 412 additions and 494 deletions.
92 changes: 46 additions & 46 deletions lib/rules/no-unused-state.js
Expand Up @@ -9,7 +9,7 @@

'use strict';

var Components = require('../util/Components');
const Components = require('../util/Components');

// Descend through all wrapping TypeCastExpressions and return the expression
// that was cast.
Expand Down Expand Up @@ -40,10 +40,10 @@ function isThisExpression(node) {
function getInitialClassInfo() {
return {
// Set of nodes where state fields were defined.
stateFields: [],
stateFields: new Set(),

// Set of names of state fields that we've seen used.
usedStateFields: [],
usedStateFields: new Set(),

// Names of local variables that may be pointing to this.state. To
// track this properly, we would need to keep track of all locals,
Expand All @@ -69,57 +69,57 @@ module.exports = {
// not yet encountered any use of this.state which we have chosen not to
// analyze. If we encounter any such usage (like this.state being spread as
// JSX attributes), then this is again set to null.
var classInfo = null;
let classInfo = null;

// Returns true if the given node is possibly a reference to `this.state`.
function isStateReference(node) {
node = uncast(node);

var isDirectStateReference =
const isDirectStateReference =
node.type === 'MemberExpression' &&
isThisExpression(node.object) &&
node.property.name === 'state';

var isAliasedStateReference =
const isAliasedStateReference =
node.type === 'Identifier' &&
classInfo.aliases &&
classInfo.aliases.indexOf(node.name) >= 0;
classInfo.aliases.has(node.name);

return isDirectStateReference || isAliasedStateReference;
}

// Takes an ObjectExpression node and adds all named Property nodes to the
// current set of state fields.
function addStateFields(node) {
node.properties.forEach(function(prop) {
for (const prop of node.properties) {
if (prop.type === 'Property' && getName(prop.key) !== null) {
classInfo.stateFields.push(prop);
classInfo.stateFields.add(prop);
}
});
}
}

// Adds the name of the given node as a used state field if the node is an
// Identifier or a Literal. Other node types are ignored.
function addUsedStateField(node) {
var name = getName(node);
const name = getName(node);
if (name) {
classInfo.usedStateFields.push(name);
classInfo.usedStateFields.add(name);
}
}

// Records used state fields and new aliases for an ObjectPattern which
// destructures `this.state`.
function handleStateDestructuring(node) {
node.properties.forEach(function(prop) {
for (const prop of node.properties) {
if (prop.type === 'Property') {
addUsedStateField(prop.key);
} else if (
prop.type === 'ExperimentalRestProperty' &&
classInfo.aliases
) {
classInfo.aliases.push(getName(prop.argument));
classInfo.aliases.add(getName(prop.argument));
}
});
}
}

// Used to record used state fields and new aliases for both
Expand All @@ -128,54 +128,54 @@ module.exports = {
switch (left.type) {
case 'Identifier':
if (isStateReference(right) && classInfo.aliases) {
classInfo.aliases.push(left.name);
classInfo.aliases.add(left.name);
}
break;
case 'ObjectPattern':
if (isStateReference(right)) {
handleStateDestructuring(left);
} else if (isThisExpression(right) && classInfo.aliases) {
left.properties.forEach(function(prop) {
for (const prop of left.properties) {
if (prop.type === 'Property' && getName(prop.key) === 'state') {
var name = getName(prop.value);
const name = getName(prop.value);
if (name) {
classInfo.aliases.push(name);
classInfo.aliases.add(name);
} else if (prop.value.type === 'ObjectPattern') {
handleStateDestructuring(prop.value);
}
}
});
}
}
break;
default:
// pass
// pass
}
}

function reportUnusedFields() {
// Report all unused state fields.
classInfo.stateFields.forEach(function(node) {
var name = getName(node.key);
if (classInfo.usedStateFields.indexOf(name) < 0) {
context.report(node, 'Unused state field: \'' + name + '\'');
for (const node of classInfo.stateFields) {
const name = getName(node.key);
if (!classInfo.usedStateFields.has(name)) {
context.report(node, `Unused state field: '${name}'`);
}
});
}
}

return {
ClassDeclaration: function(node) {
ClassDeclaration(node) {
if (utils.isES6Component(node)) {
classInfo = getInitialClassInfo();
}
},

ObjectExpression: function(node) {
ObjectExpression(node) {
if (utils.isES5Component(node)) {
classInfo = getInitialClassInfo();
}
},

'ObjectExpression:exit': function(node) {
'ObjectExpression:exit'(node) {
if (!classInfo) {
return;
}
Expand All @@ -186,15 +186,15 @@ module.exports = {
}
},

'ClassDeclaration:exit': function() {
'ClassDeclaration:exit'() {
if (!classInfo) {
return;
}
reportUnusedFields();
classInfo = null;
},

CallExpression: function(node) {
CallExpression(node) {
if (!classInfo) {
return;
}
Expand All @@ -211,7 +211,7 @@ module.exports = {
}
},

ClassProperty: function(node) {
ClassProperty(node) {
if (!classInfo) {
return;
}
Expand All @@ -227,35 +227,35 @@ module.exports = {
}
},

MethodDefinition: function() {
MethodDefinition() {
if (!classInfo) {
return;
}
// Create a new set for this.state aliases local to this method.
classInfo.aliases = [];
classInfo.aliases = new Set();
},

'MethodDefinition:exit': function() {
'MethodDefinition:exit'() {
if (!classInfo) {
return;
}
// Forget our set of local aliases.
classInfo.aliases = null;
},

FunctionExpression: function(node) {
FunctionExpression(node) {
if (!classInfo) {
return;
}

var parent = node.parent;
const parent = node.parent;
if (!utils.isES5Component(parent.parent)) {
return;
}

if (parent.key.name === 'getInitialState') {
var body = node.body.body;
var lastBodyNode = body[body.length - 1];
const body = node.body.body;
const lastBodyNode = body[body.length - 1];

if (
lastBodyNode.type === 'ReturnStatement' &&
Expand All @@ -265,11 +265,11 @@ module.exports = {
}
} else {
// Create a new set for this.state aliases local to this method.
classInfo.aliases = [];
classInfo.aliases = new Set();
}
},

AssignmentExpression: function(node) {
AssignmentExpression(node) {
if (!classInfo) {
return;
}
Expand All @@ -281,7 +281,7 @@ module.exports = {
node.right.type === 'ObjectExpression'
) {
// Find the nearest function expression containing this assignment.
var fn = node;
let fn = node;
while (fn.type !== 'FunctionExpression' && fn.parent) {
fn = fn.parent;
}
Expand All @@ -300,14 +300,14 @@ module.exports = {
}
},

VariableDeclarator: function(node) {
VariableDeclarator(node) {
if (!classInfo || !node.init) {
return;
}
handleAssignment(node.id, node.init);
},

MemberExpression: function(node) {
MemberExpression(node) {
if (!classInfo) {
return;
}
Expand All @@ -322,13 +322,13 @@ module.exports = {
}
},

JSXSpreadAttribute: function(node) {
JSXSpreadAttribute(node) {
if (classInfo && isStateReference(node.argument)) {
classInfo = null;
}
},

ExperimentalSpreadProperty: function(node) {
ExperimentalSpreadProperty(node) {
if (classInfo && isStateReference(node.argument)) {
classInfo = null;
}
Expand Down

0 comments on commit 38bd88c

Please sign in to comment.