mirror of
https://github.com/Hopiu/annotate-esprima.git
synced 2026-03-17 00:10:23 +00:00
173 lines
5 KiB
JavaScript
Executable file
173 lines
5 KiB
JavaScript
Executable file
// Usage: node findbooleantrap.js /path/to/some/directory
|
|
// For more details, please read http://esprima.org/doc/#booleantrap.
|
|
|
|
/*jslint node:true sloppy:true plusplus:true */
|
|
|
|
var fs = require('fs'),
|
|
esprima = require('../esprima'),
|
|
dirname = process.argv[2],
|
|
doubleNegativeList = [];
|
|
|
|
|
|
// Black-list of terms with double-negative meaning.
|
|
doubleNegativeList = [
|
|
'hidden',
|
|
'caseinsensitive',
|
|
'disabled'
|
|
];
|
|
|
|
|
|
// Executes visitor on the object and its children (recursively).
|
|
function traverse(object, visitor) {
|
|
var key, child;
|
|
|
|
if (visitor.call(null, object) === false) {
|
|
return;
|
|
}
|
|
for (key in object) {
|
|
if (object.hasOwnProperty(key)) {
|
|
child = object[key];
|
|
if (typeof child === 'object' && child !== null) {
|
|
traverse(child, visitor);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// http://stackoverflow.com/q/5827612/
|
|
function walk(dir, done) {
|
|
var results = [];
|
|
fs.readdir(dir, function (err, list) {
|
|
if (err) {
|
|
return done(err);
|
|
}
|
|
var i = 0;
|
|
(function next() {
|
|
var file = list[i++];
|
|
if (!file) {
|
|
return done(null, results);
|
|
}
|
|
file = dir + '/' + file;
|
|
fs.stat(file, function (err, stat) {
|
|
if (stat && stat.isDirectory()) {
|
|
walk(file, function (err, res) {
|
|
results = results.concat(res);
|
|
next();
|
|
});
|
|
} else {
|
|
results.push(file);
|
|
next();
|
|
}
|
|
});
|
|
}());
|
|
});
|
|
}
|
|
|
|
walk(dirname, function (err, results) {
|
|
if (err) {
|
|
console.log('Error', err);
|
|
return;
|
|
}
|
|
|
|
results.forEach(function (filename) {
|
|
var shortname, first, content, syntax;
|
|
|
|
shortname = filename;
|
|
first = true;
|
|
|
|
if (shortname.substr(0, dirname.length) === dirname) {
|
|
shortname = shortname.substr(dirname.length + 1, shortname.length);
|
|
}
|
|
|
|
function getFunctionName(node) {
|
|
if (node.callee.type === 'Identifier') {
|
|
return node.callee.name;
|
|
}
|
|
if (node.callee.type === 'MemberExpression') {
|
|
return node.callee.property.name;
|
|
}
|
|
}
|
|
|
|
function report(node, problem) {
|
|
if (first === true) {
|
|
console.log(shortname + ': ');
|
|
first = false;
|
|
}
|
|
console.log(' Line', node.loc.start.line, 'in function',
|
|
getFunctionName(node) + ':', problem);
|
|
}
|
|
|
|
function checkSingleArgument(node) {
|
|
var args = node['arguments'],
|
|
functionName = getFunctionName(node);
|
|
|
|
if ((args.length !== 1) || (typeof args[0].value !== 'boolean')) {
|
|
return;
|
|
}
|
|
|
|
// Check if the method is a setter, i.e. starts with 'set',
|
|
// e.g. 'setEnabled(false)'.
|
|
if (functionName.substr(0, 3) !== 'set') {
|
|
report(node, 'Boolean literal with a non-setter function');
|
|
}
|
|
|
|
// Does it contain a term with double-negative meaning?
|
|
doubleNegativeList.forEach(function (term) {
|
|
if (functionName.toLowerCase().indexOf(term.toLowerCase()) >= 0) {
|
|
report(node, 'Boolean literal with confusing double-negative');
|
|
}
|
|
});
|
|
}
|
|
|
|
function checkMultipleArguments(node) {
|
|
var args = node['arguments'],
|
|
literalCount = 0;
|
|
|
|
args.forEach(function (arg) {
|
|
if (typeof arg.value === 'boolean') {
|
|
literalCount++;
|
|
}
|
|
});
|
|
|
|
// At least two arguments must be Boolean literals.
|
|
if (literalCount >= 2) {
|
|
|
|
// Check for two different Boolean literals in one call.
|
|
if (literalCount === 2 && args.length === 2) {
|
|
if (args[0].value !== args[1].value) {
|
|
report(node, 'Confusing true vs false');
|
|
return;
|
|
}
|
|
}
|
|
|
|
report(node, 'Multiple Boolean literals');
|
|
}
|
|
}
|
|
|
|
function checkLastArgument(node) {
|
|
var args = node['arguments'];
|
|
|
|
if (args.length < 2) {
|
|
return;
|
|
}
|
|
|
|
if (typeof args[args.length - 1].value === 'boolean') {
|
|
report(node, 'Ambiguous Boolean literal as the last argument');
|
|
}
|
|
}
|
|
|
|
try {
|
|
content = fs.readFileSync(filename, 'utf-8');
|
|
syntax = esprima.parse(content, { tolerant: true, loc: true });
|
|
traverse(syntax, function (node) {
|
|
if (node.type === 'CallExpression') {
|
|
checkSingleArgument(node);
|
|
checkLastArgument(node);
|
|
checkMultipleArguments(node);
|
|
}
|
|
});
|
|
} catch (e) {
|
|
}
|
|
|
|
});
|
|
});
|