Permalink
Browse files

Make 'unused' check smarter about unused arguments.

This patch modifies 'unused' checks so that they ignore arguments
that were never used but that were followed by used arguments. For
example:

    function one(a, b) {
        return b;
    }

In this example, since unused 'a' is followed by used 'b', JSHint
assumes that this is a backwards compatibility issue or a specific
API pattern and suppresses the warning.

But this example, on the other hand, will generate a warning for 'b'
because there is no reason to keep it around:

    function one(a, b) {
        return a;
    }

References:

    Closes GH-607
  • Loading branch information...
valueof committed Aug 16, 2012
1 parent c035bc5 commit 5751c5ed249b7a035758a3ae876cfa1a360fd144
Showing with 68 additions and 61 deletions.
  1. +61 −34 jshint.js
  2. +5 −26 tests/regression/jquery.js
  3. +2 −1 tests/unit/fixtures/unused.js
View
@@ -208,7 +208,7 @@
moveTo, mootools, multistr, name, navigator, new, newcap, noarg, node, noempty, nomen,
nonew, nonstandard, nud, onbeforeunload, onblur, onerror, onevar, onecase, onfocus,
onload, onresize, onunload, open, openDatabase, openURL, opener, opera, options, outer, param,
- parent, parseFloat, parseInt, passfail, plusplus, postMessage, predef, print, process, prompt,
+ parent, parseFloat, parseInt, passfail, plusplus, postMessage, pop, predef, print, process, prompt,
proto, prototype, prototypejs, provides, push, quit, quotmark, range, raw, reach, reason, regexp,
readFile, readUrl, regexdash, removeEventListener, replace, report, require,
reserved, resizeBy, resizeTo, resolvePath, resumeUpdates, respond, rhino, right,
@@ -1745,7 +1745,6 @@ klass: do {
function addlabel(t, type, token) {
-
if (t === "hasOwnProperty") {
warning("'hasOwnProperty' is a really bad name.");
}
@@ -2340,18 +2339,22 @@ loop: for (;;) {
function assignop(s) {
symbol(s, 20).exps = true;
+
return infix(s, function (left, that) {
that.left = left;
+
if (predefined[left.value] === false &&
scope[left.value]["(global)"] === true) {
warning("Read only.", left);
} else if (left["function"]) {
warning("'{a}' is a function.", left, left.value);
}
+
if (left) {
if (option.esnext && funct[left.value] === "const") {
warning("Attempting to override '{a}' which is a constant", left, left.value);
}
+
if (left.id === "." || left.id === "[") {
if (!left.left || left.left.value === "arguments") {
warning("Bad assignment.", that);
@@ -2365,12 +2368,14 @@ loop: for (;;) {
that.right = expression(19);
return that;
}
+
if (left === syntax["function"]) {
warning(
"Expected an identifier in an assignment and instead saw a function invocation.",
token);
}
}
+
error("Bad assignment.", that);
}, 20);
}
@@ -3330,23 +3335,28 @@ loop: for (;;) {
function functionparams() {
- var i, t = nexttoken, p = [];
+ var next = nexttoken;
+ var params = [];
+ var ident;
+
advance("(");
nospace();
+
if (nexttoken.id === ")") {
advance(")");
return;
}
+
for (;;) {
- i = identifier(true);
- p.push(i);
- addlabel(i, "unused", token);
+ ident = identifier(true);
+ params.push(ident);
+ addlabel(ident, "unused", token);
if (nexttoken.id === ",") {
comma();
} else {
- advance(")", t);
+ advance(")", next);
nospace(prevtoken, token);
- return p;
+ return params;
}
}
}
@@ -4359,30 +4369,35 @@ loop: for (;;) {
implied[name] = newImplied;
};
+ var warnUnused = function (name, token) {
+ var line = token.line;
+ var chr = token.character;
+
+ if (option.unused)
+ warningAt("'{a}' is defined but never used.", line, chr, name);
+
+ unuseds.push({
+ name: name,
+ line: line,
+ character: chr
+ });
+ };
+
var checkUnused = function (func, key) {
var type = func[key];
var token = func["(tokens)"][key];
if (key.charAt(0) === "(")
return;
- // 'undefined' is a special case for (function (window, undefined) { ... })();
- // patterns.
-
- if (key === "undefined")
- return;
-
if (type !== "unused" && type !== "unction")
return;
- if (option.unused)
- warningAt("'{a}' is defined but never used.", token.line, token.character, key);
+ // Params are checked separately from other variables.
+ if (func["(params)"] && func["(params)"].indexOf(key) !== -1)
+ return;
- unuseds.push({
- name: key,
- line: token.line,
- character: token.character
- });
+ warnUnused(key, token);
};
// Check queued 'x is not defined' instances to see if they're still undefined.
@@ -4402,22 +4417,34 @@ loop: for (;;) {
checkUnused(func, key);
}
}
+
+ if (!func["(params)"])
+ return;
+
+ var params = func["(params)"].slice();
+ var param = params.pop();
+ var type;
+
+ while (param) {
+ type = func[param];
+
+ // 'undefined' is a special case for (function (window, undefined) { ... })();
+ // patterns.
+
+ if (param === "undefined")
+ return;
+
+ if (type !== "unused" && type !== "unction")
+ return;
+
+ warnUnused(param, func["(tokens)"][param]);
+ param = params.pop();
+ }
});
for (var key in declared) {
- if (is_own(declared, key)) {
- if (!is_own(global, key)) {
- if (option.unused) {
- warningAt("'{a}' is defined but never used.",
- declared[key].line, declared[key].character, key);
- }
-
- unuseds.push({
- name: key,
- line: declared[key].line,
- character: declared[key].character
- });
- }
+ if (is_own(declared, key) && !is_own(global, key)) {
+ warnUnused(key, declared[key]);
}
}
} catch (e) {
View
@@ -10,17 +10,13 @@ exports.jQuery_1_7 = function () {
var globals = { DOMParser: false, ActiveXObject: false, define: false };
TestRun()
- .addError(77, "'all' is defined but never used.")
.addError(551, "'name' is defined but never used.")
- .addError(903, "'i' is defined but never used.")
.addError(1044, "'actual' is defined but never used.")
.addError(1312, "'pCount' is defined but never used.")
.addError(1369, "'events' is defined but never used.")
.addError(1607, "'table' is defined but never used.")
.addError(1710, "'internalKey' is defined but never used.")
.addError(1813, "'internalKey' is defined but never used.")
- .addError(2760, "'i' is defined but never used.")
- .addError(2787, "'i' is defined but never used.")
.addError(2818, "Expected an assignment or function call and instead saw an expression.")
.addError(2822, "Expected an assignment or function call and instead saw an expression.")
.addError(2859, "'rnamespaces' is defined but never used.")
@@ -29,36 +25,19 @@ exports.jQuery_1_7 = function () {
.addError(2863, "'rescape' is defined but never used.")
.addError(2900, "'quick' is defined but never used.")
.addError(3269, "'related' is defined but never used.")
- .addError(3442, "'data' is defined but never used.")
- .addError(3442, "'namespaces' is defined but never used.")
- .addError(3449, "'namespaces' is defined but never used.")
.addError(3592, "'selector' is defined but never used.")
- .addError(3889, "'i' is defined but never used.")
.addError(4465, "'curLoop' is defined but never used.")
- .addError(4496, "'curLoop' is defined but never used.")
- .addError(4496, "'inplace' is defined but never used.")
- .addError(4496, "'result' is defined but never used.")
- .addError(4496, "'not' is defined but never used.")
.addError(4560, "Expected an assignment or function call and instead saw an expression.")
- .addError(4574, "'i' is defined but never used.")
- .addError(4633, "'elem' is defined but never used.")
- .addError(4637, "'elem' is defined but never used.")
- .addError(4637, "'match' is defined but never used.")
- .addError(4641, "'elem' is defined but never used.")
- .addError(4645, "'elem' is defined but never used.")
- .addError(4649, "'elem' is defined but never used.")
- .addError(4653, "'elem' is defined but never used.")
- .addError(4657, "'elem' is defined but never used.")
- .addError(4661, "'elem' is defined but never used.")
.addError(4694, "'cache' is defined but never used.")
.addError(4702, "Mixed spaces and tabs.")
.addError(4712, "Expected a 'break' statement before 'case'.")
.addError(4715, "Mixed spaces and tabs.")
- .addError(4818, "'all' is defined but never used.")
.addError(4843, "Expected an assignment or function call and instead saw an expression.")
- .addError(5552, "'i' is defined but never used.")
- .addError(5234, "'nodeCheck' is defined but never used.")
- .addError(5267, "'nodeCheck' is defined but never used.")
+ .addError(5635, "'elem' is defined but never used.")
+ .addError(5675, "'i' is defined but never used.")
+ .addError(5691, "'i' is defined but never used.")
+ .addError(7141, "'i' is defined but never used.")
+ .addError(6061, "'cur' is defined but never used.")
.addError(9209, "Mixed spaces and tabs.")
.test(src, { undef: true, unused: true }, globals);
};
@@ -12,6 +12,7 @@ function main(e, f) {
main(b);
-function foo() {
+function foo(err, cb) {
main();
+ cb();
}

4 comments on commit 5751c5e

Great news! When can I expect the new release tag including this feature and - more important - when can I expect to see it in node-jshint?

Owner

valueof replied Aug 16, 2012

Around this Sunday: both for the lib and node-jshint.

Member

goatslacker replied Aug 16, 2012

👏

Contributor

dougwilson replied Aug 18, 2012

👍

Please sign in to comment.