123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174 |
- // 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) {
- }
-
- });
- });
|