2 * JSHint, by JSHint Community.
4 * This file (and this file only) is licensed under the same slightly modified
5 * MIT license that JSLint is. It stops evil-doers everywhere.
7 * JSHint is a derivative work of JSLint:
9 * Copyright (c) 2002 Douglas Crockford (www.JSLint.com)
11 * Permission is hereby granted, free of charge, to any person obtaining
12 * a copy of this software and associated documentation files (the "Software"),
13 * to deal in the Software without restriction, including without limitation
14 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
15 * and/or sell copies of the Software, and to permit persons to whom
16 * the Software is furnished to do so, subject to the following conditions:
18 * The above copyright notice and this permission notice shall be included
19 * in all copies or substantial portions of the Software.
21 * The Software shall be used for Good, not Evil.
23 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
24 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
25 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
26 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
27 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
28 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
29 * DEALINGS IN THE SOFTWARE.
34 JSHINT is a global function. It takes two parameters.
36 var myResult = JSHINT(source, option);
38 The first parameter is either a string or an array of strings. If it is a
39 string, it will be split on '\n' or '\r'. If it is an array of strings, it
40 is assumed that each string represents one line. The source can be a
41 JavaScript text or a JSON text.
43 The second parameter is an optional object of options which control the
44 operation of JSHINT. Most of the options are booleans: They are all
45 optional and have a default value of false. One of the options, predef,
46 can be an array of names, which will be used to declare global variables,
47 or an object whose keys are used as global names, with a boolean value
48 that determines if they are assignable.
50 If it checks out, JSHINT returns true. Otherwise, it returns false.
52 If false, you can inspect JSHINT.errors to find out the problems.
53 JSHINT.errors is an array of objects containing these members:
56 line : The line (relative to 1) at which the lint was found
57 character : The character (relative to 1) at which the lint was found
59 evidence : The text line in which the problem occurred
60 raw : The raw message before the details were inserted
67 If a fatal error was found, a null will be the last element of the
70 You can request a data structure which contains JSHint's results.
72 var myData = JSHINT.data();
74 It returns a structure with this form:
90 lastcharacter: NUMBER,
140 Empty arrays will not be included.
145 evil: true, nomen: false, onevar: false, regexp: false, strict: true, boss: true,
146 undef: true, maxlen: 100, indent: 4, quotmark: double, unused: true
149 /*members "\b", "\t", "\n", "\f", "\r", "!=", "!==", "\"", "%", "(begin)",
150 "(breakage)", "(character)", "(context)", "(error)", "(explicitNewcap)", "(global)",
151 "(identifier)", "(last)", "(lastcharacter)", "(line)", "(loopage)", "(metrics)",
152 "(name)", "(onevar)", "(params)", "(scope)", "(statement)", "(verb)", "(tokens)", "(catch)",
153 "*", "+", "++", "-", "--", "\/", "<", "<=", "==",
154 "===", ">", ">=", $, $$, $A, $F, $H, $R, $break, $continue, $w, Abstract, Ajax,
155 __filename, __dirname, ActiveXObject, Array, ArrayBuffer, ArrayBufferView, Audio,
156 Autocompleter, Asset, Boolean, Builder, Buffer, Browser, Blob, COM, CScript, Canvas,
157 CustomAnimation, Class, Control, ComplexityCount, Chain, Color, Cookie, Core, DataView, Date,
158 Debug, Draggable, Draggables, Droppables, Document, DomReady, DOMEvent, DOMReady, DOMParser,
159 Drag, E, Enumerator, Enumerable, Element, Elements, Error, Effect, EvalError, Event,
160 Events, FadeAnimation, Field, Flash, Float32Array, Float64Array, Form,
161 FormField, Frame, FormData, Function, Fx, GetObject, Group, Hash, HotKey,
162 HTMLElement, HTMLAnchorElement, HTMLBaseElement, HTMLBlockquoteElement,
163 HTMLBodyElement, HTMLBRElement, HTMLButtonElement, HTMLCanvasElement, HTMLDirectoryElement,
164 HTMLDivElement, HTMLDListElement, HTMLFieldSetElement,
165 HTMLFontElement, HTMLFormElement, HTMLFrameElement, HTMLFrameSetElement,
166 HTMLHeadElement, HTMLHeadingElement, HTMLHRElement, HTMLHtmlElement,
167 HTMLIFrameElement, HTMLImageElement, HTMLInputElement, HTMLIsIndexElement,
168 HTMLLabelElement, HTMLLayerElement, HTMLLegendElement, HTMLLIElement,
169 HTMLLinkElement, HTMLMapElement, HTMLMenuElement, HTMLMetaElement,
170 HTMLModElement, HTMLObjectElement, HTMLOListElement, HTMLOptGroupElement,
171 HTMLOptionElement, HTMLParagraphElement, HTMLParamElement, HTMLPreElement,
172 HTMLQuoteElement, HTMLScriptElement, HTMLSelectElement, HTMLStyleElement,
173 HtmlTable, HTMLTableCaptionElement, HTMLTableCellElement, HTMLTableColElement,
174 HTMLTableElement, HTMLTableRowElement, HTMLTableSectionElement,
175 HTMLTextAreaElement, HTMLTitleElement, HTMLUListElement, HTMLVideoElement,
176 Iframe, IframeShim, Image, importScripts, Int16Array, Int32Array, Int8Array,
177 Insertion, InputValidator, JSON, Keyboard, Locale, LN10, LN2, LOG10E, LOG2E,
178 MAX_VALUE, MIN_VALUE, Map, Mask, Math, MenuItem, MessageChannel, MessageEvent, MessagePort,
179 MoveAnimation, MooTools, MutationObserver, NaN, Native, NEGATIVE_INFINITY, Node, NodeFilter,
180 Number, Object, ObjectRange,
181 Option, Options, OverText, PI, POSITIVE_INFINITY, PeriodicalExecuter, Point, Position, Prototype,
182 RangeError, Rectangle, ReferenceError, RegExp, ResizeAnimation, Request, RotateAnimation, Set,
183 SQRT1_2, SQRT2, ScrollBar, ScriptEngine, ScriptEngineBuildVersion,
184 ScriptEngineMajorVersion, ScriptEngineMinorVersion, Scriptaculous, Scroller,
185 Slick, Slider, Selector, SharedWorker, String, Style, SyntaxError, Sortable, Sortables,
186 SortableObserver, Sound, Spinner, System, Swiff, Text, TextArea, Template,
187 Timer, Tips, Type, TypeError, Toggle, Try, "use strict", unescape, URI, URIError, URL,
188 VBArray, WeakMap, WSH, WScript, XDomainRequest, Web, Window, XMLDOM, XMLHttpRequest, XMLSerializer,
189 XPathEvaluator, XPathException, XPathExpression, XPathNamespace, XPathNSResolver, XPathResult,
190 "\\", a, abs, addEventListener, address, alert, apply, applicationCache, arguments, arity,
191 asi, atob, b, basic, basicToken, bitwise, blacklist, block, blur, boolOptions, boss,
192 browser, btoa, c, call, callee, caller, camelcase, cases, charAt, charCodeAt, character,
193 clearInterval, clearTimeout, close, closed, closure, comment, complexityCount, condition,
194 confirm, console, constructor, content, couch, create, css, curly, d, data, datalist, dd, debug,
195 decodeURI, decodeURIComponent, defaultStatus, defineClass, deserialize, devel, document,
196 dojo, dijit, dojox, define, else, emit, encodeURI, encodeURIComponent, elem,
197 eqeq, eqeqeq, eqnull, errors, es5, escape, esnext, eval, event, evidence, evil,
198 ex, exception, exec, exps, expr, exports, FileReader, first, floor, focus, forEach,
199 forin, fragment, frames, from, fromCharCode, fud, funcscope, funct, function, functions,
200 g, gc, getComputedStyle, getRow, getter, getterToken, GLOBAL, global, globals, globalstrict,
201 hasOwnProperty, help, history, i, id, identifier, immed, implieds, importPackage, include,
202 indent, indexOf, init, ins, internals, instanceOf, isAlpha, isApplicationRunning, isArray,
203 isDigit, isFinite, isNaN, iterator, java, join, jshint,
204 JSHINT, json, jquery, jQuery, keys, label, labelled, last, lastcharacter, lastsemic, laxbreak,
205 laxcomma, latedef, lbp, led, left, length, line, load, loadClass, localStorage, location,
206 log, loopfunc, m, match, max, maxcomplexity, maxdepth, maxerr, maxlen, maxstatements, maxparams,
207 member, message, meta, module, moveBy, moveTo, mootools, multistr, name, navigator, new, newcap,
208 nestedBlockDepth, noarg, node, noempty, nomen, nonew, nonstandard, nud, onbeforeunload, onblur,
209 onerror, onevar, onecase, onfocus, onload, onresize, onunload, open, openDatabase, openURL,
210 opener, opera, options, outer, param, parent, parseFloat, parseInt, passfail, plusplus,
211 postMessage, pop, predef, print, process, prompt, proto, prototype, prototypejs, provides, push,
212 quit, quotmark, range, raw, reach, reason, regexp, readFile, readUrl, regexdash,
213 removeEventListener, replace, report, require, reserved, resizeBy, resizeTo, resolvePath,
214 resumeUpdates, respond, rhino, right, runCommand, scroll, scope, screen, scripturl, scrollBy,
215 scrollTo, scrollbar, search, seal, self, send, serialize, sessionStorage, setInterval, setTimeout,
216 setter, setterToken, shift, slice, smarttabs, sort, spawn, split, statement, statementCount, stack,
217 status, start, strict, sub, substr, supernew, shadow, supplant, sum, sync, test, toLowerCase,
218 toString, toUpperCase, toint32, token, tokens, top, trailing, type, typeOf, Uint16Array,
219 Uint32Array, Uint8Array, undef, undefs, unused, urls, validthis, value, valueOf, var, vars,
220 version, verifyMaxParametersPerFunction, verifyMaxStatementsPerFunction,
221 verifyMaxComplexityPerFunction, verifyMaxNestedBlockDepthPerFunction, WebSocket, withstmt, white,
222 window, windows, Worker, worker, wsh, yui, YUI, Y, YUI_config*/
224 /*global exports: false */
226 // We build the application inside a function so that we produce only a single
227 // global variable. That function will be invoked immediately, and its return
228 // value is the JSHINT function itself.
230 var JSHINT = (function () {
233 var anonname, // The guessed name for anonymous functions.
235 // These are operators that should not be used with the ! operator.
253 // These are the JSHint boolean options.
255 asi : true, // if automatic semicolon insertion should be tolerated
256 bitwise : true, // if bitwise operators should not be allowed
257 boss : true, // if advanced usage of assignments should be allowed
258 browser : true, // if the standard browser globals should be predefined
259 camelcase : true, // if identifiers should be required in camel case
260 couch : true, // if CouchDB globals should be predefined
261 curly : true, // if curly braces around all blocks should be required
262 debug : true, // if debugger statements should be allowed
263 devel : true, // if logging globals should be predefined (console,
265 dojo : true, // if Dojo Toolkit globals should be predefined
266 eqeqeq : true, // if === should be required
267 eqnull : true, // if == null comparisons should be tolerated
268 es5 : true, // if ES5 syntax should be allowed
269 esnext : true, // if es.next specific syntax should be allowed
270 evil : true, // if eval should be allowed
271 expr : true, // if ExpressionStatement should be allowed as Programs
272 forin : true, // if for in statements must filter
273 funcscope : true, // if only function scope should be used for scope tests
274 globalstrict: true, // if global "use strict"; should be allowed (also
276 immed : true, // if immediate invocations must be wrapped in parens
277 iterator : true, // if the `__iterator__` property should be allowed
278 jquery : true, // if jQuery globals should be predefined
279 lastsemic : true, // if semicolons may be ommitted for the trailing
280 // statements inside of a one-line blocks.
281 latedef : true, // if the use before definition should not be tolerated
282 laxbreak : true, // if line breaks should not be checked
283 laxcomma : true, // if line breaks should not be checked around commas
284 loopfunc : true, // if functions should be allowed to be defined within
286 mootools : true, // if MooTools globals should be predefined
287 multistr : true, // allow multiline strings
288 newcap : true, // if constructor names must be capitalized
289 noarg : true, // if arguments.caller and arguments.callee should be
291 node : true, // if the Node.js environment globals should be
293 noempty : true, // if empty blocks should be disallowed
294 nonew : true, // if using `new` for side-effects should be disallowed
295 nonstandard : true, // if non-standard (but widely adopted) globals should
297 nomen : true, // if names should be checked
298 onevar : true, // if only one var statement per function should be
300 onecase : true, // if one case switch statements should be allowed
301 passfail : true, // if the scan should stop on first error
302 plusplus : true, // if increment/decrement should not be allowed
303 proto : true, // if the `__proto__` property should be allowed
304 prototypejs : true, // if Prototype and Scriptaculous globals should be
306 regexdash : true, // if unescaped first/last dash (-) inside brackets
307 // should be tolerated
308 regexp : true, // if the . should not be allowed in regexp literals
309 rhino : true, // if the Rhino environment globals should be predefined
310 undef : true, // if variables should be declared before used
311 unused : true, // if variables should be always used
312 scripturl : true, // if script-targeted URLs should be tolerated
313 shadow : true, // if variable shadowing should be tolerated
314 smarttabs : true, // if smarttabs should be tolerated
315 // (http://www.emacswiki.org/emacs/SmartTabs)
316 strict : true, // require the "use strict"; pragma
317 sub : true, // if all forms of subscript notation are tolerated
318 supernew : true, // if `new function () { ... };` and `new Object;`
319 // should be tolerated
320 trailing : true, // if trailing whitespace rules apply
321 validthis : true, // if 'this' inside a non-constructor function is valid.
322 // This is a function scoped option only.
323 withstmt : true, // if with statements should be allowed
324 white : true, // if strict whitespace rules apply
325 worker : true, // if Web Worker script symbols should be allowed
326 wsh : true, // if the Windows Scripting Host environment globals
327 // should be predefined
328 yui : true // YUI variables should be predefined
331 // These are the JSHint options that can take any value
332 // (we use this object to detect invalid options)
338 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
346 // These are JSHint boolean options which are shared with JSLint
347 // where the definition in JSHint is opposite JSLint
358 // Inverted and renamed, use JSHint name here
363 // These are JSHint boolean options which are shared with JSLint
364 // where the name has been changed but the effect is unchanged
372 // browser contains a set of global names which are commonly provided by a
373 // web browser environment.
376 ArrayBufferView : false,
379 addEventListener : false,
380 applicationCache : false,
384 clearInterval : false,
385 clearTimeout : false,
390 defaultStatus : false,
394 Float32Array : false,
395 Float64Array : false,
399 getComputedStyle : false,
401 HTMLAnchorElement : false,
402 HTMLBaseElement : false,
403 HTMLBlockquoteElement : false,
404 HTMLBodyElement : false,
405 HTMLBRElement : false,
406 HTMLButtonElement : false,
407 HTMLCanvasElement : false,
408 HTMLDirectoryElement : false,
409 HTMLDivElement : false,
410 HTMLDListElement : false,
411 HTMLFieldSetElement : false,
412 HTMLFontElement : false,
413 HTMLFormElement : false,
414 HTMLFrameElement : false,
415 HTMLFrameSetElement : false,
416 HTMLHeadElement : false,
417 HTMLHeadingElement : false,
418 HTMLHRElement : false,
419 HTMLHtmlElement : false,
420 HTMLIFrameElement : false,
421 HTMLImageElement : false,
422 HTMLInputElement : false,
423 HTMLIsIndexElement : false,
424 HTMLLabelElement : false,
425 HTMLLayerElement : false,
426 HTMLLegendElement : false,
427 HTMLLIElement : false,
428 HTMLLinkElement : false,
429 HTMLMapElement : false,
430 HTMLMenuElement : false,
431 HTMLMetaElement : false,
432 HTMLModElement : false,
433 HTMLObjectElement : false,
434 HTMLOListElement : false,
435 HTMLOptGroupElement : false,
436 HTMLOptionElement : false,
437 HTMLParagraphElement : false,
438 HTMLParamElement : false,
439 HTMLPreElement : false,
440 HTMLQuoteElement : false,
441 HTMLScriptElement : false,
442 HTMLSelectElement : false,
443 HTMLStyleElement : false,
444 HTMLTableCaptionElement : false,
445 HTMLTableCellElement : false,
446 HTMLTableColElement : false,
447 HTMLTableElement : false,
448 HTMLTableRowElement : false,
449 HTMLTableSectionElement : false,
450 HTMLTextAreaElement : false,
451 HTMLTitleElement : false,
452 HTMLUListElement : false,
453 HTMLVideoElement : false,
460 localStorage : false,
462 MessageChannel : false,
463 MessageEvent : false,
467 MutationObserver : false,
472 onbeforeunload : true,
480 openDatabase : false,
485 removeEventListener : false,
492 sessionStorage : false,
495 SharedWorker : false,
504 XMLHttpRequest : false,
505 XMLSerializer : false,
506 XPathEvaluator : false,
507 XPathException : false,
508 XPathExpression : false,
509 XPathNamespace : false,
510 XPathNSResolver : false,
528 declared, // Globals that were declared using /*global ... */ syntax.
547 funct, // The current function
550 "closure", "exception", "global", "label",
551 "outer", "unused", "var"
554 functions, // All of the functions
556 global, // The global scope
557 implied, // Implied globals
597 InputValidator : false,
627 exports : true, // In Node it is ok to exports = module.exports = foo();
634 clearTimeout : false,
636 clearInterval : false
641 predefined, // Global variables defined by option
666 PeriodicalExecuter: false,
673 Autocompleter : false,
681 SortableObserver : false,
683 Scriptaculous : false
693 importPackage: false,
710 scope, // The current scope
713 // standard contains the global names that are provided by the
714 // ECMAScript standard.
720 decodeURIComponent : false,
722 encodeURIComponent : false,
727 hasOwnProperty : false,
739 ReferenceError : false,
749 // widely adopted global names that are not part of ECMAScript standard
765 importScripts : true,
771 ActiveXObject : true,
775 ScriptEngineBuildVersion : true,
776 ScriptEngineMajorVersion : true,
777 ScriptEngineMinorVersion : true,
781 XDomainRequest : true
789 // Regular expressions. Some of these are stupidly long.
790 var ax, cx, tx, nx, nxg, lx, ix, jx, ft;
792 /*jshint maxlen:300 */
794 // unsafe comment or string
795 ax = /@cc|<\/?|script|\]\s*\]|<\s*!|</i;
797 // unsafe characters that are silently deleted by one or more browsers
798 cx = /[\u0000-\u001f\u007f-\u009f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/;
801 tx = /^\s*([(){}\[.,:;'"~\?\]#@]|==?=?|\/=(?!(\S*\/[gim]?))|\/(\*(jshint|jslint|members?|global)?|\/)?|\*[\/=]?|\+(?:=|\++)?|-(?:=|-+)?|%=?|&[&=]?|\|[|=]?|>>?>?=?|<([\/=!]|\!(\[|--)?|<=?)?|\^=?|\!=?=?|[a-zA-Z_$][a-zA-Z0-9_$]*|[0-9]+([xX][0-9a-fA-F]+|\.[0-9]*)?([eE][+\-]?[0-9]+)?)/;
803 // characters in strings that need escapement
804 nx = /[\u0000-\u001f&<"\/\\\u007f-\u009f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/;
805 nxg = /[\u0000-\u001f&<"\/\\\u007f-\u009f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g;
811 ix = /^([a-zA-Z_$][a-zA-Z0-9_$]*)$/;
814 jx = /^(?:javascript|jscript|ecmascript|vbscript|mocha|livescript)\s*:/i;
816 // catches /* falls through */ comments
817 ft = /^\s*\/\*\s*falls\sthrough\s*\*\/\s*$/;
820 function F() {} // Used by Object.create
822 function is_own(object, name) {
823 // The object.hasOwnProperty method fails when the property under consideration
824 // is named 'hasOwnProperty'. So we have to use this more convoluted form.
825 return Object.prototype.hasOwnProperty.call(object, name);
828 function checkOption(name, t) {
829 if (valOptions[name] === undefined && boolOptions[name] === undefined) {
830 warning("Bad option: '" + name + "'.", t);
834 function isString(obj) {
835 return Object.prototype.toString.call(obj) === "[object String]";
838 // Provide critical ES5 functions to ES3.
840 if (typeof Array.isArray !== "function") {
841 Array.isArray = function (o) {
842 return Object.prototype.toString.apply(o) === "[object Array]";
846 if (!Array.prototype.forEach) {
847 Array.prototype.forEach = function (fn, scope) {
848 var len = this.length;
850 for (var i = 0; i < len; i++) {
851 fn.call(scope || this, this[i], i, this);
856 if (!Array.prototype.indexOf) {
857 Array.prototype.indexOf = function (searchElement /*, fromIndex */ ) {
858 if (this === null || this === undefined) {
859 throw new TypeError();
862 var t = new Object(this);
863 var len = t.length >>> 0;
870 if (arguments.length > 0) {
871 n = Number(arguments[1]);
872 if (n != n) { // shortcut for verifying if it's NaN
874 } else if (n !== 0 && n != Infinity && n != -Infinity) {
875 n = (n > 0 || -1) * Math.floor(Math.abs(n));
883 var k = n >= 0 ? n : Math.max(len - Math.abs(n), 0);
884 for (; k < len; k++) {
885 if (k in t && t[k] === searchElement) {
894 if (typeof Object.create !== "function") {
895 Object.create = function (o) {
901 if (typeof Object.keys !== "function") {
902 Object.keys = function (o) {
913 // Non standard methods
915 function isAlpha(str) {
916 return (str >= "a" && str <= "z\uffff") ||
917 (str >= "A" && str <= "Z\uffff");
920 function isDigit(str) {
921 return (str >= "0" && str <= "9");
924 function isIdentifier(token, value) {
928 if (!token.identifier || token.value !== value)
934 function supplant(str, data) {
935 return str.replace(/\{([^{}]*)\}/g, function (a, b) {
937 return typeof r === "string" || typeof r === "number" ? r : a;
941 function combine(t, o) {
944 if (is_own(o, n) && !is_own(JSHINT.blacklist, n)) {
950 function updatePredefined() {
951 Object.keys(JSHINT.blacklist).forEach(function (key) {
952 delete predefined[key];
958 combine(predefined, couch);
962 combine(predefined, rhino);
965 if (option.prototypejs) {
966 combine(predefined, prototypejs);
970 combine(predefined, node);
971 option.globalstrict = true;
975 combine(predefined, devel);
979 combine(predefined, dojo);
982 if (option.browser) {
983 combine(predefined, browser);
986 if (option.nonstandard) {
987 combine(predefined, nonstandard);
991 combine(predefined, jquery);
994 if (option.mootools) {
995 combine(predefined, mootools);
999 combine(predefined, worker);
1003 combine(predefined, wsh);
1006 if (option.esnext) {
1010 if (option.globalstrict && option.strict !== false) {
1011 option.strict = true;
1015 combine(predefined, yui);
1020 // Produce an error warning.
1021 function quit(message, line, chr) {
1022 var percentage = Math.floor((line / lines.length) * 100);
1025 name: "JSHintError",
1028 message: message + " (" + percentage + "% scanned).",
1033 function isundef(scope, m, t, a) {
1034 return JSHINT.undefs.push([scope, m, t, a]);
1037 function warning(m, t, a, b, c, d) {
1040 if (t.id === "(end)") { // `~
1048 evidence: lines[l - 1] || "",
1051 scope: JSHINT.scope,
1057 w.reason = supplant(m, w);
1058 JSHINT.errors.push(w);
1059 if (option.passfail) {
1060 quit("Stopping. ", l, ch);
1063 if (warnings >= option.maxerr) {
1064 quit("Too many errors.", l, ch);
1069 function warningAt(m, l, ch, a, b, c, d) {
1076 function error(m, t, a, b, c, d) {
1077 warning(m, t, a, b, c, d);
1080 function errorAt(m, l, ch, a, b, c, d) {
1087 // Tracking of "internal" scripts, like eval containing a static string
1088 function addInternalSrc(elem, src) {
1095 JSHINT.internals.push(i);
1100 // lexical analysis and token construction
1102 var lex = (function lex() {
1103 var character, from, line, s;
1105 // Private lex methods
1107 function nextLine() {
1110 tw; // trailing whitespace check
1112 if (line >= lines.length)
1119 // If smarttabs option is used check for spaces followed by tabs only.
1120 // Otherwise check for any occurence of mixed tabs and spaces.
1121 // Tabs and one space followed by block comment is allowed.
1122 if (option.smarttabs) {
1123 // negative look-behind for "//"
1124 match = s.match(/(\/\/)? \t/);
1125 at = match && !match[1] ? 0 : -1;
1127 at = s.search(/ \t|\t [^\*]/);
1131 warningAt("Mixed spaces and tabs.", line, at + 1);
1133 s = s.replace(/\t/g, tab);
1137 warningAt("Unsafe character.", line, at);
1139 if (option.maxlen && option.maxlen < s.length)
1140 warningAt("Line too long.", line, s.length);
1142 // Check for trailing whitespaces
1143 tw = option.trailing && s.match(/^(.*?)\s+$/);
1144 if (tw && !/^\s+$/.test(s)) {
1145 warningAt("Trailing whitespace.", line, tw[1].length + 1);
1150 // Produce a token object. The token inherits from a syntax symbol.
1152 function it(type, value) {
1155 function checkName(name) {
1156 if (!option.proto && name === "__proto__") {
1157 warningAt("The '{a}' property is deprecated.", line, from, name);
1161 if (!option.iterator && name === "__iterator__") {
1162 warningAt("'{a}' is only available in JavaScript 1.7.", line, from, name);
1166 // Check for dangling underscores unless we're in Node
1167 // environment and this identifier represents built-in
1168 // Node globals with underscores.
1170 var hasDangling = /^(_+.*|.*_+)$/.test(name);
1172 if (option.nomen && hasDangling && name !== "_") {
1173 if (option.node && token.id !== "." && /^(__dirname|__filename)$/.test(name))
1176 warningAt("Unexpected {a} in '{b}'.", line, from, "dangling '_'", name);
1180 // Check for non-camelcase names. Names like MY_VAR and
1181 // _myVar are okay though.
1183 if (option.camelcase) {
1184 if (name.replace(/^_+/, "").indexOf("_") > -1 && !name.match(/^[A-Z0-9_]*$/)) {
1185 warningAt("Identifier '{a}' is not in camel case.", line, from, value);
1190 if (type === "(color)" || type === "(range)") {
1192 } else if (type === "(punctuator)" ||
1193 (type === "(identifier)" && is_own(syntax, value))) {
1194 t = syntax[value] || syntax["(error)"];
1199 t = Object.create(t);
1201 if (type === "(string)" || type === "(range)") {
1202 if (!option.scripturl && jx.test(value)) {
1203 warningAt("Script URL.", line, from);
1207 if (type === "(identifier)") {
1208 t.identifier = true;
1214 t.character = character;
1217 if (i !== "(endline)") {
1219 (("(,=:[!&|?{};".indexOf(i.charAt(i.length - 1)) >= 0) ||
1226 // Public lex methods
1228 init: function (source) {
1229 if (typeof source === "string") {
1231 .replace(/\r\n/g, "\n")
1232 .replace(/\r/g, "\n")
1238 // If the first line is a shebang (#!), make it a blank and move on.
1239 // Shebangs are used by Node scripts.
1240 if (lines[0] && lines[0].substr(0, 2) === "#!")
1248 range: function (begin, end) {
1251 if (s.charAt(0) !== begin) {
1252 errorAt("Expected '{a}' and instead saw '{b}'.",
1253 line, character, begin, s.charAt(0));
1261 errorAt("Missing '{a}'.", line, character, c);
1266 return it("(range)", value);
1268 warningAt("Unexpected '{a}'.", line, character, c);
1276 // token -- this is called by advance to get the next token
1277 token: function () {
1278 var b, c, captures, d, depth, high, i, l, low, q, t, isLiteral, isInRange, n;
1281 var r = x.exec(s), r1;
1288 from = character + l - r1.length;
1294 function string(x) {
1295 var c, j, r = "", allowNewLine = false;
1297 if (jsonmode && x !== "\"") {
1298 warningAt("Strings must use doublequote.",
1302 if (option.quotmark) {
1303 if (option.quotmark === "single" && x !== "'") {
1304 warningAt("Strings must use singlequote.",
1306 } else if (option.quotmark === "double" && x !== "\"") {
1307 warningAt("Strings must use doublequote.",
1309 } else if (option.quotmark === true) {
1310 quotmark = quotmark || x;
1311 if (quotmark !== x) {
1312 warningAt("Mixed double and single quotes.",
1319 var i = parseInt(s.substr(j + 1, n), 16);
1321 if (i >= 32 && i <= 126 &&
1322 i !== 34 && i !== 92 && i !== 39) {
1323 warningAt("Unnecessary escapement.", line, character);
1326 c = String.fromCharCode(i);
1333 while (j >= s.length) {
1336 var cl = line, cf = from;
1338 errorAt("Unclosed string.", cl, cf);
1339 break unclosedString;
1343 allowNewLine = false;
1345 warningAt("Unclosed string.", cl, cf);
1352 s = s.substr(j + 1);
1353 return it("(string)", r, x);
1357 if (c === "\n" || c === "\r") {
1360 warningAt("Control character in string: {a}.",
1361 line, character + j, s.slice(0, j));
1362 } else if (c === "\\") {
1366 n = s.charAt(j + 1);
1374 warningAt("Avoid \\'.", line, character);
1394 // Octal literals fail in strict mode
1395 // check if the number is between 00 and 07
1396 // where 'n' is the token next to 'c'
1397 if (n >= 0 && n <= 7 && directive["use strict"]) {
1399 "Octal literals are not allowed in strict mode.",
1408 warningAt("Avoid \\v.", line, character);
1414 warningAt("Avoid \\x-.", line, character);
1419 // last character is escape character
1420 // always allow new line if escaped, but show
1421 // warning if option is not set
1422 allowNewLine = true;
1423 if (option.multistr) {
1425 warningAt("Avoid EOL escapement.", line, character);
1431 warningAt("Bad escapement of EOL. Use option multistr if needed.",
1435 if (s.charAt(j - 2) === "<")
1439 warningAt("Bad escapement.", line, character);
1450 return it(nextLine() ? "(endline)" : "(end)", "");
1458 while (s && s < "!") {
1462 errorAt("Unexpected '{a}'.", line, character, s.substr(0, 1));
1469 if (isAlpha(c) || c === "_" || c === "$") {
1470 return it("(identifier)", t);
1476 if (!isFinite(Number(t))) {
1477 warningAt("Bad number '{a}'.",
1478 line, character, t);
1480 if (isAlpha(s.substr(0, 1))) {
1481 warningAt("Missing space after '{a}'.",
1482 line, character, t);
1487 if (token.id !== ".") {
1488 warningAt("Don't use extra leading zeros '{a}'.",
1489 line, character, t);
1491 } else if (jsonmode && (d === "x" || d === "X")) {
1492 warningAt("Avoid 0x-. '{a}'.",
1493 line, character, t);
1496 if (t.substr(t.length - 1) === ".") {
1498 "A trailing decimal point can be confused with a dot '{a}'.", line, character, t);
1500 return it("(number)", t);
1514 token.comment = true;
1526 errorAt("Unclosed comment.", line, character);
1529 s = s.substr(i + 2);
1530 token.comment = true;
1533 // /*members /*jshint /*global
1545 character: character,
1553 if (s.charAt(0) === "=") {
1554 errorAt("A regular expression literal can be confused with '/='.",
1568 errorAt("Unclosed regular expression.", line, from);
1569 return quit("Stopping.", line, from);
1572 warningAt("{a} unterminated regular expression " +
1573 "group(s).", line, from + l, depth);
1575 c = s.substr(0, l - 1);
1581 while (q[s.charAt(l)] === true) {
1582 q[s.charAt(l)] = false;
1588 if (q === "/" || q === "*") {
1589 errorAt("Confusing regular expression.",
1592 return it("(regexp)", c);
1597 "Unexpected control character in regular expression.", line, from + l);
1598 } else if (c === "<") {
1600 "Unexpected escaped character '{a}' in regular expression.", line, from + l, c);
1607 if (s.charAt(l) === "?") {
1609 switch (s.charAt(l)) {
1617 "Expected '{a}' and instead saw '{b}'.", line, from + l, ":", s.charAt(l));
1628 warningAt("Unescaped '{a}'.",
1629 line, from + l, ")");
1636 while (s.charAt(l) === " ") {
1642 "Spaces are hard to count. Use {{a}}.", line, from + l, q);
1649 if (s.charAt(l) === "]") {
1650 errorAt("Unescaped '{a}'.",
1651 line, from + l, "^");
1655 warningAt("Empty class.", line,
1667 warningAt("Unescaped '{a}'.",
1676 if (isLiteral && !isInRange) {
1679 } else if (isInRange) {
1681 } else if (s.charAt(l) === "]") {
1684 if (option.regexdash !== (l === 2 || (l === 3 &&
1685 s.charAt(1) === "^"))) {
1686 warningAt("Unescaped '{a}'.",
1687 line, from + l - 1, "-");
1693 if (isInRange && !option.regexdash) {
1694 warningAt("Unescaped '{a}'.",
1695 line, from + l - 1, "-");
1702 "Unexpected control character in regular expression.", line, from + l);
1703 } else if (c === "<") {
1705 "Unexpected escaped character '{a}' in regular expression.", line, from + l, c);
1709 // \w, \s and \d are never part of a character range
1710 if (/[wsd]/i.test(c)) {
1712 warningAt("Unescaped '{a}'.",
1713 line, from + l, "-");
1717 } else if (isInRange) {
1724 warningAt("Unescaped '{a}'.",
1725 line, from + l - 1, "/");
1750 if (option.regexp) {
1751 warningAt("Insecure '{a}'.", line,
1761 warningAt("Unescaped '{a}'.", line,
1765 switch (s.charAt(l)) {
1770 if (s.charAt(l) === "?") {
1777 if (c < "0" || c > "9") {
1779 "Expected a number and instead saw '{a}'.", line, from + l, c);
1780 break; // No reason to continue checking numbers.
1786 if (c < "0" || c > "9") {
1790 low = +c + (low * 10);
1797 if (c >= "0" && c <= "9") {
1802 if (c < "0" || c > "9") {
1806 high = +c + (high * 10);
1810 if (s.charAt(l) !== "}") {
1812 "Expected '{a}' and instead saw '{b}'.", line, from + l, "}", c);
1816 if (s.charAt(l) === "?") {
1821 "'{a}' should not be greater than '{b}'.", line, from + l, low, high);
1826 c = s.substr(0, l - 1);
1829 return it("(regexp)", c);
1831 return it("(punctuator)", t);
1836 return it("(punctuator)", t);
1838 return it("(punctuator)", t);
1847 function addlabel(t, type, token) {
1848 if (t === "hasOwnProperty") {
1849 warning("'hasOwnProperty' is a really bad name.");
1852 // Define t in the current function in the current scope.
1853 if (type === "exception") {
1854 if (is_own(funct["(context)"], t)) {
1855 if (funct[t] !== true && !option.node) {
1856 warning("Value of '{a}' may be overwritten in IE.", nexttoken, t);
1861 if (is_own(funct, t) && !funct["(global)"]) {
1862 if (funct[t] === true) {
1864 warning("'{a}' was used before it was defined.", nexttoken, t);
1866 if (!option.shadow && type !== "exception") {
1867 warning("'{a}' is already defined.", nexttoken, t);
1875 funct["(tokens)"][t] = token;
1878 if (funct["(global)"]) {
1880 if (is_own(implied, t)) {
1882 warning("'{a}' was used before it was defined.", nexttoken, t);
1891 function doOption() {
1894 var quotmarkValue = option.quotmark;
1896 var b, obj, filter, t, tn, v, minus;
1900 error("Unbegun comment.");
1909 option.quotmark = false;
1914 filter = boolOptions;
1929 if (t.type === "special" && t.value === "*/") {
1932 if (t.id !== "(endline)" && t.id !== ",") {
1938 if (o === "/*global" && t.value === "-") {
1943 if (t.type !== "(string)" && t.type !== "(identifier)" && o !== "/*members") {
1944 error("Bad option.", t);
1951 if (obj === membersOnly) {
1952 error("Expected '{a}' and instead saw '{b}'.", t, "*/", ":");
1955 if (o === "/*jshint") {
1956 checkOption(t.value, t);
1969 if (numericVals.indexOf(t.value) > -1 && (o === "/*jshint" || o === "/*jslint")) {
1972 if (typeof b !== "number" || !isFinite(b) || b <= 0 || Math.floor(b) !== b) {
1973 error("Expected a small integer and instead saw '{a}'.", v, v.value);
1976 if (t.value === "indent")
1980 } else if (t.value === "validthis") {
1981 if (funct["(global)"]) {
1982 error("Option 'validthis' can't be used in a global scope.");
1984 if (v.value === "true" || v.value === "false")
1985 obj[t.value] = v.value === "true";
1987 error("Bad option value.", v);
1989 } else if (t.value === "quotmark" && (o === "/*jshint")) {
1992 obj.quotmark = true;
1995 obj.quotmark = false;
1999 obj.quotmark = v.value;
2002 error("Bad option value.", v);
2004 } else if (v.value === "true" || v.value === "false") {
2005 if (o === "/*jslint") {
2006 tn = renamedOptions[t.value] || t.value;
2007 obj[tn] = v.value === "true";
2008 if (invertedOptions[tn] !== undefined) {
2012 obj[t.value] = v.value === "true";
2015 if (t.value === "newcap")
2016 obj["(explicitNewcap)"] = true;
2018 error("Bad option value.", v);
2022 if (o === "/*jshint" || o === "/*jslint") {
2023 error("Missing option value.", t);
2026 obj[t.value] = false;
2028 if (o === "/*global" && minus === true) {
2029 JSHINT.blacklist[t.value] = t.value;
2037 if (o === "/*members") {
2038 option.quotmark = quotmarkValue;
2041 combine(predefined, predef);
2043 for (var key in predef) {
2044 if (is_own(predef, key)) {
2055 // We need a peek function. If it has an argument, it peeks that much farther
2056 // ahead. It is used to distinguish
2057 // for ( var i in ...
2059 // for ( var i = ...
2062 var i = p || 0, j = 0, t;
2067 t = lookahead[j] = lex.token();
2076 // Produce the next token. It looks for programming errors.
2078 function advance(id, t) {
2081 if (nexttoken.id === ".") {
2082 warning("A dot following a number can be confused with a decimal point.", token);
2086 if (nexttoken.id === "-" || nexttoken.id === "--") {
2087 warning("Confusing minusses.");
2091 if (nexttoken.id === "+" || nexttoken.id === "++") {
2092 warning("Confusing plusses.");
2097 if (token.type === "(string)" || token.identifier) {
2098 anonname = token.value;
2101 if (id && nexttoken.id !== id) {
2103 if (nexttoken.id === "(end)") {
2104 warning("Unmatched '{a}'.", t, t.id);
2106 warning("Expected '{a}' to match '{b}' from line {c} and instead saw '{d}'.",
2107 nexttoken, id, t.id, t.line, nexttoken.value);
2109 } else if (nexttoken.type !== "(identifier)" ||
2110 nexttoken.value !== id) {
2111 warning("Expected '{a}' and instead saw '{b}'.",
2112 nexttoken, id, nexttoken.value);
2119 nexttoken = lookahead.shift() || lex.token();
2120 if (nexttoken.id === "(end)" || nexttoken.id === "(error)") {
2123 if (nexttoken.type === "special") {
2126 if (nexttoken.id !== "(endline)") {
2134 // This is the heart of JSHINT, the Pratt parser. In addition to parsing, it
2135 // is looking for ad hoc lint patterns. We add .fud to Pratt's model, which is
2136 // like .nud except that it is only used on the first token of a statement.
2137 // Having .fud makes it much easier to define statement-oriented languages like
2138 // JavaScript. I retained Pratt's nomenclature.
2140 // .nud Null denotation
2141 // .fud First null denotation
2142 // .led Left denotation
2143 // lbp Left binding power
2144 // rbp Right binding power
2146 // They are elements of the parsing method called Top Down Operator Precedence.
2148 function expression(rbp, initial) {
2149 var left, isArray = false, isObject = false;
2151 if (nexttoken.id === "(end)")
2152 error("Unexpected early end of program.", token);
2156 anonname = "anonymous";
2157 funct["(verb)"] = token.value;
2159 if (initial === true && token.fud) {
2165 if (nexttoken.type === "(number)" && token.id === ".") {
2166 warning("A leading decimal point can be confused with a dot: '.{a}'.",
2167 token, nexttoken.value);
2171 error("Expected an identifier and instead saw '{a}'.",
2175 while (rbp < nexttoken.lbp) {
2176 isArray = token.value === "Array";
2177 isObject = token.value === "Object";
2179 // #527, new Foo.Array(), Foo.Array(), new Foo.Object(), Foo.Object()
2180 // Line breaks in IfStatement heads exist to satisfy the checkJSHint
2181 // "Line too long." error.
2182 if (left && (left.value || (left.first && left.first.value))) {
2183 // If the left.value is not "new", or the left.first.value is a "."
2184 // then safely assume that this is not "new Array()" and possibly
2185 // not "new Object()"...
2186 if (left.value !== "new" ||
2187 (left.first && left.first.value && left.first.value === ".")) {
2189 // ...In the case of Object, if the left.value and token.value
2190 // are not equal, then safely assume that this not "new Object()"
2191 if (left.value !== token.value) {
2198 if (isArray && token.id === "(" && nexttoken.id === ")")
2199 warning("Use the array literal notation [].", token);
2200 if (isObject && token.id === "(" && nexttoken.id === ")")
2201 warning("Use the object literal notation {}.", token);
2203 left = token.led(left);
2205 error("Expected an operator and instead saw '{a}'.",
2214 // Functions for conformance of style.
2216 function adjacent(left, right) {
2217 left = left || token;
2218 right = right || nexttoken;
2220 if (left.character !== right.from && left.line === right.line) {
2221 left.from += (left.character - left.from);
2222 warning("Unexpected space after '{a}'.", left, left.value);
2227 function nobreak(left, right) {
2228 left = left || token;
2229 right = right || nexttoken;
2230 if (option.white && (left.character !== right.from || left.line !== right.line)) {
2231 warning("Unexpected space before '{a}'.", right, right.value);
2235 function nospace(left, right) {
2236 left = left || token;
2237 right = right || nexttoken;
2238 if (option.white && !left.comment) {
2239 if (left.line === right.line) {
2240 adjacent(left, right);
2245 function nonadjacent(left, right) {
2247 left = left || token;
2248 right = right || nexttoken;
2249 if (left.value === ";" && right.value === ";") {
2252 if (left.line === right.line && left.character === right.from) {
2253 left.from += (left.character - left.from);
2254 warning("Missing space after '{a}'.",
2260 function nobreaknonadjacent(left, right) {
2261 left = left || token;
2262 right = right || nexttoken;
2263 if (!option.laxbreak && left.line !== right.line) {
2264 warning("Bad line breaking before '{a}'.", right, right.id);
2265 } else if (option.white) {
2266 left = left || token;
2267 right = right || nexttoken;
2268 if (left.character === right.from) {
2269 left.from += (left.character - left.from);
2270 warning("Missing space after '{a}'.",
2276 function indentation(bias) {
2278 if (option.white && nexttoken.id !== "(end)") {
2279 i = indent + (bias || 0);
2280 if (nexttoken.from !== i) {
2282 "Expected '{a}' to have an indentation at {b} instead at {c}.",
2283 nexttoken, nexttoken.value, i, nexttoken.from);
2288 function nolinebreak(t) {
2290 if (t.line !== nexttoken.line) {
2291 warning("Line breaking error '{a}'.", t, t.value);
2297 if (token.line !== nexttoken.line) {
2298 if (!option.laxcomma) {
2300 warning("Comma warnings can be turned off with 'laxcomma'");
2301 comma.first = false;
2303 warning("Bad line breaking before '{a}'.", token, nexttoken.id);
2305 } else if (!token.comment && token.character !== nexttoken.from && option.white) {
2306 token.from += (token.character - token.from);
2307 warning("Unexpected space after '{a}'.", token, token.value);
2310 nonadjacent(token, nexttoken);
2314 // Functional constructors for making the symbols that will be inherited by
2317 function symbol(s, p) {
2319 if (!x || typeof x !== "object") {
2331 return symbol(s, 0);
2335 function stmt(s, f) {
2337 x.identifier = x.reserved = true;
2343 function blockstmt(s, f) {
2350 function reserveName(x) {
2351 var c = x.id.charAt(0);
2352 if ((c >= "a" && c <= "z") || (c >= "A" && c <= "Z")) {
2353 x.identifier = x.reserved = true;
2359 function prefix(s, f) {
2360 var x = symbol(s, 150);
2362 x.nud = (typeof f === "function") ? f : function () {
2363 this.right = expression(150);
2364 this.arity = "unary";
2365 if (this.id === "++" || this.id === "--") {
2366 if (option.plusplus) {
2367 warning("Unexpected use of '{a}'.", this, this.id);
2368 } else if ((!this.right.identifier || this.right.reserved) &&
2369 this.right.id !== "." && this.right.id !== "[") {
2370 warning("Bad operand.", this);
2379 function type(s, f) {
2387 function reserve(s, f) {
2389 x.identifier = x.reserved = true;
2394 function reservevar(s, v) {
2395 return reserve(s, function () {
2396 if (typeof v === "function") {
2404 function infix(s, f, p, w) {
2405 var x = symbol(s, p);
2407 x.led = function (left) {
2409 nobreaknonadjacent(prevtoken, token);
2410 nonadjacent(token, nexttoken);
2412 if (s === "in" && left.id === "!") {
2413 warning("Confusing use of '{a}'.", left, "!");
2415 if (typeof f === "function") {
2416 return f(left, this);
2419 this.right = expression(p);
2427 function relation(s, f) {
2428 var x = symbol(s, 100);
2429 x.led = function (left) {
2430 nobreaknonadjacent(prevtoken, token);
2431 nonadjacent(token, nexttoken);
2432 var right = expression(100);
2434 if (isIdentifier(left, "NaN") || isIdentifier(right, "NaN")) {
2435 warning("Use the isNaN function to compare with NaN.", this);
2437 f.apply(this, [left, right]);
2439 if (left.id === "!") {
2440 warning("Confusing use of '{a}'.", left, "!");
2442 if (right.id === "!") {
2443 warning("Confusing use of '{a}'.", right, "!");
2453 function isPoorRelation(node) {
2455 ((node.type === "(number)" && +node.value === 0) ||
2456 (node.type === "(string)" && node.value === "") ||
2457 (node.type === "null" && !option.eqnull) ||
2458 node.type === "true" ||
2459 node.type === "false" ||
2460 node.type === "undefined");
2464 function assignop(s) {
2465 symbol(s, 20).exps = true;
2467 return infix(s, function (left, that) {
2470 if (predefined[left.value] === false &&
2471 scope[left.value]["(global)"] === true) {
2472 warning("Read only.", left);
2473 } else if (left["function"]) {
2474 warning("'{a}' is a function.", left, left.value);
2478 if (option.esnext && funct[left.value] === "const") {
2479 warning("Attempting to override '{a}' which is a constant", left, left.value);
2482 if (left.id === "." || left.id === "[") {
2483 if (!left.left || left.left.value === "arguments") {
2484 warning("Bad assignment.", that);
2486 that.right = expression(19);
2488 } else if (left.identifier && !left.reserved) {
2489 if (funct[left.value] === "exception") {
2490 warning("Do not assign to the exception parameter.", left);
2492 that.right = expression(19);
2496 if (left === syntax["function"]) {
2498 "Expected an identifier in an assignment and instead saw a function invocation.",
2503 error("Bad assignment.", that);
2508 function bitwise(s, f, p) {
2509 var x = symbol(s, p);
2511 x.led = (typeof f === "function") ? f : function (left) {
2512 if (option.bitwise) {
2513 warning("Unexpected use of '{a}'.", this, this.id);
2516 this.right = expression(p);
2523 function bitwiseassignop(s) {
2524 symbol(s, 20).exps = true;
2525 return infix(s, function (left, that) {
2526 if (option.bitwise) {
2527 warning("Unexpected use of '{a}'.", that, that.id);
2529 nonadjacent(prevtoken, token);
2530 nonadjacent(token, nexttoken);
2532 if (left.id === "." || left.id === "[" ||
2533 (left.identifier && !left.reserved)) {
2537 if (left === syntax["function"]) {
2539 "Expected an identifier in an assignment, and instead saw a function invocation.",
2544 error("Bad assignment.", that);
2549 function suffix(s) {
2550 var x = symbol(s, 150);
2551 x.led = function (left) {
2552 if (option.plusplus) {
2553 warning("Unexpected use of '{a}'.", this, this.id);
2554 } else if ((!left.identifier || left.reserved) &&
2555 left.id !== "." && left.id !== "[") {
2556 warning("Bad operand.", this);
2565 // fnparam means that this identifier is being defined as a function
2566 // argument (see identifier())
2567 function optionalidentifier(fnparam) {
2568 if (nexttoken.identifier) {
2570 if (token.reserved && !option.es5) {
2571 // `undefined` as a function param is a common pattern to protect
2572 // against the case when somebody does `undefined = true` and
2573 // help with minification. More info: https://gist.github.com/315916
2574 if (!fnparam || token.value !== "undefined") {
2575 warning("Expected an identifier and instead saw '{a}' (a reserved word).",
2583 // fnparam means that this identifier is being defined as a function
2585 function identifier(fnparam) {
2586 var i = optionalidentifier(fnparam);
2590 if (token.id === "function" && nexttoken.id === "(") {
2591 warning("Missing name in function declaration.");
2593 error("Expected an identifier and instead saw '{a}'.",
2594 nexttoken, nexttoken.value);
2599 function reachable(s) {
2601 if (nexttoken.id !== ";" || noreach) {
2609 if (t.id !== "(endline)") {
2610 if (t.id === "function") {
2611 if (!option.latedef) {
2615 "Inner functions should be listed at the top of the outer function.", t);
2618 warning("Unreachable '{a}' after '{b}'.", t, t.value, s);
2626 function statement(noindent) {
2627 var i = indent, r, s = scope, t = nexttoken;
2634 // Is this a labelled statement?
2636 if (t.identifier && !t.reserved && peek().id === ":") {
2639 scope = Object.create(s);
2640 addlabel(t.value, "label");
2642 if (!nexttoken.labelled && nexttoken.value !== "{") {
2643 warning("Label '{a}' on {b} statement.", nexttoken, t.value, nexttoken.value);
2646 if (jx.test(t.value + ":")) {
2647 warning("Label '{a}' looks like a javascript url.", t, t.value);
2650 nexttoken.label = t.value;
2654 // Is it a lonely block?
2661 // Parse the statement.
2666 r = expression(0, true);
2668 // Look for the final semicolon.
2671 if (!option.expr && (!r || !r.exps)) {
2672 warning("Expected an assignment or function call and instead saw an expression.",
2674 } else if (option.nonew && r.id === "(" && r.left.id === "new") {
2675 warning("Do not use 'new' for side effects.", t);
2678 if (nexttoken.id === ",") {
2682 if (nexttoken.id !== ";") {
2684 // If this is the last statement in a block that ends on
2685 // the same line *and* option lastsemic is on, ignore the warning.
2686 // Otherwise, complain about missing semicolon.
2687 if (!option.lastsemic || nexttoken.id !== "}" ||
2688 nexttoken.line !== token.line) {
2689 warningAt("Missing semicolon.", token.line, token.character);
2693 adjacent(token, nexttoken);
2695 nonadjacent(token, nexttoken);
2699 // Restore the indentation.
2707 function statements(startLine) {
2710 while (!nexttoken.reach && nexttoken.id !== "(end)") {
2711 if (nexttoken.id === ";") {
2713 if (!p || p.id !== "(") {
2714 warning("Unnecessary semicolon.");
2718 a.push(statement(startLine === nexttoken.line));
2726 * read all directives
2727 * recognizes a simple form of asi, but always
2728 * warns, if it is used
2730 function directives() {
2734 if (nexttoken.id === "(string)") {
2736 if (p.id === "(endline)") {
2741 } while (pn.id === "(endline)");
2743 if (pn.id !== ";") {
2744 if (pn.id !== "(string)" && pn.id !== "(number)" &&
2745 pn.id !== "(regexp)" && pn.identifier !== true &&
2749 warning("Missing semicolon.", nexttoken);
2753 } else if (p.id === "}") {
2754 // directive with no other statements, warn about missing semicolon
2755 warning("Missing semicolon.", p);
2756 } else if (p.id !== ";") {
2762 if (directive[token.value]) {
2763 warning("Unnecessary directive \"{a}\".", token, token.value);
2766 if (token.value === "use strict") {
2767 if (!option["(explicitNewcap)"])
2768 option.newcap = true;
2769 option.undef = true;
2772 // there's no directive negation, so always set to true
2773 directive[token.value] = true;
2786 * Parses a single block. A block is a sequence of statements wrapped in
2789 * ordinary - true for everything but function bodies and try blocks.
2790 * stmt - true if block can be a single statement (e.g. in if/for/while).
2791 * isfunc - true if block is a function body
2793 function block(ordinary, stmt, isfunc) {
2796 old_indent = indent,
2805 if (!ordinary || !option.funcscope)
2806 scope = Object.create(scope);
2808 nonadjacent(token, nexttoken);
2811 var metrics = funct["(metrics)"];
2812 metrics.nestedBlockDepth += 1;
2813 metrics.verifyMaxNestedBlockDepthPerFunction();
2815 if (nexttoken.id === "{") {
2818 if (nexttoken.id !== "}") {
2819 indent += option.indent;
2820 while (!ordinary && nexttoken.from > indent) {
2821 indent += option.indent;
2826 for (d in directive) {
2827 if (is_own(directive, d)) {
2828 m[d] = directive[d];
2833 if (option.strict && funct["(context)"]["(global)"]) {
2834 if (!m["use strict"] && !directive["use strict"]) {
2835 warning("Missing \"use strict\" statement.");
2840 a = statements(line);
2842 metrics.statementCount += a.length;
2848 indent -= option.indent;
2849 if (line !== nexttoken.line) {
2852 } else if (line !== nexttoken.line) {
2856 indent = old_indent;
2857 } else if (!ordinary) {
2858 error("Expected '{a}' and instead saw '{b}'.",
2859 nexttoken, "{", nexttoken.value);
2861 if (!stmt || option.curly)
2862 warning("Expected '{a}' and instead saw '{b}'.",
2863 nexttoken, "{", nexttoken.value);
2866 indent += option.indent;
2867 // test indentation only if statement is in new line
2868 a = [statement(nexttoken.line === token.line)];
2869 indent -= option.indent;
2872 funct["(verb)"] = null;
2873 if (!ordinary || !option.funcscope) scope = s;
2875 if (ordinary && option.noempty && (!a || a.length === 0)) {
2876 warning("Empty block.");
2878 metrics.nestedBlockDepth -= 1;
2883 function countMember(m) {
2884 if (membersOnly && typeof membersOnly[m] !== "boolean") {
2885 warning("Unexpected /*member '{a}'.", token, m);
2887 if (typeof member[m] === "number") {
2895 function note_implied(token) {
2896 var name = token.value, line = token.line, a = implied[name];
2897 if (typeof a === "function") {
2904 } else if (a[a.length - 1] !== line) {
2910 // Build the syntax table by declaring the syntactic elements of the language.
2912 type("(number)", function () {
2916 type("(string)", function () {
2920 syntax["(identifier)"] = {
2921 type: "(identifier)",
2929 if (typeof s === "function") {
2930 // Protection against accidental inheritance.
2932 } else if (typeof s === "boolean") {
2934 funct = functions[0];
2940 // The name is in scope and defined in the current function.
2942 // Change 'unused' to 'var', and reject labels.
2948 funct[v] = "function";
2949 this["function"] = true;
2952 this["function"] = true;
2955 warning("'{a}' is a statement label.", token, v);
2958 } else if (funct["(global)"]) {
2959 // The name is not defined in the function. If we are in the global
2960 // scope, then we have an undefined variable.
2962 // Operators typeof and delete do not raise runtime errors even if
2963 // the base object of a reference is null so no need to display warning
2964 // if we're inside of typeof or delete.
2966 if (option.undef && typeof predefined[v] !== "boolean") {
2967 // Attempting to subscript a null reference will throw an
2968 // error, even within the typeof and delete operators
2969 if (!(anonname === "typeof" || anonname === "delete") ||
2970 (nexttoken && (nexttoken.value === "." || nexttoken.value === "["))) {
2972 isundef(funct, "'{a}' is not defined.", token, v);
2976 note_implied(token);
2978 // If the name is already defined in the current
2979 // function, but not as outer, then there is a scope error.
2986 warning("'{a}' used out of scope.", token, v);
2989 warning("'{a}' is a statement label.", token, v);
2995 // If the name is defined in an outer function, make an outer entry,
2996 // and if it was unused, make it var.
2999 } else if (s === null) {
3000 warning("'{a}' is not allowed.", token, v);
3001 note_implied(token);
3002 } else if (typeof s !== "object") {
3003 // Operators typeof and delete do not raise runtime errors even
3004 // if the base object of a reference is null so no need to
3005 // display warning if we're inside of typeof or delete.
3007 // Attempting to subscript a null reference will throw an
3008 // error, even within the typeof and delete operators
3009 if (!(anonname === "typeof" || anonname === "delete") ||
3011 (nexttoken.value === "." || nexttoken.value === "["))) {
3013 isundef(funct, "'{a}' is not defined.", token, v);
3017 note_implied(token);
3022 this["function"] = true;
3024 funct[v] = s["(global)"] ? "global" : "outer";
3029 funct[v] = s["(global)"] ? "global" : "outer";
3032 funct[v] = s["(global)"] ? "global" : "outer";
3035 warning("'{a}' is a statement label.", token, v);
3043 error("Expected an operator and instead saw '{a}'.",
3044 nexttoken, nexttoken.value);
3048 type("(regexp)", function () {
3053 // ECMAScript parser
3057 delim("(end)").reach = true;
3058 delim("</").reach = true;
3062 delim("(error)").reach = true;
3063 delim("}").reach = true;
3066 delim("\"").reach = true;
3067 delim("'").reach = true;
3069 delim(":").reach = true;
3074 reserve("case").reach = true;
3076 reserve("default").reach = true;
3078 reservevar("arguments", function (x) {
3079 if (directive["use strict"] && funct["(global)"]) {
3080 warning("Strict violation.", x);
3084 reservevar("false");
3085 reservevar("Infinity");
3087 reservevar("this", function (x) {
3088 if (directive["use strict"] && !option.validthis && ((funct["(statement)"] &&
3089 funct["(name)"].charAt(0) > "Z") || funct["(global)"])) {
3090 warning("Possible strict violation.", x);
3094 reservevar("undefined");
3095 assignop("=", "assign", 20);
3096 assignop("+=", "assignadd", 20);
3097 assignop("-=", "assignsub", 20);
3098 assignop("*=", "assignmult", 20);
3099 assignop("/=", "assigndiv", 20).nud = function () {
3100 error("A regular expression literal can be confused with '/='.");
3102 assignop("%=", "assignmod", 20);
3103 bitwiseassignop("&=", "assignbitand", 20);
3104 bitwiseassignop("|=", "assignbitor", 20);
3105 bitwiseassignop("^=", "assignbitxor", 20);
3106 bitwiseassignop("<<=", "assignshiftleft", 20);
3107 bitwiseassignop(">>=", "assignshiftright", 20);
3108 bitwiseassignop(">>>=", "assignshiftrightunsigned", 20);
3109 infix("?", function (left, that) {
3111 that.right = expression(10);
3113 that["else"] = expression(10);
3117 infix("||", "or", 40);
3118 infix("&&", "and", 50);
3119 bitwise("|", "bitor", 70);
3120 bitwise("^", "bitxor", 80);
3121 bitwise("&", "bitand", 90);
3122 relation("==", function (left, right) {
3123 var eqnull = option.eqnull && (left.value === "null" || right.value === "null");
3125 if (!eqnull && option.eqeqeq)
3126 warning("Expected '{a}' and instead saw '{b}'.", this, "===", "==");
3127 else if (isPoorRelation(left))
3128 warning("Use '{a}' to compare with '{b}'.", this, "===", left.value);
3129 else if (isPoorRelation(right))
3130 warning("Use '{a}' to compare with '{b}'.", this, "===", right.value);
3135 relation("!=", function (left, right) {
3136 var eqnull = option.eqnull &&
3137 (left.value === "null" || right.value === "null");
3139 if (!eqnull && option.eqeqeq) {
3140 warning("Expected '{a}' and instead saw '{b}'.",
3142 } else if (isPoorRelation(left)) {
3143 warning("Use '{a}' to compare with '{b}'.",
3144 this, "!==", left.value);
3145 } else if (isPoorRelation(right)) {
3146 warning("Use '{a}' to compare with '{b}'.",
3147 this, "!==", right.value);
3156 bitwise("<<", "shiftleft", 120);
3157 bitwise(">>", "shiftright", 120);
3158 bitwise(">>>", "shiftrightunsigned", 120);
3159 infix("in", "in", 120);
3160 infix("instanceof", "instanceof", 120);
3161 infix("+", function (left, that) {
3162 var right = expression(130);
3163 if (left && right && left.id === "(string)" && right.id === "(string)") {
3164 left.value += right.value;
3165 left.character = right.character;
3166 if (!option.scripturl && jx.test(left.value)) {
3167 warning("JavaScript URL.", left);
3176 prefix("+++", function () {
3177 warning("Confusing pluses.");
3178 this.right = expression(150);
3179 this.arity = "unary";
3182 infix("+++", function (left) {
3183 warning("Confusing pluses.");
3185 this.right = expression(130);
3188 infix("-", "sub", 130);
3190 prefix("---", function () {
3191 warning("Confusing minuses.");
3192 this.right = expression(150);
3193 this.arity = "unary";
3196 infix("---", function (left) {
3197 warning("Confusing minuses.");
3199 this.right = expression(130);
3202 infix("*", "mult", 140);
3203 infix("/", "div", 140);
3204 infix("%", "mod", 140);
3206 suffix("++", "postinc");
3207 prefix("++", "preinc");
3208 syntax["++"].exps = true;
3210 suffix("--", "postdec");
3211 prefix("--", "predec");
3212 syntax["--"].exps = true;
3213 prefix("delete", function () {
3214 var p = expression(0);
3215 if (!p || (p.id !== "." && p.id !== "[")) {
3216 warning("Variables should not be deleted.");
3222 prefix("~", function () {
3223 if (option.bitwise) {
3224 warning("Unexpected '{a}'.", this, "~");
3230 prefix("!", function () {
3231 this.right = expression(150);
3232 this.arity = "unary";
3233 if (bang[this.right.id] === true) {
3234 warning("Confusing use of '{a}'.", this, "!");
3238 prefix("typeof", "typeof");
3239 prefix("new", function () {
3240 var c = expression(155), i;
3241 if (c && c.id !== "function") {
3250 warning("Do not use {a} as a constructor.", prevtoken, c.value);
3254 warning("The Function constructor is eval.");
3261 if (c.id !== "function") {
3262 i = c.value.substr(0, 1);
3263 if (option.newcap && (i < "A" || i > "Z") && !is_own(global, c.value)) {
3264 warning("A constructor name should start with an uppercase letter.",
3270 if (c.id !== "." && c.id !== "[" && c.id !== "(") {
3271 warning("Bad constructor.", token);
3275 if (!option.supernew)
3276 warning("Weird construction. Delete 'new'.", this);
3278 adjacent(token, nexttoken);
3279 if (nexttoken.id !== "(" && !option.supernew) {
3280 warning("Missing '()' invoking a constructor.",
3281 token, token.value);
3286 syntax["new"].exps = true;
3288 prefix("void").exps = true;
3290 infix(".", function (left, that) {
3291 adjacent(prevtoken, token);
3293 var m = identifier();
3294 if (typeof m === "string") {
3299 if (left && left.value === "arguments" && (m === "callee" || m === "caller")) {
3301 warning("Avoid arguments.{a}.", left, m);
3302 else if (directive["use strict"])
3303 error("Strict violation.");
3304 } else if (!option.evil && left && left.value === "document" &&
3305 (m === "write" || m === "writeln")) {
3306 warning("document.write can be a form of eval.", left);
3308 if (!option.evil && (m === "eval" || m === "execScript")) {
3309 warning("eval is evil.");
3314 infix("(", function (left, that) {
3315 if (prevtoken.id !== "}" && prevtoken.id !== ")") {
3316 nobreak(prevtoken, token);
3319 if (option.immed && !left.immed && left.id === "function") {
3320 warning("Wrap an immediate function invocation in parentheses " +
3321 "to assist the reader in understanding that the expression " +
3322 "is the result of a function, and not the function itself.");
3327 if (left.type === "(identifier)") {
3328 if (left.value.match(/^[A-Z]([A-Z0-9_$]*[a-z][A-Za-z0-9_$]*)?$/)) {
3329 if ("Number String Boolean Date Object".indexOf(left.value) === -1) {
3330 if (left.value === "Math") {
3331 warning("Math is not a function.", left);
3332 } else if (option.newcap) {
3333 warning("Missing 'new' prefix when invoking a constructor.", left);
3339 if (nexttoken.id !== ")") {
3341 p[p.length] = expression(10);
3343 if (nexttoken.id !== ",") {
3350 nospace(prevtoken, token);
3351 if (typeof left === "object") {
3352 if (left.value === "parseInt" && n === 1) {
3353 warning("Missing radix parameter.", token);
3356 if (left.value === "eval" || left.value === "Function" ||
3357 left.value === "execScript") {
3358 warning("eval is evil.", left);
3360 if (p[0] && [0].id === "(string)") {
3361 addInternalSrc(left, p[0].value);
3363 } else if (p[0] && p[0].id === "(string)" &&
3364 (left.value === "setTimeout" ||
3365 left.value === "setInterval")) {
3367 "Implied eval is evil. Pass a function instead of a string.", left);
3368 addInternalSrc(left, p[0].value);
3370 // window.setTimeout/setInterval
3371 } else if (p[0] && p[0].id === "(string)" &&
3372 left.value === "." &&
3373 left.left.value === "window" &&
3374 (left.right === "setTimeout" ||
3375 left.right === "setInterval")) {
3377 "Implied eval is evil. Pass a function instead of a string.", left);
3378 addInternalSrc(left, p[0].value);
3381 if (!left.identifier && left.id !== "." && left.id !== "[" &&
3382 left.id !== "(" && left.id !== "&&" && left.id !== "||" &&
3384 warning("Bad invocation.", left);
3389 }, 155, true).exps = true;
3391 prefix("(", function () {
3393 if (nexttoken.id === "function") {
3394 nexttoken.immed = true;
3396 var v = expression(0);
3398 nospace(prevtoken, token);
3399 if (option.immed && v.id === "function") {
3400 if (nexttoken.id !== "(" &&
3401 (nexttoken.id !== "." || (peek().value !== "call" && peek().value !== "apply"))) {
3403 "Do not wrap function literals in parens unless they are to be immediately invoked.",
3411 infix("[", function (left, that) {
3412 nobreak(prevtoken, token);
3414 var e = expression(0), s;
3415 if (e && e.type === "(string)") {
3416 if (!option.evil && (e.value === "eval" || e.value === "execScript")) {
3417 warning("eval is evil.", that);
3419 countMember(e.value);
3420 if (!option.sub && ix.test(e.value)) {
3421 s = syntax[e.value];
3422 if (!s || !s.reserved) {
3423 warning("['{a}'] is better written in dot notation.",
3424 prevtoken, e.value);
3429 nospace(prevtoken, token);
3435 prefix("[", function () {
3436 var b = token.line !== nexttoken.line;
3439 indent += option.indent;
3440 if (nexttoken.from === indent + option.indent) {
3441 indent += option.indent;
3444 while (nexttoken.id !== "(end)") {
3445 while (nexttoken.id === ",") {
3447 warning("Extra comma.");
3450 if (nexttoken.id === "]") {
3453 if (b && token.line !== nexttoken.line) {
3456 this.first.push(expression(10));
3457 if (nexttoken.id === ",") {
3459 if (nexttoken.id === "]" && !option.es5) {
3460 warning("Extra comma.", token);
3468 indent -= option.indent;
3476 function property_name() {
3477 var id = optionalidentifier(true);
3479 if (nexttoken.id === "(string)") {
3480 id = nexttoken.value;
3482 } else if (nexttoken.id === "(number)") {
3483 id = nexttoken.value.toString();
3491 function functionparams() {
3492 var next = nexttoken;
3499 if (nexttoken.id === ")") {
3505 ident = identifier(true);
3507 addlabel(ident, "unused", token);
3508 if (nexttoken.id === ",") {
3512 nospace(prevtoken, token);
3519 function doFunction(name, statement) {
3521 var oldOption = option;
3522 var oldScope = scope;
3524 option = Object.create(option);
3525 scope = Object.create(scope);
3528 "(name)" : name || "\"" + anonname + "\"",
3529 "(line)" : nexttoken.line,
3530 "(character)": nexttoken.character,
3531 "(context)" : funct,
3534 "(metrics)" : createMetrics(nexttoken),
3536 "(statement)": statement,
3541 token.funct = funct;
3543 functions.push(funct);
3546 addlabel(name, "function");
3549 funct["(params)"] = functionparams();
3550 funct["(metrics)"].verifyMaxParametersPerFunction(funct["(params)"]);
3552 block(false, false, true);
3554 funct["(metrics)"].verifyMaxStatementsPerFunction();
3555 funct["(metrics)"].verifyMaxComplexityPerFunction();
3559 funct["(last)"] = token.line;
3560 funct["(lastcharacter)"] = token.character;
3561 funct = funct["(context)"];
3566 function createMetrics(functionStartToken) {
3569 nestedBlockDepth: -1,
3571 verifyMaxStatementsPerFunction: function () {
3572 if (option.maxstatements &&
3573 this.statementCount > option.maxstatements) {
3574 var message = "Too many statements per function (" + this.statementCount + ").";
3575 warning(message, functionStartToken);
3579 verifyMaxParametersPerFunction: function (params) {
3580 params = params || [];
3582 if (option.maxparams && params.length > option.maxparams) {
3583 var message = "Too many parameters per function (" + params.length + ").";
3584 warning(message, functionStartToken);
3588 verifyMaxNestedBlockDepthPerFunction: function () {
3589 if (option.maxdepth &&
3590 this.nestedBlockDepth > 0 &&
3591 this.nestedBlockDepth === option.maxdepth + 1) {
3592 var message = "Blocks are nested too deeply (" + this.nestedBlockDepth + ").";
3597 verifyMaxComplexityPerFunction: function () {
3598 var max = option.maxcomplexity;
3599 var cc = this.ComplexityCount;
3600 if (max && cc > max) {
3601 var message = "Cyclomatic complexity is too high per function (" + cc + ").";
3602 warning(message, functionStartToken);
3608 function increaseComplexityCount() {
3609 funct["(metrics)"].ComplexityCount += 1;
3614 x.nud = function () {
3616 var props = {}; // All properties, including accessors
3618 function saveProperty(name, token) {
3619 if (props[name] && is_own(props, name))
3620 warning("Duplicate member '{a}'.", nexttoken, i);
3624 props[name].basic = true;
3625 props[name].basicToken = token;
3628 function saveSetter(name, token) {
3629 if (props[name] && is_own(props, name)) {
3630 if (props[name].basic || props[name].setter)
3631 warning("Duplicate member '{a}'.", nexttoken, i);
3636 props[name].setter = true;
3637 props[name].setterToken = token;
3640 function saveGetter(name) {
3641 if (props[name] && is_own(props, name)) {
3642 if (props[name].basic || props[name].getter)
3643 warning("Duplicate member '{a}'.", nexttoken, i);
3648 props[name].getter = true;
3649 props[name].getterToken = token;
3652 b = token.line !== nexttoken.line;
3654 indent += option.indent;
3655 if (nexttoken.from === indent + option.indent) {
3656 indent += option.indent;
3660 if (nexttoken.id === "}") {
3666 if (nexttoken.value === "get" && peek().id !== ":") {
3669 error("get/set are ES5 features.");
3671 i = property_name();
3673 error("Missing property name.");
3677 adjacent(token, nexttoken);
3681 warning("Unexpected parameter '{a}' in get {b} function.", t, p[0], i);
3683 adjacent(token, nexttoken);
3684 } else if (nexttoken.value === "set" && peek().id !== ":") {
3687 error("get/set are ES5 features.");
3689 i = property_name();
3691 error("Missing property name.");
3693 saveSetter(i, nexttoken);
3695 adjacent(token, nexttoken);
3698 if (!p || p.length !== 1) {
3699 warning("Expected a single parameter in set {a} function.", t, i);
3702 i = property_name();
3703 saveProperty(i, nexttoken);
3704 if (typeof i !== "string") {
3708 nonadjacent(token, nexttoken);
3713 if (nexttoken.id === ",") {
3715 if (nexttoken.id === ",") {
3716 warning("Extra comma.", token);
3717 } else if (nexttoken.id === "}" && !option.es5) {
3718 warning("Extra comma.", token);
3725 indent -= option.indent;
3730 // Check for lonely setters if in the ES5 mode.
3732 for (var name in props) {
3733 if (is_own(props, name) && props[name].setter && !props[name].getter) {
3734 warning("Setter is defined without getter.", props[name].setterToken);
3740 x.fud = function () {
3741 error("Expected to see a statement and instead saw a block.", token);
3745 // This Function is called when esnext option is set to true
3746 // it adds the `const` statement to JSHINT
3748 useESNextSyntax = function () {
3749 var conststatement = stmt("const", function (prefix) {
3750 var id, name, value;
3754 nonadjacent(token, nexttoken);
3756 if (funct[id] === "const") {
3757 warning("const '" + id + "' has already been declared");
3759 if (funct["(global)"] && predefined[id] === false) {
3760 warning("Redefinition of '{a}'.", token, id);
3762 addlabel(id, "const");
3767 this.first.push(token);
3769 if (nexttoken.id !== "=") {
3771 "'{a}' is initialized to 'undefined'.", token, id);
3774 if (nexttoken.id === "=") {
3775 nonadjacent(token, nexttoken);
3777 nonadjacent(token, nexttoken);
3778 if (nexttoken.id === "undefined") {
3779 warning("It is not necessary to initialize " +
3780 "'{a}' to 'undefined'.", token, id);
3782 if (peek(0).id === "=" && nexttoken.identifier) {
3783 error("Constant {a} was not declared correctly.",
3784 nexttoken, nexttoken.value);
3786 value = expression(0);
3790 if (nexttoken.id !== ",") {
3797 conststatement.exps = true;
3800 var varstatement = stmt("var", function (prefix) {
3801 // JavaScript does not have block scope. It only has function scope. So,
3802 // declaring a variable in a block can have unexpected consequences.
3803 var id, name, value;
3805 if (funct["(onevar)"] && option.onevar) {
3806 warning("Too many var statements.");
3807 } else if (!funct["(global)"]) {
3808 funct["(onevar)"] = true;
3814 nonadjacent(token, nexttoken);
3817 if (option.esnext && funct[id] === "const") {
3818 warning("const '" + id + "' has already been declared");
3821 if (funct["(global)"] && predefined[id] === false) {
3822 warning("Redefinition of '{a}'.", token, id);
3825 addlabel(id, "unused", token);
3832 this.first.push(token);
3834 if (nexttoken.id === "=") {
3835 nonadjacent(token, nexttoken);
3837 nonadjacent(token, nexttoken);
3838 if (nexttoken.id === "undefined") {
3839 warning("It is not necessary to initialize '{a}' to 'undefined'.", token, id);
3841 if (peek(0).id === "=" && nexttoken.identifier) {
3842 error("Variable {a} was not declared correctly.",
3843 nexttoken, nexttoken.value);
3845 value = expression(0);
3848 if (nexttoken.id !== ",") {
3855 varstatement.exps = true;
3857 blockstmt("function", function () {
3859 warning("Function declarations should not be placed in blocks. " +
3860 "Use a function expression or move the statement to the top of " +
3861 "the outer function.", token);
3864 var i = identifier();
3865 if (option.esnext && funct[i] === "const") {
3866 warning("const '" + i + "' has already been declared");
3868 adjacent(token, nexttoken);
3869 addlabel(i, "unction", token);
3871 doFunction(i, { statement: true });
3872 if (nexttoken.id === "(" && nexttoken.line === token.line) {
3874 "Function declarations are not invocable. Wrap the whole function invocation in parens.");
3879 prefix("function", function () {
3880 var i = optionalidentifier();
3882 adjacent(token, nexttoken);
3884 nonadjacent(token, nexttoken);
3887 if (!option.loopfunc && funct["(loopage)"]) {
3888 warning("Don't make functions within a loop.");
3893 blockstmt("if", function () {
3895 increaseComplexityCount();
3897 nonadjacent(this, t);
3900 if (nexttoken.id === "=") {
3902 warning("Expected a conditional expression and instead saw an assignment.");
3907 nospace(prevtoken, token);
3909 if (nexttoken.id === "else") {
3910 nonadjacent(token, nexttoken);
3912 if (nexttoken.id === "if" || nexttoken.id === "switch") {
3921 blockstmt("try", function () {
3924 function doCatch() {
3925 var oldScope = scope;
3929 nonadjacent(token, nexttoken);
3932 scope = Object.create(oldScope);
3934 e = nexttoken.value;
3935 if (nexttoken.type !== "(identifier)") {
3937 warning("Expected an identifier and instead saw '{a}'.", nexttoken, e);
3944 "(name)" : "(catch)",
3945 "(line)" : nexttoken.line,
3946 "(character)": nexttoken.character,
3947 "(context)" : funct,
3948 "(breakage)" : funct["(breakage)"],
3949 "(loopage)" : funct["(loopage)"],
3951 "(statement)": false,
3952 "(metrics)" : createMetrics(nexttoken),
3958 addlabel(e, "exception");
3961 token.funct = funct;
3962 functions.push(funct);
3968 funct["(last)"] = token.line;
3969 funct["(lastcharacter)"] = token.character;
3970 funct = funct["(context)"];
3975 if (nexttoken.id === "catch") {
3976 increaseComplexityCount();
3981 if (nexttoken.id === "finally") {
3986 error("Expected '{a}' and instead saw '{b}'.",
3987 nexttoken, "catch", nexttoken.value);
3993 blockstmt("while", function () {
3995 funct["(breakage)"] += 1;
3996 funct["(loopage)"] += 1;
3997 increaseComplexityCount();
3999 nonadjacent(this, t);
4002 if (nexttoken.id === "=") {
4004 warning("Expected a conditional expression and instead saw an assignment.");
4009 nospace(prevtoken, token);
4011 funct["(breakage)"] -= 1;
4012 funct["(loopage)"] -= 1;
4016 blockstmt("with", function () {
4018 if (directive["use strict"]) {
4019 error("'with' is not allowed in strict mode.", token);
4020 } else if (!option.withstmt) {
4021 warning("Don't use 'with'.", token);
4025 nonadjacent(this, t);
4029 nospace(prevtoken, token);
4035 blockstmt("switch", function () {
4038 funct["(breakage)"] += 1;
4040 nonadjacent(this, t);
4042 this.condition = expression(20);
4044 nospace(prevtoken, token);
4045 nonadjacent(token, nexttoken);
4048 nonadjacent(token, nexttoken);
4049 indent += option.indent;
4052 switch (nexttoken.id) {
4054 switch (funct["(verb)"]) {
4063 // You can tell JSHint that you don't use break intentionally by
4064 // adding a comment /* falls through */ on a line just before
4066 if (!ft.test(lines[nexttoken.line - 2])) {
4068 "Expected a 'break' statement before 'case'.",
4072 indentation(-option.indent);
4074 this.cases.push(expression(20));
4075 increaseComplexityCount();
4078 funct["(verb)"] = "case";
4081 switch (funct["(verb)"]) {
4088 if (!ft.test(lines[nexttoken.line - 2])) {
4090 "Expected a 'break' statement before 'default'.",
4094 indentation(-option.indent);
4100 indent -= option.indent;
4103 if (this.cases.length === 1 || this.condition.id === "true" ||
4104 this.condition.id === "false") {
4105 if (!option.onecase)
4106 warning("This 'switch' should be an 'if'.", this);
4108 funct["(breakage)"] -= 1;
4109 funct["(verb)"] = undefined;
4112 error("Missing '{a}'.", nexttoken, "}");
4118 error("Each value should have its own case label.");
4125 error("Missing ':' on a case clause.", token);
4129 if (token.id === ":") {
4131 error("Unexpected '{a}'.", token, ":");
4134 error("Expected '{a}' and instead saw '{b}'.",
4135 nexttoken, "case", nexttoken.value);
4143 stmt("debugger", function () {
4144 if (!option.debug) {
4145 warning("All 'debugger' statements should be removed.");
4151 var x = stmt("do", function () {
4152 funct["(breakage)"] += 1;
4153 funct["(loopage)"] += 1;
4154 increaseComplexityCount();
4156 this.first = block(true);
4159 nonadjacent(token, t);
4163 if (nexttoken.id === "=") {
4165 warning("Expected a conditional expression and instead saw an assignment.");
4170 nospace(prevtoken, token);
4171 funct["(breakage)"] -= 1;
4172 funct["(loopage)"] -= 1;
4179 blockstmt("for", function () {
4180 var s, t = nexttoken;
4181 funct["(breakage)"] += 1;
4182 funct["(loopage)"] += 1;
4183 increaseComplexityCount();
4185 nonadjacent(this, t);
4187 if (peek(nexttoken.id === "var" ? 1 : 0).id === "in") {
4188 if (nexttoken.id === "var") {
4190 varstatement.fud.call(varstatement, true);
4192 switch (funct[nexttoken.value]) {
4194 funct[nexttoken.value] = "var";
4199 warning("Bad for in variable '{a}'.",
4200 nexttoken, nexttoken.value);
4207 s = block(true, true);
4208 if (option.forin && s && (s.length > 1 || typeof s[0] !== "object" ||
4209 s[0].value !== "if")) {
4210 warning("The body of a for in should be wrapped in an if statement to filter " +
4211 "unwanted properties from the prototype.", this);
4213 funct["(breakage)"] -= 1;
4214 funct["(loopage)"] -= 1;
4217 if (nexttoken.id !== ";") {
4218 if (nexttoken.id === "var") {
4220 varstatement.fud.call(varstatement);
4223 expression(0, "for");
4224 if (nexttoken.id !== ",") {
4233 if (nexttoken.id !== ";") {
4235 if (nexttoken.id === "=") {
4237 warning("Expected a conditional expression and instead saw an assignment.");
4244 if (nexttoken.id === ";") {
4245 error("Expected '{a}' and instead saw '{b}'.",
4246 nexttoken, ")", ";");
4248 if (nexttoken.id !== ")") {
4250 expression(0, "for");
4251 if (nexttoken.id !== ",") {
4258 nospace(prevtoken, token);
4260 funct["(breakage)"] -= 1;
4261 funct["(loopage)"] -= 1;
4267 stmt("break", function () {
4268 var v = nexttoken.value;
4270 if (funct["(breakage)"] === 0)
4271 warning("Unexpected '{a}'.", nexttoken, this.value);
4276 if (nexttoken.id !== ";") {
4277 if (token.line === nexttoken.line) {
4278 if (funct[v] !== "label") {
4279 warning("'{a}' is not a statement label.", nexttoken, v);
4280 } else if (scope[v] !== funct) {
4281 warning("'{a}' is out of scope.", nexttoken, v);
4283 this.first = nexttoken;
4292 stmt("continue", function () {
4293 var v = nexttoken.value;
4295 if (funct["(breakage)"] === 0)
4296 warning("Unexpected '{a}'.", nexttoken, this.value);
4301 if (nexttoken.id !== ";") {
4302 if (token.line === nexttoken.line) {
4303 if (funct[v] !== "label") {
4304 warning("'{a}' is not a statement label.", nexttoken, v);
4305 } else if (scope[v] !== funct) {
4306 warning("'{a}' is out of scope.", nexttoken, v);
4308 this.first = nexttoken;
4311 } else if (!funct["(loopage)"]) {
4312 warning("Unexpected '{a}'.", nexttoken, this.value);
4314 reachable("continue");
4319 stmt("return", function () {
4320 if (this.line === nexttoken.line) {
4321 if (nexttoken.id === "(regexp)")
4322 warning("Wrap the /regexp/ literal in parens to disambiguate the slash operator.");
4324 if (nexttoken.id !== ";" && !nexttoken.reach) {
4325 nonadjacent(token, nexttoken);
4326 if (peek().value === "=" && !option.boss) {
4327 warningAt("Did you mean to return a conditional instead of an assignment?",
4328 token.line, token.character + 1);
4330 this.first = expression(0);
4332 } else if (!option.asi) {
4333 nolinebreak(this); // always warn (Line breaking error)
4335 reachable("return");
4340 stmt("throw", function () {
4342 nonadjacent(token, nexttoken);
4343 this.first = expression(20);
4348 // Superfluous reserved words
4360 reserve("implements");
4361 reserve("interface");
4364 reserve("protected");
4371 function jsonValue() {
4373 function jsonObject() {
4374 var o = {}, t = nexttoken;
4376 if (nexttoken.id !== "}") {
4378 if (nexttoken.id === "(end)") {
4379 error("Missing '}' to match '{' from line {a}.",
4381 } else if (nexttoken.id === "}") {
4382 warning("Unexpected comma.", token);
4384 } else if (nexttoken.id === ",") {
4385 error("Unexpected comma.", nexttoken);
4386 } else if (nexttoken.id !== "(string)") {
4387 warning("Expected a string and instead saw {a}.",
4388 nexttoken, nexttoken.value);
4390 if (o[nexttoken.value] === true) {
4391 warning("Duplicate key '{a}'.",
4392 nexttoken, nexttoken.value);
4393 } else if ((nexttoken.value === "__proto__" &&
4394 !option.proto) || (nexttoken.value === "__iterator__" &&
4395 !option.iterator)) {
4396 warning("The '{a}' key may produce unexpected results.",
4397 nexttoken, nexttoken.value);
4399 o[nexttoken.value] = true;
4404 if (nexttoken.id !== ",") {
4413 function jsonArray() {
4416 if (nexttoken.id !== "]") {
4418 if (nexttoken.id === "(end)") {
4419 error("Missing ']' to match '[' from line {a}.",
4421 } else if (nexttoken.id === "]") {
4422 warning("Unexpected comma.", token);
4424 } else if (nexttoken.id === ",") {
4425 error("Unexpected comma.", nexttoken);
4428 if (nexttoken.id !== ",") {
4437 switch (nexttoken.id) {
4453 if (token.character !== nexttoken.from) {
4454 warning("Unexpected space after '-'.", token);
4456 adjacent(token, nexttoken);
4457 advance("(number)");
4460 error("Expected a JSON value.", nexttoken);
4465 // The actual JSHINT function itself.
4466 var itself = function (s, o, g) {
4469 var newOptionObj = {};
4472 JSHINT.scope = o.scope;
4476 JSHINT.internals = [];
4477 JSHINT.blacklist = {};
4478 JSHINT.scope = "(main)";
4481 predefined = Object.create(standard);
4482 declared = Object.create(null);
4483 combine(predefined, g || {});
4488 if (!Array.isArray(a) && typeof a === "object") {
4491 a.forEach(function (item) {
4493 if (item[0] === "-") {
4494 slice = item.slice(1);
4495 JSHINT.blacklist[slice] = slice;
4497 predefined[item] = true;
4502 optionKeys = Object.keys(o);
4503 for (x = 0; x < optionKeys.length; x++) {
4504 newOptionObj[optionKeys[x]] = o[optionKeys[x]];
4506 if (optionKeys[x] === "newcap" && o[optionKeys[x]] === false)
4507 newOptionObj["(explicitNewcap)"] = true;
4509 if (optionKeys[x] === "indent")
4510 newOptionObj.white = true;
4514 option = newOptionObj;
4516 option.indent = option.indent || 4;
4517 option.maxerr = option.maxerr || 50;
4520 for (i = 0; i < option.indent; i += 1) {
4524 global = Object.create(predefined);
4528 "(name)": "(global)",
4533 "(metrics)": createMetrics(nexttoken)
4535 functions = [funct];
4548 if (!isString(s) && !Array.isArray(s)) {
4549 errorAt("Input is neither a string nor an array of strings.", 0);
4553 if (isString(s) && /^\s*$/g.test(s)) {
4554 errorAt("Input is an empty string.", 0);
4558 if (s.length === 0) {
4559 errorAt("Input is an empty array.", 0);
4568 prevtoken = token = nexttoken = syntax["(begin)"];
4571 for (var name in o) {
4572 if (is_own(o, name)) {
4573 checkOption(name, token);
4579 // combine the passed globals after we've assumed all our options
4580 combine(predefined, g || {});
4584 quotmark = undefined;
4588 switch (nexttoken.id) {
4591 option.laxbreak = true;
4597 if (directive["use strict"] && !option.globalstrict) {
4598 warning("Use the function form of \"use strict\".", prevtoken);
4603 advance((nexttoken && nexttoken.value !== ".") ? "(end)" : undefined);
4605 var markDefined = function (name, context) {
4607 if (typeof context[name] === "string") {
4608 // JSHINT marks unused variables as 'unused' and
4609 // unused function declaration as 'unction'. This
4610 // code changes such instances back 'var' and
4611 // 'closure' so that the code in JSHINT.data()
4612 // doesn't think they're unused.
4614 if (context[name] === "unused")
4615 context[name] = "var";
4616 else if (context[name] === "unction")
4617 context[name] = "closure";
4622 context = context["(context)"];
4628 var clearImplied = function (name, line) {
4632 var newImplied = [];
4633 for (var i = 0; i < implied[name].length; i += 1) {
4634 if (implied[name][i] !== line)
4635 newImplied.push(implied[name][i]);
4638 if (newImplied.length === 0)
4639 delete implied[name];
4641 implied[name] = newImplied;
4644 var warnUnused = function (name, token) {
4645 var line = token.line;
4646 var chr = token.character;
4649 warningAt("'{a}' is defined but never used.", line, chr, name);
4658 var checkUnused = function (func, key) {
4659 var type = func[key];
4660 var token = func["(tokens)"][key];
4662 if (key.charAt(0) === "(")
4665 if (type !== "unused" && type !== "unction")
4668 // Params are checked separately from other variables.
4669 if (func["(params)"] && func["(params)"].indexOf(key) !== -1)
4672 warnUnused(key, token);
4675 // Check queued 'x is not defined' instances to see if they're still undefined.
4676 for (i = 0; i < JSHINT.undefs.length; i += 1) {
4677 k = JSHINT.undefs[i].slice(0);
4679 if (markDefined(k[2].value, k[0])) {
4680 clearImplied(k[2].value, k[2].line);
4682 warning.apply(warning, k.slice(1));
4686 functions.forEach(function (func) {
4687 for (var key in func) {
4688 if (is_own(func, key)) {
4689 checkUnused(func, key);
4693 if (!func["(params)"])
4696 var params = func["(params)"].slice();
4697 var param = params.pop();
4703 // 'undefined' is a special case for (function (window, undefined) { ... })();
4706 if (param === "undefined")
4709 if (type !== "unused" && type !== "unction")
4712 warnUnused(param, func["(tokens)"][param]);
4713 param = params.pop();
4717 for (var key in declared) {
4718 if (is_own(declared, key) && !is_own(global, key)) {
4719 warnUnused(key, declared[key]);
4724 var nt = nexttoken || {};
4725 JSHINT.errors.push({
4728 line : e.line || nt.line,
4729 character : e.character || nt.from
4734 // Loop over the listed "internals", and check them as well.
4736 if (JSHINT.scope === "(main)") {
4739 for (i = 0; i < JSHINT.internals.length; i += 1) {
4740 k = JSHINT.internals[i];
4742 itself(k.value, o, g);
4746 return JSHINT.errors.length === 0;
4750 itself.data = function () {
4757 var fu, f, i, j, n, globals;
4759 if (itself.errors.length) {
4760 data.errors = itself.errors;
4767 for (n in implied) {
4768 if (is_own(implied, n)) {
4776 if (implieds.length > 0) {
4777 data.implieds = implieds;
4780 if (urls.length > 0) {
4784 globals = Object.keys(scope);
4785 if (globals.length > 0) {
4786 data.globals = globals;
4789 for (i = 1; i < functions.length; i += 1) {
4793 for (j = 0; j < functionicity.length; j += 1) {
4794 fu[functionicity[j]] = [];
4797 for (j = 0; j < functionicity.length; j += 1) {
4798 if (fu[functionicity[j]].length === 0) {
4799 delete fu[functionicity[j]];
4803 fu.name = f["(name)"];
4804 fu.param = f["(params)"];
4805 fu.line = f["(line)"];
4806 fu.character = f["(character)"];
4807 fu.last = f["(last)"];
4808 fu.lastcharacter = f["(lastcharacter)"];
4809 data.functions.push(fu);
4812 if (unuseds.length > 0) {
4813 data.unused = unuseds;
4818 if (typeof member[n] === "number") {
4819 data.member = member;
4827 itself.jshint = itself;
4832 // Make JSHINT a Node module, if possible.
4833 if (typeof exports === "object" && exports) {
4834 exports.JSHINT = JSHINT;