Skip to content
This repository
  • 6 commits
  • 6 files changed
  • 0 comments
  • 2 contributors
Jul 19, 2012
shybyte shybyte Added new options maxstatementsperfunction, maxnestedblockdepthperfun…
…ction and maxparametersperfunction
4f2f629
Jul 20, 2012
shybyte shybyte Added new option maxcyclomaticcomplexitysperfunction 2a85b47
shybyte shybyte Fixed type for new option maxcyclomaticcomplexitysperfunction ->maxcy…
…clomaticcomplexityperfunction
0bb7486
Jul 26, 2012
shybyte shybyte Made the option names maxstatementsperfunction, maxnestedblockdepthpe…
…rfunction, maxparametersperfunction and maxcyclomaticcomplexityperfunction shorter by removing the suffix 'perfunction'
8cef419
Jul 31, 2012
shybyte shybyte Renamed options: maxparameters -> maxparams, maxnestedblockdepth -> m…
…axdepth and maxcyclomaticcomplexity -> maxcomplexity
c63e566
Aug 27, 2012
Anton Kovalyov valueof Merge branch 'master' of git://github.com/shybyte/jshint into shybyte…
…-master

Resolved conflicts, fixed a typo in the complexity warning, removed
word 'cyclomatic' from functions and variables (I thought it was
redundant) and made some minor styling changes.

Conflicts:
	jshint.js
	tests/unit/options.js
cbe1314
134 jshint.js
@@ -150,13 +150,13 @@
150 150
151 151 /*members "\b", "\t", "\n", "\f", "\r", "!=", "!==", "\"", "%", "(begin)",
152 152 "(breakage)", "(character)", "(context)", "(error)", "(explicitNewcap)", "(global)",
153   - "(identifier)", "(last)", "(lastcharacter)", "(line)", "(loopage)", "(name)",
154   - "(onevar)", "(params)", "(scope)", "(statement)", "(verb)", "(tokens)",
  153 + "(identifier)", "(last)", "(lastcharacter)", "(line)", "(loopage)", "(metrics)",
  154 + "(name)", "(onevar)", "(params)", "(scope)", "(statement)", "(verb)", "(tokens)",
155 155 "*", "+", "++", "-", "--", "\/", "<", "<=", "==",
156 156 "===", ">", ">=", $, $$, $A, $F, $H, $R, $break, $continue, $w, Abstract, Ajax,
157 157 __filename, __dirname, ActiveXObject, Array, ArrayBuffer, ArrayBufferView, Audio,
158 158 Autocompleter, Assets, Boolean, Builder, Buffer, Browser, COM, CScript, Canvas,
159   - CustomAnimation, Class, Control, Chain, Color, Cookie, Core, DataView, Date,
  159 + CustomAnimation, Class, Control, ComplexityCount, Chain, Color, Cookie, Core, DataView, Date,
160 160 Debug, Draggable, Draggables, Droppables, Document, DomReady, DOMReady, DOMParser, Drag,
161 161 E, Enumerator, Enumerable, Element, Elements, Error, Effect, EvalError, Event,
162 162 Events, FadeAnimation, Field, Flash, Float32Array, Float64Array, Form,
@@ -192,9 +192,9 @@
192 192 "\\", a, abs, addEventListener, address, alert, apply, applicationCache, arguments, arity,
193 193 asi, atob, b, basic, basicToken, bitwise, blacklist, block, blur, boolOptions, boss,
194 194 browser, btoa, c, call, callee, caller, camelcase, cases, charAt, charCodeAt, character,
195   - clearInterval, clearTimeout, close, closed, closure, comment, condition, confirm, console,
196   - constructor, content, couch, create, css, curly, d, data, datalist, dd, debug, decodeURI,
197   - decodeURIComponent, defaultStatus, defineClass, deserialize, devel, document,
  195 + clearInterval, clearTimeout, close, closed, closure, comment, complexityCount, condition,
  196 + confirm, console, constructor, content, couch, create, css, curly, d, data, datalist, dd, debug,
  197 + decodeURI, decodeURIComponent, defaultStatus, defineClass, deserialize, devel, document,
198 198 dojo, dijit, dojox, define, else, emit, encodeURI, encodeURIComponent,
199 199 eqeq, eqeqeq, eqnull, errors, es5, escape, esnext, eval, event, evidence, evil,
200 200 ex, exception, exec, exps, expr, exports, FileReader, first, floor, focus, forEach,
@@ -205,21 +205,23 @@
205 205 isDigit, isFinite, isNaN, iterator, java, join, jshint,
206 206 JSHINT, json, jquery, jQuery, keys, label, labelled, last, lastcharacter, lastsemic, laxbreak,
207 207 laxcomma, latedef, lbp, led, left, length, line, load, loadClass, localStorage, location,
208   - log, loopfunc, m, match, max, maxerr, maxlen, member,message, meta, module, moveBy,
209   - moveTo, mootools, multistr, name, navigator, new, newcap, noarg, node, noempty, nomen,
210   - nonew, nonstandard, nud, onbeforeunload, onblur, onerror, onevar, onecase, onfocus,
211   - onload, onresize, onunload, open, openDatabase, openURL, opener, opera, options, outer, param,
212   - parent, parseFloat, parseInt, passfail, plusplus, postMessage, pop, predef, print, process, prompt,
213   - proto, prototype, prototypejs, provides, push, quit, quotmark, range, raw, reach, reason, regexp,
214   - readFile, readUrl, regexdash, removeEventListener, replace, report, require,
215   - reserved, resizeBy, resizeTo, resolvePath, resumeUpdates, respond, rhino, right,
216   - runCommand, scroll, screen, scripturl, scrollBy, scrollTo, scrollbar, search, seal, self,
217   - send, serialize, sessionStorage, setInterval, setTimeout, setter, setterToken, shift, slice,
218   - smarttabs, sort, spawn, split, stack, status, start, strict, sub, substr, supernew, shadow,
219   - supplant, sum, sync, test, toLowerCase, toString, toUpperCase, toint32, token, tokens, top,
220   - trailing, type, typeOf, Uint16Array, Uint32Array, Uint8Array, undef, undefs, unused,
221   - urls, validthis, value, valueOf, var, vars, version, WebSocket, withstmt, white, window, windows,
222   - Worker, worker, wsh*/
  208 + log, loopfunc, m, match, max, maxcomplexity, maxdepth, maxerr, maxlen, maxstatements, maxparams,
  209 + member, message, meta, module, moveBy, moveTo, mootools, multistr, name, navigator, new, newcap,
  210 + nestedBlockDepth, noarg, node, noempty, nomen, nonew, nonstandard, nud, onbeforeunload, onblur,
  211 + onerror, onevar, onecase, onfocus, onload, onresize, onunload, open, openDatabase, openURL,
  212 + opener, opera, options, outer, param, parent, parseFloat, parseInt, passfail, plusplus,
  213 + postMessage, pop, predef, print, process, prompt, proto, prototype, prototypejs, provides, push,
  214 + quit, quotmark, range, raw, reach, reason, regexp, readFile, readUrl, regexdash,
  215 + removeEventListener, replace, report, require, reserved, resizeBy, resizeTo, resolvePath,
  216 + resumeUpdates, respond, rhino, right, runCommand, scroll, screen, scripturl, scrollBy, scrollTo,
  217 + scrollbar, search, seal, self, send, serialize, sessionStorage, setInterval, setTimeout, setter,
  218 + setterToken, shift, slice, smarttabs, sort, spawn, split, statementCount, stack, status, start,
  219 + strict, sub, substr, supernew, shadow, supplant, sum, sync, test, toLowerCase, toString,
  220 + toUpperCase, toint32, token, tokens, top, trailing, type, typeOf, Uint16Array, Uint32Array,
  221 + Uint8Array, undef, undefs, unused, urls, validthis, value, valueOf, var, vars, version,
  222 + verifyMaxParametersPerFunction, verifyMaxStatementsPerFunction, verifyMaxComplexityPerFunction,
  223 + verifyMaxNestedBlockDepthPerFunction, WebSocket, withstmt, white, window, windows, Worker, worker,
  224 + wsh*/
223 225
224 226 /*global exports: false */
225 227
@@ -330,11 +332,15 @@ var JSHINT = (function () {
330 332 // These are the JSHint options that can take any value
331 333 // (we use this object to detect invalid options)
332 334 valOptions = {
333   - maxlen: false,
334   - indent: false,
335   - maxerr: false,
336   - predef: false,
337   - quotmark: false //'single'|'double'|true
  335 + maxlen : false,
  336 + indent : false,
  337 + maxerr : false,
  338 + predef : false,
  339 + quotmark : false, //'single'|'double'|true
  340 + maxstatements: false, // {int} max statements per function
  341 + maxdepth : false, // {int} max nested block depth per function
  342 + maxparams : false, // {int} max params per function
  343 + maxcomplexity: false // {int} max cyclomatic complexity per function
338 344 },
339 345
340 346 // These are JSHint boolean options which are shared with JSLint
@@ -2741,6 +2747,10 @@ loop: for (;;) {
2741 2747 nonadjacent(token, nexttoken);
2742 2748 t = nexttoken;
2743 2749
  2750 + var metrics = funct["(metrics)"];
  2751 + metrics.nestedBlockDepth += 1;
  2752 + metrics.verifyMaxNestedBlockDepthPerFunction();
  2753 +
2744 2754 if (nexttoken.id === "{") {
2745 2755 advance("{");
2746 2756 line = token.line;
@@ -2768,6 +2778,8 @@ loop: for (;;) {
2768 2778
2769 2779 a = statements(line);
2770 2780
  2781 + metrics.statementCount += a.length;
  2782 +
2771 2783 if (isfunc) {
2772 2784 directive = m;
2773 2785 }
@@ -2802,6 +2814,7 @@ loop: for (;;) {
2802 2814 if (ordinary && option.noempty && (!a || a.length === 0)) {
2803 2815 warning("Empty block.");
2804 2816 }
  2817 + metrics.nestedBlockDepth -= 1;
2805 2818 return a;
2806 2819 }
2807 2820
@@ -3445,6 +3458,7 @@ loop: for (;;) {
3445 3458 "(context)" : funct,
3446 3459 "(breakage)" : 0,
3447 3460 "(loopage)" : 0,
  3461 + "(metrics)" : createMetrics(nexttoken),
3448 3462 "(scope)" : scope,
3449 3463 "(statement)": statement,
3450 3464 "(tokens)" : {}
@@ -3456,8 +3470,13 @@ loop: for (;;) {
3456 3470 addlabel(i, "function");
3457 3471 }
3458 3472 funct["(params)"] = functionparams();
  3473 + funct["(metrics)"].verifyMaxParametersPerFunction(funct["(params)"]);
3459 3474
3460 3475 block(false, false, true);
  3476 +
  3477 + funct["(metrics)"].verifyMaxStatementsPerFunction();
  3478 + funct["(metrics)"].verifyMaxComplexityPerFunction();
  3479 +
3461 3480 scope = oldScope;
3462 3481 option = oldOption;
3463 3482 funct["(last)"] = token.line;
@@ -3466,6 +3485,51 @@ loop: for (;;) {
3466 3485 return f;
3467 3486 }
3468 3487
  3488 + function createMetrics(functionStartToken) {
  3489 + return {
  3490 + statementCount: 0,
  3491 + nestedBlockDepth: -1,
  3492 + ComplexityCount: 1,
  3493 + verifyMaxStatementsPerFunction: function () {
  3494 + if (option.maxstatements &&
  3495 + this.statementCount > option.maxstatements) {
  3496 + var message = "Too many statements per function (" + this.statementCount + ").";
  3497 + warning(message, functionStartToken);
  3498 + }
  3499 + },
  3500 +
  3501 + verifyMaxParametersPerFunction: function (parameters) {
  3502 + if (option.maxparams &&
  3503 + parameters.length > option.maxparams) {
  3504 + var message = "Too many parameters per function (" + parameters.length + ").";
  3505 + warning(message, functionStartToken);
  3506 + }
  3507 + },
  3508 +
  3509 + verifyMaxNestedBlockDepthPerFunction: function () {
  3510 + if (option.maxdepth &&
  3511 + this.nestedBlockDepth > 0 &&
  3512 + this.nestedBlockDepth === option.maxdepth + 1) {
  3513 + var message = "Blocks are nested too deeply (" + this.nestedBlockDepth + ").";
  3514 + warning(message);
  3515 + }
  3516 + },
  3517 +
  3518 + verifyMaxComplexityPerFunction: function () {
  3519 + var max = option.maxcomplexity;
  3520 + var cc = this.ComplexityCount;
  3521 + if (max && cc > max) {
  3522 + var message = "Cyclomatic complexity is too high per function (" + cc + ").";
  3523 + warning(message, functionStartToken);
  3524 + }
  3525 + }
  3526 + };
  3527 + }
  3528 +
  3529 + function increaseComplexityCount() {
  3530 + funct["(metrics)"].ComplexityCount += 1;
  3531 + }
  3532 +
3469 3533
3470 3534 (function (x) {
3471 3535 x.nud = function () {
@@ -3749,6 +3813,7 @@ loop: for (;;) {
3749 3813
3750 3814 blockstmt("if", function () {
3751 3815 var t = nexttoken;
  3816 + increaseComplexityCount();
3752 3817 advance("(");
3753 3818 nonadjacent(this, t);
3754 3819 nospace();
@@ -3779,6 +3844,7 @@ loop: for (;;) {
3779 3844
3780 3845 block(false);
3781 3846 if (nexttoken.id === "catch") {
  3847 + increaseComplexityCount();
3782 3848 advance("catch");
3783 3849 nonadjacent(token, nexttoken);
3784 3850 advance("(");
@@ -3812,6 +3878,7 @@ loop: for (;;) {
3812 3878 var t = nexttoken;
3813 3879 funct["(breakage)"] += 1;
3814 3880 funct["(loopage)"] += 1;
  3881 + increaseComplexityCount();
3815 3882 advance("(");
3816 3883 nonadjacent(this, t);
3817 3884 nospace();
@@ -3889,6 +3956,7 @@ loop: for (;;) {
3889 3956 indentation(-option.indent);
3890 3957 advance("case");
3891 3958 this.cases.push(expression(20));
  3959 + increaseComplexityCount();
3892 3960 g = true;
3893 3961 advance(":");
3894 3962 funct["(verb)"] = "case";
@@ -3967,6 +4035,8 @@ loop: for (;;) {
3967 4035 var x = stmt("do", function () {
3968 4036 funct["(breakage)"] += 1;
3969 4037 funct["(loopage)"] += 1;
  4038 + increaseComplexityCount();
  4039 +
3970 4040 this.first = block(true);
3971 4041 advance("while");
3972 4042 var t = nexttoken;
@@ -3994,6 +4064,7 @@ loop: for (;;) {
3994 4064 var s, t = nexttoken;
3995 4065 funct["(breakage)"] += 1;
3996 4066 funct["(loopage)"] += 1;
  4067 + increaseComplexityCount();
3997 4068 advance("(");
3998 4069 nonadjacent(this, t);
3999 4070 nospace();
@@ -4327,12 +4398,13 @@ loop: for (;;) {
4327 4398 global = Object.create(predefined);
4328 4399 scope = global;
4329 4400 funct = {
4330   - "(global)": true,
4331   - "(name)": "(global)",
4332   - "(scope)": scope,
  4401 + "(global)": true,
  4402 + "(name)": "(global)",
  4403 + "(scope)": scope,
4333 4404 "(breakage)": 0,
4334   - "(loopage)": 0,
4335   - "(tokens)": {}
  4405 + "(loopage)": 0,
  4406 + "(tokens)": {},
  4407 + "(metrics)": createMetrics(nexttoken)
4336 4408 };
4337 4409 functions = [funct];
4338 4410 urls = [];
74 tests/unit/fixtures/max-cyclomatic-complexity-per-function.js
... ... @@ -0,0 +1,74 @@
  1 +function functionWithCyclomaticComplexity_1() {
  2 +}
  3 +
  4 +function functionWithCyclomaticComplexity_1_2() {
  5 + var dummy = 1;
  6 +}
  7 +
  8 +function functionWithCyclomaticComplexity_2() {
  9 + var dummy = 10;
  10 + if (dummy < 20) {
  11 + dummy = 2;
  12 + }
  13 +}
  14 +
  15 +function functionWithCyclomaticComplexity_2() {
  16 + var dummy = 10;
  17 + if (dummy < 20) {
  18 + dummy = 2;
  19 + } else {
  20 + // else does not count for cyclomatic complexity
  21 + dummy = 2;
  22 + }
  23 +}
  24 +
  25 +function functionWithCyclomaticComplexity_2_try_catch() {
  26 + var dummy = 0;
  27 + try {
  28 + dummy = dummy / 0;
  29 + } catch (exception) {
  30 + dummy = 10;
  31 + } finally {
  32 + // finally does no count
  33 + dummy = 10;
  34 + }
  35 +}
  36 +
  37 +function functionWithCyclomaticComplexity_1_try_finally() {
  38 + var dummy = 0;
  39 + try {
  40 + dummy = dummy / 0;
  41 + } finally {
  42 + // finally does no count
  43 + dummy = 10;
  44 + }
  45 +}
  46 +
  47 +function functionWithCyclomaticComplexity_8() {
  48 + var dummy = 10;
  49 + if (dummy < 20) {
  50 + while (dummy > 0) {
  51 + dummy -= 1;
  52 + }
  53 + for (dummy = 1; dummy < 10; dummy++) {
  54 + dummy += 1;
  55 + }
  56 + } else if (dummy<100) {
  57 + dummy = 2;
  58 + } else {
  59 + // else does not count for cyclomatic complexity
  60 + dummy = 2;
  61 + }
  62 +
  63 + do {
  64 + dummy++;
  65 + } while (dummy < 1000);
  66 +
  67 + switch (dummy) {
  68 + case 1:
  69 + break;
  70 + case 2: break;
  71 + // default does not count
  72 + default :
  73 + }
  74 +}
18 tests/unit/fixtures/max-nested-block-depth-per-function.js
... ... @@ -0,0 +1,18 @@
  1 +function functionWithNestedBlockDepth3() {
  2 + var dummy = 10;
  3 + if (dummy < 20) {
  4 + // here we should get a warning for max depth 1
  5 + while (dummy > 0) {
  6 + dummy -= 1;
  7 + // here we should get a warning for max depth 2
  8 + // but not for max depth 1, as we already got one above
  9 + if (dummy > 5) {
  10 + dummy -= 1;
  11 + }
  12 + }
  13 + // here we should get a warning for max depth 1
  14 + if (dummy === 0) {
  15 + dummy = 1;
  16 + }
  17 + }
  18 +}
2  tests/unit/fixtures/max-parameters-per-function.js
... ... @@ -0,0 +1,2 @@
  1 +function functionWith3Parameters(param1,param2,param3) {
  2 +}
19 tests/unit/fixtures/max-statements-per-function.js
... ... @@ -0,0 +1,19 @@
  1 +function functionWith8Statements() {
  2 + var i = 0;
  3 + var s = 0;
  4 +
  5 + // function declarations count 1
  6 + function innerFunction() {
  7 + // this does not count for the outer function
  8 + var i2 = 1;
  9 + i2 = 2;
  10 + }
  11 +
  12 + i = 2;
  13 + if (i > 0) {
  14 + while(i<10) {
  15 + s +=i;
  16 + }
  17 + }
  18 + return i;
  19 +}
81 tests/unit/options.js
@@ -1256,3 +1256,84 @@ exports.blacklist = function () {
1256 1256 .addError(5, "'btoa' is not defined.")
1257 1257 .test(code, { undef: true });
1258 1258 };
  1259 +
  1260 +/*
  1261 + * Tests the `maxstatements` option
  1262 + */
  1263 +exports.maxstatements = function () {
  1264 + var src = fs.readFileSync(__dirname + '/fixtures/max-statements-per-function.js', 'utf8');
  1265 +
  1266 + TestRun()
  1267 + .addError(1, "Too many statements per function (8).")
  1268 + .test(src, { maxstatements: 7 });
  1269 +
  1270 + TestRun()
  1271 + .test(src, { maxstatements: 8 });
  1272 +
  1273 + TestRun()
  1274 + .test(src, {});
  1275 +};
  1276 +
  1277 +/*
  1278 + * Tests the `maxdepth` option
  1279 + */
  1280 +exports.maxdepth = function () {
  1281 + var fixture = '/fixtures/max-nested-block-depth-per-function.js';
  1282 + var src = fs.readFileSync(__dirname + fixture, 'utf8');
  1283 +
  1284 + TestRun()
  1285 + .addError(5, "Blocks are nested too deeply (2).")
  1286 + .addError(14, "Blocks are nested too deeply (2).")
  1287 + .test(src, { maxdepth: 1 });
  1288 +
  1289 + TestRun()
  1290 + .addError(9, "Blocks are nested too deeply (3).")
  1291 + .test(src, { maxdepth: 2 });
  1292 +
  1293 + TestRun()
  1294 + .test(src, { maxdepth: 3 });
  1295 +
  1296 + TestRun()
  1297 + .test(src, {});
  1298 +};
  1299 +
  1300 +/*
  1301 + * Tests the `maxparams` option
  1302 + */
  1303 +exports.maxparams = function () {
  1304 + var fixture = '/fixtures/max-parameters-per-function.js';
  1305 + var src = fs.readFileSync(__dirname + fixture, 'utf8');
  1306 +
  1307 + TestRun()
  1308 + .addError(1, "Too many parameters per function (3).")
  1309 + .test(src, { maxparams: 2 });
  1310 +
  1311 + TestRun()
  1312 + .test(src, { maxparams: 3 });
  1313 +
  1314 +
  1315 + TestRun()
  1316 + .test(src, {});
  1317 +};
  1318 +
  1319 +/*
  1320 + * Tests the `maxcomplexity` option
  1321 + */
  1322 +exports.maxcomplexity = function () {
  1323 + var fixture = '/fixtures/max-cyclomatic-complexity-per-function.js';
  1324 + var src = fs.readFileSync(__dirname + fixture, 'utf8');
  1325 +
  1326 + TestRun()
  1327 + .addError(8, "Cyclomatic complexity is too high per function (2).")
  1328 + .addError(15, "Cyclomatic complexity is too high per function (2).")
  1329 + .addError(25, "Cyclomatic complexity is too high per function (2).")
  1330 + .addError(47, "Cyclomatic complexity is too high per function (8).")
  1331 + .test(src, { maxcomplexity: 1 });
  1332 +
  1333 + TestRun()
  1334 + .test(src, { maxcomplexity: 8 });
  1335 +
  1336 +
  1337 + TestRun()
  1338 + .test(src, {});
  1339 +};

No commit comments for this range

Something went wrong with that request. Please try again.