2 * JSHint, by JSHint Community.
4 * Licensed under the same slightly modified MIT license that JSLint is.
5 * 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.
31 * JSHint was forked from 2010-12-16 edition of JSLint.
36 JSHINT is a global function. It takes two parameters.
38 var myResult = JSHINT(source, option);
40 The first parameter is either a string or an array of strings. If it is a
41 string, it will be split on '\n' or '\r'. If it is an array of strings, it
42 is assumed that each string represents one line. The source can be a
43 JavaScript text or a JSON text.
45 The second parameter is an optional object of options which control the
46 operation of JSHINT. Most of the options are booleans: They are all
47 optional and have a default value of false. One of the options, predef,
48 can be an array of names, which will be used to declare global variables,
49 or an object whose keys are used as global names, with a boolean value
50 that determines if they are assignable.
52 If it checks out, JSHINT returns true. Otherwise, it returns false.
54 If false, you can inspect JSHINT.errors to find out the problems.
55 JSHINT.errors is an array of objects containing these members:
58 line : The line (relative to 0) at which the lint was found
59 character : The character (relative to 0) at which the lint was found
61 evidence : The text line in which the problem occurred
62 raw : The raw message before the details were inserted
69 If a fatal error was found, a null will be the last element of the
72 You can request a Function Report, which shows all of the functions
73 and the parameters and vars that they use. This can be used to find
74 implied global variables and other problems. The report is in HTML and
75 can be inserted in an HTML <body>.
77 var myReport = JSHINT.report(limited);
79 If limited is true, then the report will be limited to only errors.
81 You can request a data structure which contains JSHint's results.
83 var myData = JSHINT.data();
85 It returns a structure with this form:
149 Empty arrays will not be included.
154 evil: true, nomen: false, onevar: false, regexp: false, strict: true, boss: true,
155 undef: true, maxlen: 100, indent:4
158 /*members "\b", "\t", "\n", "\f", "\r", "!=", "!==", "\"", "%", "(begin)",
159 "(breakage)", "(context)", "(error)", "(global)", "(identifier)", "(last)",
160 "(line)", "(loopage)", "(name)", "(onevar)", "(params)", "(scope)",
161 "(statement)", "(verb)", "*", "+", "++", "-", "--", "\/", "<", "<=", "==",
162 "===", ">", ">=", $, $$, $A, $F, $H, $R, $break, $continue, $w, Abstract, Ajax,
163 __filename, __dirname, ActiveXObject, Array, ArrayBuffer, ArrayBufferView, Audio,
164 Autocompleter, Assets, Boolean, Builder, Buffer, Browser, COM, CScript, Canvas,
165 CustomAnimation, Class, Control, Chain, Color, Cookie, Core, DataView, Date,
166 Debug, Draggable, Draggables, Droppables, Document, DomReady, DOMReady, Drag,
167 E, Enumerator, Enumerable, Element, Elements, Error, Effect, EvalError, Event,
168 Events, FadeAnimation, Field, Flash, Float32Array, Float64Array, Form,
169 FormField, Frame, FormData, Function, Fx, GetObject, Group, Hash, HotKey,
170 HTMLElement, HTMLAnchorElement, HTMLBaseElement, HTMLBlockquoteElement,
171 HTMLBodyElement, HTMLBRElement, HTMLButtonElement, HTMLCanvasElement, HTMLDirectoryElement,
172 HTMLDivElement, HTMLDListElement, HTMLFieldSetElement,
173 HTMLFontElement, HTMLFormElement, HTMLFrameElement, HTMLFrameSetElement,
174 HTMLHeadElement, HTMLHeadingElement, HTMLHRElement, HTMLHtmlElement,
175 HTMLIFrameElement, HTMLImageElement, HTMLInputElement, HTMLIsIndexElement,
176 HTMLLabelElement, HTMLLayerElement, HTMLLegendElement, HTMLLIElement,
177 HTMLLinkElement, HTMLMapElement, HTMLMenuElement, HTMLMetaElement,
178 HTMLModElement, HTMLObjectElement, HTMLOListElement, HTMLOptGroupElement,
179 HTMLOptionElement, HTMLParagraphElement, HTMLParamElement, HTMLPreElement,
180 HTMLQuoteElement, HTMLScriptElement, HTMLSelectElement, HTMLStyleElement,
181 HtmlTable, HTMLTableCaptionElement, HTMLTableCellElement, HTMLTableColElement,
182 HTMLTableElement, HTMLTableRowElement, HTMLTableSectionElement,
183 HTMLTextAreaElement, HTMLTitleElement, HTMLUListElement, HTMLVideoElement,
184 Iframe, IframeShim, Image, Int16Array, Int32Array, Int8Array,
185 Insertion, InputValidator, JSON, Keyboard, Locale, LN10, LN2, LOG10E, LOG2E,
186 MAX_VALUE, MIN_VALUE, Mask, Math, MenuItem, MoveAnimation, MooTools, Native,
187 NEGATIVE_INFINITY, Number, Object, ObjectRange, Option, Options, OverText, PI,
188 POSITIVE_INFINITY, PeriodicalExecuter, Point, Position, Prototype, RangeError,
189 Rectangle, ReferenceError, RegExp, ResizeAnimation, Request, RotateAnimation,
190 SQRT1_2, SQRT2, ScrollBar, ScriptEngine, ScriptEngineBuildVersion,
191 ScriptEngineMajorVersion, ScriptEngineMinorVersion, Scriptaculous, Scroller,
192 Slick, Slider, Selector, SharedWorker, String, Style, SyntaxError, Sortable, Sortables,
193 SortableObserver, Sound, Spinner, System, Swiff, Text, TextArea, Template,
194 Timer, Tips, Type, TypeError, Toggle, Try, "use strict", unescape, URI, URIError, URL,
195 VBArray, WSH, WScript, XDomainRequest, Web, Window, XMLDOM, XMLHttpRequest, XPathEvaluator,
196 XPathException, XPathExpression, XPathNamespace, XPathNSResolver, XPathResult, "\\", a,
197 addEventListener, address, alert, apply, applicationCache, arguments, arity,
198 asi, b, bitwise, block, blur, boolOptions, boss, browser, c, call, callee,
199 caller, cases, charAt, charCodeAt, character, clearInterval, clearTimeout,
200 close, closed, closure, comment, condition, confirm, console, constructor,
201 content, couch, create, css, curly, d, data, datalist, dd, debug, decodeURI,
202 decodeURIComponent, defaultStatus, defineClass, deserialize, devel, document,
203 dojo, dijit, dojox, define, edition, else, emit, encodeURI, encodeURIComponent,
204 entityify, eqeqeq, eqnull, errors, es5, escape, esnext, eval, event, evidence, evil,
205 ex, exception, exec, exps, expr, exports, FileReader, first, floor, focus,
206 forin, fragment, frames, from, fromCharCode, fud, funcscope, funct, function, functions,
207 g, gc, getComputedStyle, getRow, getter, GLOBAL, global, globals, globalstrict,
208 hasOwnProperty, help, history, i, id, identifier, immed, implieds, importPackage, include,
209 indent, indexOf, init, ins, instanceOf, isAlpha, isApplicationRunning, isArray,
210 isDigit, isFinite, isNaN, iterator, java, join, jshint,
211 JSHINT, json, jquery, jQuery, keys, label, labelled, last, lastsemic, laxbreak,
212 latedef, lbp, led, left, length, line, load, loadClass, localStorage, location,
213 log, loopfunc, m, match, maxerr, maxlen, member,message, meta, module, moveBy,
214 moveTo, mootools, multistr, name, navigator, new, newcap, noarg, node, noempty, nomen,
215 nonew, nonstandard, nud, onbeforeunload, onblur, onerror, onevar, onecase, onfocus,
216 onload, onresize, onunload, open, openDatabase, openURL, opener, opera, options, outer, param,
217 parent, parseFloat, parseInt, passfail, plusplus, predef, print, process, prompt,
218 proto, prototype, prototypejs, provides, push, quit, range, raw, reach, reason, regexp,
219 readFile, readUrl, regexdash, removeEventListener, replace, report, require,
220 reserved, resizeBy, resizeTo, resolvePath, resumeUpdates, respond, rhino, right,
221 runCommand, scroll, screen, scripturl, scrollBy, scrollTo, scrollbar, search, seal,
222 send, serialize, sessionStorage, setInterval, setTimeout, setter, setterToken, shift, slice,
223 smarttabs, sort, spawn, split, stack, status, start, strict, sub, substr, supernew, shadow,
224 supplant, sum, sync, test, toLowerCase, toString, toUpperCase, toint32, token, top, trailing,
225 type, typeOf, Uint16Array, Uint32Array, Uint8Array, undef, undefs, unused, urls, validthis,
226 value, valueOf, var, version, WebSocket, white, window, Worker, wsh*/
228 /*global exports: false */
230 // We build the application inside a function so that we produce only a single
231 // global variable. That function will be invoked immediately, and its return
232 // value is the JSHINT function itself.
234 var JSHINT
= (function () {
237 var anonname
, // The guessed name for anonymous functions.
239 // These are operators that should not be used with the ! operator.
257 // These are the JSHint boolean options.
259 asi
: true, // if automatic semicolon insertion should be tolerated
260 bitwise
: true, // if bitwise operators should not be allowed
261 boss
: true, // if advanced usage of assignments should be allowed
262 browser
: true, // if the standard browser globals should be predefined
263 couch
: true, // if CouchDB globals should be predefined
264 curly
: true, // if curly braces around all blocks should be required
265 debug
: true, // if debugger statements should be allowed
266 devel
: true, // if logging globals should be predefined (console,
268 dojo
: true, // if Dojo Toolkit globals should be predefined
269 eqeqeq
: true, // if === should be required
270 eqnull
: true, // if == null comparisons should be tolerated
271 es5
: true, // if ES5 syntax should be allowed
272 esnext
: true, // if es.next specific syntax should be allowed
273 evil
: true, // if eval should be allowed
274 expr
: true, // if ExpressionStatement should be allowed as Programs
275 forin
: true, // if for in statements must filter
276 funcscope
: true, // if only function scope should be used for scope tests
277 globalstrict
: true, // if global "use strict"; should be allowed (also
279 immed
: true, // if immediate invocations must be wrapped in parens
280 iterator
: true, // if the `__iterator__` property should be allowed
281 jquery
: true, // if jQuery globals should be predefined
282 lastsemic
: true, // if semicolons may be ommitted for the trailing
283 // statements inside of a one-line blocks.
284 latedef
: true, // if the use before definition should not be tolerated
285 laxbreak
: true, // if line breaks should not be checked
286 loopfunc
: true, // if functions should be allowed to be defined within
288 mootools
: true, // if MooTools globals should be predefined
289 multistr
: true, // allow multiline strings
290 newcap
: true, // if constructor names must be capitalized
291 noarg
: true, // if arguments.caller and arguments.callee should be
293 node
: true, // if the Node.js environment globals should be
295 noempty
: true, // if empty blocks should be disallowed
296 nonew
: true, // if using `new` for side-effects should be disallowed
297 nonstandard
: true, // if non-standard (but widely adopted) globals should
299 nomen
: true, // if names should be checked
300 onevar
: true, // if only one var statement per function should be
302 onecase
: true, // if one case switch statements should be allowed
303 passfail
: true, // if the scan should stop on first error
304 plusplus
: true, // if increment/decrement should not be allowed
305 proto
: true, // if the `__proto__` property should be allowed
306 prototypejs
: true, // if Prototype and Scriptaculous globals should be
308 regexdash
: true, // if unescaped first/last dash (-) inside brackets
309 // should be tolerated
310 regexp
: true, // if the . should not be allowed in regexp literals
311 rhino
: true, // if the Rhino environment globals should be predefined
312 undef
: true, // if variables should be declared before used
313 scripturl
: true, // if script-targeted URLs should be tolerated
314 shadow
: true, // if variable shadowing should be tolerated
315 smarttabs
: true, // if smarttabs should be tolerated
316 // (http://www.emacswiki.org/emacs
/SmartTabs
)
317 strict
: true, // require the "use strict"; pragma
318 sub
: true, // if all forms of subscript notation are tolerated
319 supernew
: true, // if `new function () { ... };` and `new Object;`
320 // should be tolerated
321 trailing
: true, // if trailing whitespace rules apply
322 validthis
: true, // if 'this' inside a non-constructor function is valid.
323 // This is a function scoped option only.
324 white
: true, // if strict whitespace rules apply
325 wsh
: true // if the Windows Scripting Host environment globals
326 // should be predefined
329 // browser contains a set of global names which are commonly provided by a
330 // web browser environment.
333 ArrayBufferView
: false,
335 addEventListener
: false,
336 applicationCache
: false,
338 clearInterval
: false,
339 clearTimeout
: false,
343 defaultStatus
: false,
347 Float32Array
: false,
348 Float64Array
: false,
352 getComputedStyle
: false,
354 HTMLAnchorElement
: false,
355 HTMLBaseElement
: false,
356 HTMLBlockquoteElement
: false,
357 HTMLBodyElement
: false,
358 HTMLBRElement
: false,
359 HTMLButtonElement
: false,
360 HTMLCanvasElement
: false,
361 HTMLDirectoryElement
: false,
362 HTMLDivElement
: false,
363 HTMLDListElement
: false,
364 HTMLFieldSetElement
: false,
365 HTMLFontElement
: false,
366 HTMLFormElement
: false,
367 HTMLFrameElement
: false,
368 HTMLFrameSetElement
: false,
369 HTMLHeadElement
: false,
370 HTMLHeadingElement
: false,
371 HTMLHRElement
: false,
372 HTMLHtmlElement
: false,
373 HTMLIFrameElement
: false,
374 HTMLImageElement
: false,
375 HTMLInputElement
: false,
376 HTMLIsIndexElement
: false,
377 HTMLLabelElement
: false,
378 HTMLLayerElement
: false,
379 HTMLLegendElement
: false,
380 HTMLLIElement
: false,
381 HTMLLinkElement
: false,
382 HTMLMapElement
: false,
383 HTMLMenuElement
: false,
384 HTMLMetaElement
: false,
385 HTMLModElement
: false,
386 HTMLObjectElement
: false,
387 HTMLOListElement
: false,
388 HTMLOptGroupElement
: false,
389 HTMLOptionElement
: false,
390 HTMLParagraphElement
: false,
391 HTMLParamElement
: false,
392 HTMLPreElement
: false,
393 HTMLQuoteElement
: false,
394 HTMLScriptElement
: false,
395 HTMLSelectElement
: false,
396 HTMLStyleElement
: false,
397 HTMLTableCaptionElement
: false,
398 HTMLTableCellElement
: false,
399 HTMLTableColElement
: false,
400 HTMLTableElement
: false,
401 HTMLTableRowElement
: false,
402 HTMLTableSectionElement
: false,
403 HTMLTextAreaElement
: false,
404 HTMLTitleElement
: false,
405 HTMLUListElement
: false,
406 HTMLVideoElement
: false,
413 localStorage
: false,
419 onbeforeunload
: true,
427 openDatabase
: false,
432 removeEventListener
: false,
439 sessionStorage
: false,
442 SharedWorker
: false,
451 XMLHttpRequest
: false,
452 XPathEvaluator
: false,
453 XPathException
: false,
454 XPathExpression
: false,
455 XPathNamespace
: false,
456 XPathNSResolver
: false,
502 funct
, // The current function
505 'closure', 'exception', 'global', 'label',
506 'outer', 'unused', 'var'
509 functions
, // All of the functions
511 global
, // The global scope
512 implied
, // Implied globals
551 InputValidator
: false,
588 clearTimeout
: false,
590 clearInterval
: false
595 predefined
, // Global variables defined by option
620 PeriodicalExecuter
: false,
627 Autocompleter
: false,
635 SortableObserver
: false,
637 Scriptaculous
: false
645 importPackage
: false,
662 scope
, // The current scope
665 // standard contains the global names that are provided by the
666 // ECMAScript standard.
672 decodeURIComponent
: false,
674 encodeURIComponent
: false,
679 hasOwnProperty
: false,
689 ReferenceError
: false,
697 // widely adopted global names that are not part of ECMAScript standard
711 NEGATIVE_INFINITY
: true,
713 POSITIVE_INFINITY
: true,
727 ActiveXObject
: true,
731 ScriptEngineBuildVersion
: true,
732 ScriptEngineMajorVersion
: true,
733 ScriptEngineMinorVersion
: true,
737 XDomainRequest
: true
740 // Regular expressions. Some of these are stupidly long.
741 var ax
, cx
, tx
, nx
, nxg
, lx
, ix
, jx
, ft
;
743 /*jshint maxlen:300 */
745 // unsafe comment or string
746 ax
= /@cc|<\/?|script
|\]\s
*\]|<\s
*!|<
/i
;
748 // unsafe characters that are silently deleted by one or more browsers
749 cx
= /[\u0000-\u001f\u007f-\u009f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/;
752 tx
= /^\s*([(){}\[.,:;'"~\?\]#@]|==?=?|\/(\*(jshint
|jslint
|members
?|global
)?|=|\/)?|\*[\/=]?|\+(?:=|\++)?|-(?:=|-+)?|%=?|&[&=]?|\|[|=]?|>>?>?=?|<([\/=!]|\!(\[|--)?|<=?)?|\^=?|\!=?=?|[a-zA-Z_$][a-zA-Z0-9_$]*|[0-9]+([xX][0-9a-fA-F]+|\.[0-9]*)?([eE][+\-]?[0-9]+)?)/;
754 // characters in strings that need escapement
755 nx
= /[\u0000-\u001f&<"\/\\\u007f-\u009f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/;
756 nxg
= /[\u0000-\u001f&<"\/\\\u007f-\u009f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g
;
762 ix
= /^([a-zA-Z_$][a-zA-Z0-9_$]*)$/;
765 jx
= /^(?:javascript|jscript|ecmascript|vbscript|mocha|livescript)\s*:/i;
767 // catches /* falls through */ comments
768 ft
= /^\s*\/\*\s
*falls
\sthrough
\s
*\*\/\s*$/;
771 function F() {} // Used by Object.create
773 function is_own(object
, name
) {
775 // The object.hasOwnProperty method fails when the property under consideration
776 // is named 'hasOwnProperty'. So we have to use this more convoluted form.
778 return Object
.prototype.hasOwnProperty
.call(object
, name
);
781 // Provide critical ES5 functions to ES3.
783 if (typeof Array
.isArray
!== 'function') {
784 Array
.isArray
= function (o
) {
785 return Object
.prototype.toString
.apply(o
) === '[object Array]';
789 if (typeof Object
.create
!== 'function') {
790 Object
.create
= function (o
) {
796 if (typeof Object
.keys
!== 'function') {
797 Object
.keys
= function (o
) {
808 // Non standard methods
810 if (typeof String
.prototype.entityify
!== 'function') {
811 String
.prototype.entityify
= function () {
813 .replace(/&/g, '&')
814 .replace(/</g, '<')
815 .replace(/>/g, '>');
819 if (typeof String
.prototype.isAlpha
!== 'function') {
820 String
.prototype.isAlpha
= function () {
821 return (this >= 'a' && this <= 'z\uffff') ||
822 (this >= 'A' && this <= 'Z\uffff');
826 if (typeof String
.prototype.isDigit
!== 'function') {
827 String
.prototype.isDigit
= function () {
828 return (this >= '0' && this <= '9');
832 if (typeof String
.prototype.supplant
!== 'function') {
833 String
.prototype.supplant
= function (o
) {
834 return this.replace(/\{([^{}]*)\}/g, function (a
, b
) {
836 return typeof r
=== 'string' || typeof r
=== 'number' ? r
: a
;
841 if (typeof String
.prototype.name
!== 'function') {
842 String
.prototype.name
= function () {
844 // If the string looks like an identifier, then we can return it as is.
845 // If the string contains no control characters, no quote characters, and no
846 // backslash characters, then we can simply slap some quotes around it.
847 // Otherwise we must also replace the offending characters with safe
854 return '"' + this.replace(nxg
, function (a
) {
859 return '\\u' + ('0000' + a
.charCodeAt().toString(16)).slice(-4);
862 return '"' + this + '"';
867 function combine(t
, o
) {
878 combine(predefined
, couch
);
882 combine(predefined
, rhino
);
885 if (option
.prototypejs
) {
886 combine(predefined
, prototypejs
);
890 combine(predefined
, node
);
894 combine(predefined
, devel
);
898 combine(predefined
, dojo
);
901 if (option
.browser
) {
902 combine(predefined
, browser
);
905 if (option
.nonstandard
) {
906 combine(predefined
, nonstandard
);
910 combine(predefined
, jquery
);
913 if (option
.mootools
) {
914 combine(predefined
, mootools
);
918 combine(predefined
, wsh
);
925 if (option
.globalstrict
&& option
.strict
!== false) {
926 option
.strict
= true;
931 // Produce an error warning.
932 function quit(message
, line
, chr
) {
933 var percentage
= Math
.floor((line
/ lines
.length
) * 100);
939 message
: message
+ " (" + percentage
+ "% scanned).",
944 function isundef(scope
, m
, t
, a
) {
945 return JSHINT
.undefs
.push([scope
, m
, t
, a
]);
948 function warning(m
, t
, a
, b
, c
, d
) {
951 if (t
.id
=== '(end)') { // `~
959 evidence
: lines
[l
- 1] || '',
967 w
.reason
= m
.supplant(w
);
968 JSHINT
.errors
.push(w
);
969 if (option
.passfail
) {
970 quit('Stopping. ', l
, ch
);
973 if (warnings
>= option
.maxerr
) {
974 quit("Too many errors.", l
, ch
);
979 function warningAt(m
, l
, ch
, a
, b
, c
, d
) {
986 function error(m
, t
, a
, b
, c
, d
) {
987 var w
= warning(m
, t
, a
, b
, c
, d
);
990 function errorAt(m
, l
, ch
, a
, b
, c
, d
) {
999 // lexical analysis and token construction
1001 var lex
= (function lex() {
1002 var character
, from
, line
, s
;
1004 // Private lex methods
1006 function nextLine() {
1008 tw
; // trailing whitespace check
1010 if (line
>= lines
.length
)
1017 // If smarttabs option is used check for spaces followed by tabs only.
1018 // Otherwise check for any occurence of mixed tabs and spaces.
1019 if (option
.smarttabs
)
1020 at
= s
.search(/ \t/);
1022 at
= s
.search(/ \t|\t /);
1025 warningAt("Mixed spaces and tabs.", line
, at
+ 1);
1027 s
= s
.replace(/\t/g, tab
);
1031 warningAt("Unsafe character.", line
, at
);
1033 if (option
.maxlen
&& option
.maxlen
< s
.length
)
1034 warningAt("Line too long.", line
, s
.length
);
1036 // Check for trailing whitespaces
1037 tw
= /\s+$/.test(s
);
1038 if (option
.trailing
&& tw
&& !/^\s+$/.test(s
)) {
1039 warningAt("Trailing whitespace.", line
, tw
);
1044 // Produce a token object. The token inherits from a syntax symbol.
1046 function it(type
, value
) {
1048 if (type
=== '(color)' || type
=== '(range)') {
1050 } else if (type
=== '(punctuator)' ||
1051 (type
=== '(identifier)' && is_own(syntax
, value
))) {
1052 t
= syntax
[value
] || syntax
['(error)'];
1056 t
= Object
.create(t
);
1057 if (type
=== '(string)' || type
=== '(range)') {
1058 if (!option
.scripturl
&& jx
.test(value
)) {
1059 warningAt("Script URL.", line
, from
);
1062 if (type
=== '(identifier)') {
1063 t
.identifier
= true;
1064 if (value
=== '__proto__' && !option
.proto
) {
1065 warningAt("The '{a}' property is deprecated.",
1067 } else if (value
=== '__iterator__' && !option
.iterator
) {
1068 warningAt("'{a}' is only available in JavaScript 1.7.",
1070 } else if (option
.nomen
&& (value
.charAt(0) === '_' ||
1071 value
.charAt(value
.length
- 1) === '_')) {
1072 if (!option
.node
|| token
.id
=== '.' ||
1073 (value
!== '__dirname' && value
!== '__filename')) {
1074 warningAt("Unexpected {a} in '{b}'.", line
, from
, "dangling '_'", value
);
1080 t
.character
= character
;
1083 if (i
!== '(endline)') {
1085 (('(,=:[!&|?{};'.indexOf(i
.charAt(i
.length
- 1)) >= 0) ||
1092 // Public lex methods
1094 init
: function (source
) {
1095 if (typeof source
=== 'string') {
1097 .replace(/\r\n/g, '\n')
1098 .replace(/\r/g, '\n')
1104 // If the first line is a shebang (#!), make it a blank and move on.
1105 // Shebangs are used by Node scripts.
1106 if (lines
[0] && lines
[0].substr(0, 2) === '#!')
1114 range
: function (begin
, end
) {
1117 if (s
.charAt(0) !== begin
) {
1118 errorAt("Expected '{a}' and instead saw '{b}'.",
1119 line
, character
, begin
, s
.charAt(0));
1127 errorAt("Missing '{a}'.", line
, character
, c
);
1132 return it('(range)', value
);
1134 warningAt("Unexpected '{a}'.", line
, character
, c
);
1142 // token -- this is called by advance to get the next token
1143 token
: function () {
1144 var b
, c
, captures
, d
, depth
, high
, i
, l
, low
, q
, t
, isLiteral
, isInRange
;
1147 var r
= x
.exec(s
), r1
;
1153 from
= character
+ l
- r1
.length
;
1159 function string(x
) {
1160 var c
, j
, r
= '', allowNewLine
= false;
1162 if (jsonmode
&& x
!== '"') {
1163 warningAt("Strings must use doublequote.",
1168 var i
= parseInt(s
.substr(j
+ 1, n
), 16);
1170 if (i
>= 32 && i
<= 126 &&
1171 i
!== 34 && i
!== 92 && i
!== 39) {
1172 warningAt("Unnecessary escapement.", line
, character
);
1175 c
= String
.fromCharCode(i
);
1178 unclosedString
: for (;;) {
1179 while (j
>= s
.length
) {
1182 var cl
= line
, cf
= from
;
1184 errorAt("Unclosed string.", cl
, cf
);
1185 break unclosedString
;
1189 allowNewLine
= false;
1191 warningAt("Unclosed string.", cl
, cf
);
1197 s
= s
.substr(j
+ 1);
1198 return it('(string)', r
, x
);
1201 if (c
=== '\n' || c
=== '\r') {
1204 warningAt("Control character in string: {a}.",
1205 line
, character
+ j
, s
.slice(0, j
));
1206 } else if (c
=== '\\') {
1217 warningAt("Avoid \\'.", line
, character
);
1240 warningAt("Avoid \\v.", line
, character
);
1246 warningAt("Avoid \\x-.", line
, character
);
1251 // last character is escape character
1252 // always allow new line if escaped, but show
1253 // warning if option is not set
1254 allowNewLine
= true;
1255 if (option
.multistr
) {
1257 warningAt("Avoid EOL escapement.", line
, character
);
1263 warningAt("Bad escapement of EOL. Use option multistr if needed.",
1267 warningAt("Bad escapement.", line
, character
);
1278 return it(nextLine() ? '(endline)' : '(end)', '');
1284 while (s
&& s
< '!') {
1288 errorAt("Unexpected '{a}'.", line
, character
, s
.substr(0, 1));
1295 if (c
.isAlpha() || c
=== '_' || c
=== '$') {
1296 return it('(identifier)', t
);
1302 if (!isFinite(Number(t
))) {
1303 warningAt("Bad number '{a}'.",
1304 line
, character
, t
);
1306 if (s
.substr(0, 1).isAlpha()) {
1307 warningAt("Missing space after '{a}'.",
1308 line
, character
, t
);
1313 if (token
.id
!== '.') {
1314 warningAt("Don't use extra leading zeros '{a}'.",
1315 line
, character
, t
);
1317 } else if (jsonmode
&& (d
=== 'x' || d
=== 'X')) {
1318 warningAt("Avoid 0x-. '{a}'.",
1319 line
, character
, t
);
1322 if (t
.substr(t
.length
- 1) === '.') {
1324 "A trailing decimal point can be confused with a dot '{a}'.", line
, character
, t
);
1326 return it('(number)', t
);
1340 token
.comment
= true;
1352 errorAt("Unclosed comment.", line
, character
);
1356 if (s
.substr(i
, 1) === '/') {
1357 errorAt("Nested comment.", line
, character
);
1359 s
= s
.substr(i
+ 2);
1360 token
.comment
= true;
1363 // /*members
/*jshint /*global
1375 character: character,
1383 if (token.id === '/=') {
1384 errorAt("A regular expression literal can be confused with '/='.",
1397 errorAt("Unclosed regular expression.", line, from);
1398 return quit('Stopping
.', line, from);
1401 warningAt("{a} unterminated regular expression " +
1402 "group(s).", line, from + l, depth);
1404 c = s.substr(0, l - 1);
1410 while (q[s.charAt(l)] === true) {
1411 q[s.charAt(l)] = false;
1417 if (q === '/' || q === '*') {
1418 errorAt("Confusing regular expression.",
1421 return it('(regexp
)', c);
1426 "Unexpected control character in regular expression.", line, from + l);
1427 } else if (c === '<') {
1429 "Unexpected escaped character '{a
}' in regular expression.", line, from + l, c);
1436 if (s.charAt(l) === '?') {
1438 switch (s.charAt(l)) {
1446 "Expected '{a
}' and instead saw '{b
}'.", line, from + l, ':', s.charAt(l));
1457 warningAt("Unescaped '{a
}'.",
1458 line, from + l, ')');
1465 while (s.charAt(l) === ' ') {
1471 "Spaces are hard to count. Use {{a}}.", line, from + l, q);
1478 if (option.regexp) {
1479 warningAt("Insecure '{a
}'.",
1481 } else if (s.charAt(l) === ']') {
1482 errorAt("Unescaped '{a
}'.",
1483 line, from + l, '^');
1487 warningAt("Empty class.", line,
1498 warningAt("Unescaped '{a
}'.",
1507 if (isLiteral && !isInRange) {
1510 } else if (isInRange) {
1512 } else if (s.charAt(l) === ']') {
1515 if (option.regexdash !== (l === 2 || (l === 3 &&
1516 s.charAt(1) === '^'))) {
1517 warningAt("Unescaped '{a
}'.",
1518 line, from + l - 1, '-');
1524 if (isInRange && !option.regexdash) {
1525 warningAt("Unescaped '{a
}'.",
1526 line, from + l - 1, '-');
1533 "Unexpected control character in regular expression.", line, from + l);
1534 } else if (c === '<') {
1536 "Unexpected escaped character '{a
}' in regular expression.", line, from + l, c);
1540 // \w, \s and \d are never part of a character range
1541 if (/[wsd]/i.test(c)) {
1543 warningAt("Unescaped '{a
}'.",
1544 line, from + l, '-');
1548 } else if (isInRange) {
1555 warningAt("Unescaped '{a
}'.",
1556 line, from + l - 1, '/');
1581 if (option.regexp) {
1582 warningAt("Insecure '{a
}'.", line,
1592 warningAt("Unescaped '{a
}'.", line,
1596 switch (s.charAt(l)) {
1601 if (s.charAt(l) === '?') {
1608 if (c < '0' || c > '9') {
1610 "Expected a number and instead saw '{a
}'.", line, from + l, c);
1616 if (c < '0' || c > '9') {
1620 low = +c + (low * 10);
1627 if (c >= '0' && c <= '9') {
1632 if (c < '0' || c > '9') {
1636 high = +c + (high * 10);
1640 if (s.charAt(l) !== '}') {
1642 "Expected '{a
}' and instead saw '{b
}'.", line, from + l, '}', c);
1646 if (s.charAt(l) === '?') {
1651 "'{a
}' should not be greater than '{b
}'.", line, from + l, low, high);
1656 c = s.substr(0, l - 1);
1659 return it('(regexp
)', c);
1661 return it('(punctuator
)', t);
1666 return it('(punctuator
)', t);
1668 return it('(punctuator
)', t);
1677 function addlabel(t, type) {
1679 if (t === 'hasOwnProperty
') {
1680 warning("'hasOwnProperty
' is a really bad name.");
1683 // Define t in the current function in the current scope.
1684 if (is_own(funct, t) && !funct['(global
)']) {
1685 if (funct[t] === true) {
1687 warning("'{a
}' was used before it was defined.", nexttoken, t);
1689 if (!option.shadow && type !== "exception")
1690 warning("'{a
}' is already defined.", nexttoken, t);
1695 if (funct['(global
)']) {
1697 if (is_own(implied, t)) {
1699 warning("'{a
}' was used before it was defined.", nexttoken, t);
1708 function doOption() {
1709 var b, obj, filter, o = nexttoken.value, t, v;
1712 error("Unbegun comment.");
1725 filter = boolOptions;
1736 if (t.type === 'special' && t.value === '*/') {
1739 if (t.id !== '(endline
)' && t.id !== ',') {
1744 if (t.type !== '(string
)' && t.type !== '(identifier
)' &&
1745 o !== '/*members') {
1746 error("Bad option.", t);
1751 if (obj === membersOnly) {
1752 error("Expected '{a}' and instead saw '{b}'.",
1755 if (t.value === 'indent
' && (o === '/*jshint' || o === '/*jslint')) {
1757 if (typeof b !== 'number' || !isFinite(b) || b <= 0 ||
1758 Math.floor(b) !== b) {
1759 error("Expected a small integer and instead saw '{a}'.",
1764 } else if (t.value === 'maxerr' && (o === '/*jshint' || o === '/*jslint')) {
1766 if (typeof b !== 'number' || !isFinite(b) || b <= 0 ||
1767 Math.floor(b) !== b) {
1768 error("Expected a small integer and instead saw '{a}'.",
1772 } else if (t.value === 'maxlen' && (o === '/*jshint' || o === '/*jslint')) {
1774 if (typeof b !== 'number' || !isFinite(b) || b <= 0 ||
1775 Math.floor(b) !== b) {
1776 error("Expected a small integer and instead saw '{a}'.",
1780 } else if (t.value === 'validthis') {
1781 if (funct['(global)']) {
1782 error("Option 'validthis' can't be used in a global scope.");
1784 if (v.value === 'true' || v.value === 'false')
1785 obj[t.value] = v.value === 'true';
1787 error("Bad option value.", v);
1789 } else if (v.value === 'true') {
1790 obj[t.value] = true;
1791 } else if (v.value === 'false') {
1792 obj[t.value] = false;
1794 error("Bad option value.", v);
1798 if (o === '/*jshint' || o === '/*jslint') {
1799 error("Missing option value.", t);
1801 obj[t.value] = false;
1811 // We need a peek function. If it has an argument, it peeks that much farther
1812 // ahead. It is used to distinguish
1813 // for ( var i in ...
1815 // for ( var i = ...
1818 var i = p || 0, j = 0, t;
1823 t = lookahead[j] = lex.token();
1832 // Produce the next token. It looks for programming errors.
1834 function advance(id, t) {
1837 if (nexttoken.id === '.') {
1838 warning("A dot following a number can be confused with a decimal point.", token);
1842 if (nexttoken.id === '-' || nexttoken.id === '--') {
1843 warning("Confusing minusses.");
1847 if (nexttoken.id === '+' || nexttoken.id === '++') {
1848 warning("Confusing plusses.");
1853 if (token.type === '(string)' || token.identifier) {
1854 anonname = token.value;
1857 if (id && nexttoken.id !== id) {
1859 if (nexttoken.id === '(end)') {
1860 warning("Unmatched '{a}'.", t, t.id);
1862 warning("Expected '{a}' to match '{b}' from line {c} and instead saw '{d}'.",
1863 nexttoken, id, t.id, t.line, nexttoken.value);
1865 } else if (nexttoken.type !== '(identifier)' ||
1866 nexttoken.value !== id) {
1867 warning("Expected '{a}' and instead saw '{b}'.",
1868 nexttoken, id, nexttoken.value);
1875 nexttoken = lookahead.shift() || lex.token();
1876 if (nexttoken.id === '(end)' || nexttoken.id === '(error)') {
1879 if (nexttoken.type === 'special') {
1882 if (nexttoken.id !== '(endline)') {
1890 // This is the heart of JSHINT, the Pratt parser. In addition to parsing, it
1891 // is looking for ad hoc lint patterns. We add .fud to Pratt's model, which is
1892 // like .nud except that it is only used on the first token of a statement.
1893 // Having .fud makes it much easier to define statement-oriented languages like
1894 // JavaScript. I retained Pratt's nomenclature.
1896 // .nud Null denotation
1897 // .fud First null denotation
1898 // .led Left denotation
1899 // lbp Left binding power
1900 // rbp Right binding power
1902 // They are elements of the parsing method called Top Down Operator Precedence.
1904 function expression(rbp, initial) {
1905 var left, isArray = false;
1907 if (nexttoken.id === '(end)')
1908 error("Unexpected early end of program.", token);
1912 anonname = 'anonymous';
1913 funct['(verb)'] = token.value;
1915 if (initial === true && token.fud) {
1921 if (nexttoken.type === '(number)' && token.id === '.') {
1922 warning("A leading decimal point can be confused with a dot: '.{a}'.",
1923 token, nexttoken.value);
1927 error("Expected an identifier and instead saw '{a}'.",
1931 while (rbp < nexttoken.lbp) {
1932 isArray = token.value === 'Array';
1934 if (isArray && token.id === '(' && nexttoken.id === ')')
1935 warning("Use the array literal notation [].", token);
1937 left = token.led(left);
1939 error("Expected an operator and instead saw '{a}'.",
1948 // Functions for conformance of style.
1950 function adjacent(left, right) {
1951 left = left || token;
1952 right = right || nexttoken;
1954 if (left.character !== right.from && left.line === right.line) {
1955 left.from += (left.character - left.from);
1956 warning("Unexpected space after '{a}'.", left, left.value);
1961 function nobreak(left, right) {
1962 left = left || token;
1963 right = right || nexttoken;
1964 if (option.white && (left.character !== right.from || left.line !== right.line)) {
1965 warning("Unexpected space before '{a}'.", right, right.value);
1969 function nospace(left, right) {
1970 left = left || token;
1971 right = right || nexttoken;
1972 if (option.white && !left.comment) {
1973 if (left.line === right.line) {
1974 adjacent(left, right);
1979 function nonadjacent(left, right) {
1981 left = left || token;
1982 right = right || nexttoken;
1983 if (left.line === right.line && left.character === right.from) {
1984 left.from += (left.character - left.from);
1985 warning("Missing space after '{a}'.",
1991 function nobreaknonadjacent(left, right) {
1992 left = left || token;
1993 right = right || nexttoken;
1994 if (!option.laxbreak && left.line !== right.line) {
1995 warning("Bad line breaking before '{a}'.", right, right.id);
1996 } else if (option.white) {
1997 left = left || token;
1998 right = right || nexttoken;
1999 if (left.character === right.from) {
2000 left.from += (left.character - left.from);
2001 warning("Missing space after '{a}'.",
2007 function indentation(bias) {
2009 if (option.white && nexttoken.id !== '(end)') {
2010 i = indent + (bias || 0);
2011 if (nexttoken.from !== i) {
2013 "Expected '{a}' to have an indentation at {b} instead at {c}.",
2014 nexttoken, nexttoken.value, i, nexttoken.from);
2019 function nolinebreak(t) {
2021 if (t.line !== nexttoken.line) {
2022 warning("Line breaking error '{a}'.", t, t.value);
2028 if (token.line !== nexttoken.line) {
2029 if (!option.laxbreak) {
2030 warning("Bad line breaking before '{a}'.", token, nexttoken.id);
2032 } else if (!token.comment && token.character !== nexttoken.from && option.white) {
2033 token.from += (token.character - token.from);
2034 warning("Unexpected space after '{a}'.", token, token.value);
2037 nonadjacent(token, nexttoken);
2041 // Functional constructors for making the symbols that will be inherited by
2044 function symbol(s, p) {
2046 if (!x || typeof x !== 'object') {
2058 return symbol(s, 0);
2062 function stmt(s, f) {
2064 x.identifier = x.reserved = true;
2070 function blockstmt(s, f) {
2077 function reserveName(x) {
2078 var c = x.id.charAt(0);
2079 if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')) {
2080 x.identifier = x.reserved = true;
2086 function prefix(s, f) {
2087 var x = symbol(s, 150);
2089 x.nud = (typeof f === 'function') ? f : function () {
2090 this.right = expression(150);
2091 this.arity = 'unary';
2092 if (this.id === '++' || this.id === '--') {
2093 if (option.plusplus) {
2094 warning("Unexpected use of '{a}'.", this, this.id);
2095 } else if ((!this.right.identifier || this.right.reserved) &&
2096 this.right.id !== '.' && this.right.id !== '[') {
2097 warning("Bad operand.", this);
2106 function type(s, f) {
2114 function reserve(s, f) {
2116 x.identifier = x.reserved = true;
2121 function reservevar(s, v) {
2122 return reserve(s, function () {
2123 if (typeof v === 'function') {
2131 function infix(s, f, p, w) {
2132 var x = symbol(s, p);
2134 x.led = function (left) {
2136 nobreaknonadjacent(prevtoken, token);
2137 nonadjacent(token, nexttoken);
2139 if (s === "in" && left.id === "!") {
2140 warning("Confusing use of '{a}'.", left, '!');
2142 if (typeof f === 'function') {
2143 return f(left, this);
2146 this.right = expression(p);
2154 function relation(s, f) {
2155 var x = symbol(s, 100);
2156 x.led = function (left) {
2157 nobreaknonadjacent(prevtoken, token);
2158 nonadjacent(token, nexttoken);
2159 var right = expression(100);
2160 if ((left && left.id === 'NaN') || (right && right.id === 'NaN')) {
2161 warning("Use the isNaN function to compare with NaN.", this);
2163 f.apply(this, [left, right]);
2165 if (left.id === '!') {
2166 warning("Confusing use of '{a}'.", left, '!');
2168 if (right.id === '!') {
2169 warning("Confusing use of '{a}'.", right, '!');
2179 function isPoorRelation(node) {
2181 ((node.type === '(number)' && +node.value === 0) ||
2182 (node.type === '(string)' && node.value === '') ||
2183 (node.type === 'null' && !option.eqnull) ||
2184 node.type === 'true' ||
2185 node.type === 'false' ||
2186 node.type === 'undefined');
2190 function assignop(s, f) {
2191 symbol(s, 20).exps = true;
2192 return infix(s, function (left, that) {
2195 if (predefined[left.value] === false &&
2196 scope[left.value]['(global)'] === true) {
2197 warning("Read only.", left);
2198 } else if (left['function']) {
2199 warning("'{a}' is a function.", left, left.value);
2202 if (option.esnext && funct[left.value] === 'const') {
2203 warning("Attempting to override '{a}' which is a constant", left, left.value);
2205 if (left.id === '.' || left.id === '[') {
2206 if (!left.left || left.left.value === 'arguments') {
2207 warning('Bad assignment.', that);
2209 that.right = expression(19);
2211 } else if (left.identifier && !left.reserved) {
2212 if (funct[left.value] === 'exception') {
2213 warning("Do not assign to the exception parameter.", left);
2215 that.right = expression(19);
2218 if (left === syntax['function']) {
2220 "Expected an identifier in an assignment and instead saw a function invocation.",
2224 error("Bad assignment.", that);
2229 function bitwise(s, f, p) {
2230 var x = symbol(s, p);
2232 x.led = (typeof f === 'function') ? f : function (left) {
2233 if (option.bitwise) {
2234 warning("Unexpected use of '{a}'.", this, this.id);
2237 this.right = expression(p);
2244 function bitwiseassignop(s) {
2245 symbol(s, 20).exps = true;
2246 return infix(s, function (left, that) {
2247 if (option.bitwise) {
2248 warning("Unexpected use of '{a}'.", that, that.id);
2250 nonadjacent(prevtoken, token);
2251 nonadjacent(token, nexttoken);
2253 if (left.id === '.' || left.id === '[' ||
2254 (left.identifier && !left.reserved)) {
2258 if (left === syntax['function']) {
2260 "Expected an identifier in an assignment, and instead saw a function invocation.",
2265 error("Bad assignment.", that);
2270 function suffix(s, f) {
2271 var x = symbol(s, 150);
2272 x.led = function (left) {
2273 if (option.plusplus) {
2274 warning("Unexpected use of '{a}'.", this, this.id);
2275 } else if ((!left.identifier || left.reserved) &&
2276 left.id !== '.' && left.id !== '[') {
2277 warning("Bad operand.", this);
2286 // fnparam means that this identifier is being defined as a function
2287 // argument (see identifier())
2288 function optionalidentifier(fnparam) {
2289 if (nexttoken.identifier) {
2291 if (token.reserved && !option.es5) {
2292 // `undefined` as a function param is a common pattern to protect
2293 // against the case when somebody does `undefined = true` and
2294 // help with minification. More info: https://gist.github.com/315916
2295 if (!fnparam || token.value !== 'undefined') {
2296 warning("Expected an identifier and instead saw '{a}' (a reserved word).",
2304 // fnparam means that this identifier is being defined as a function
2306 function identifier(fnparam) {
2307 var i = optionalidentifier(fnparam);
2311 if (token.id === 'function' && nexttoken.id === '(') {
2312 warning("Missing name in function declaration.");
2314 error("Expected an identifier and instead saw '{a}'.",
2315 nexttoken, nexttoken.value);
2320 function reachable(s) {
2322 if (nexttoken.id !== ';' || noreach) {
2330 if (t.id !== '(endline)') {
2331 if (t.id === 'function') {
2333 "Inner functions should be listed at the top of the outer function.", t);
2336 warning("Unreachable '{a}' after '{b}'.", t, t.value, s);
2344 function statement(noindent) {
2345 var i = indent, r, s = scope, t = nexttoken;
2352 // Is this a labelled statement?
2354 if (t.identifier && !t.reserved && peek().id === ':') {
2357 scope = Object.create(s);
2358 addlabel(t.value, 'label');
2359 if (!nexttoken.labelled) {
2360 warning("Label '{a}' on {b} statement.",
2361 nexttoken, t.value, nexttoken.value);
2363 if (jx.test(t.value + ':')) {
2364 warning("Label '{a}' looks like a javascript url.",
2367 nexttoken.label = t.value;
2371 // Parse the statement.
2376 r = expression(0, true);
2378 // Look for the final semicolon.
2380 if (!option.expr && (!r || !r.exps)) {
2381 warning("Expected an assignment or function call and instead saw an expression.",
2383 } else if (option.nonew && r.id === '(' && r.left.id === 'new') {
2384 warning("Do not use 'new' for side effects.");
2387 if (nexttoken.id !== ';') {
2389 // If this is the last statement in a block that ends on
2390 // the same line *and* option lastsemic is on, ignore the warning.
2391 // Otherwise, complain about missing semicolon.
2392 if (!option.lastsemic || nexttoken.id !== '}' ||
2393 nexttoken.line !== token.line) {
2394 warningAt("Missing semicolon.", token.line, token.character);
2398 adjacent(token, nexttoken);
2400 nonadjacent(token, nexttoken);
2404 // Restore the indentation.
2412 function statements(startLine) {
2415 while (!nexttoken.reach && nexttoken.id !== '(end)') {
2416 if (nexttoken.id === ';') {
2417 warning("Unnecessary semicolon.");
2420 a.push(statement(startLine === nexttoken.line));
2428 * read all directives
2429 * recognizes a simple form of asi, but always
2430 * warns, if it is used
2432 function directives() {
2436 if (nexttoken
.id
=== "(string)") {
2438 if (p
.id
=== "(endline)") {
2443 } while (pn
.id
=== "(endline)");
2445 if (pn
.id
!== ";") {
2446 if (pn
.id
!== "(string)" && pn
.id
!== "(number)" &&
2447 pn
.id
!== "(regexp)" && pn
.identifier
!== true &&
2451 warning("Missing semicolon.", nexttoken
);
2455 } else if (p
.id
=== "}") {
2456 // directive with no other statements, warn about missing semicolon
2457 warning("Missing semicolon.", p
);
2458 } else if (p
.id
!== ";") {
2464 if (directive
[token
.value
]) {
2465 warning("Unnecessary directive \"{a}\".", token
, token
.value
);
2468 if (token
.value
=== "use strict") {
2469 option
.newcap
= true;
2470 option
.undef
= true;
2473 // there's no directive negation, so always set to true
2474 directive
[token
.value
] = true;
2487 * Parses a single block. A block is a sequence of statements wrapped in
2490 * ordinary - true for everything but function bodies and try blocks.
2491 * stmt - true if block can be a single statement (e.g. in if/for/while).
2492 * isfunc - true if block is a function body
2494 function block(ordinary
, stmt
, isfunc
) {
2497 old_indent
= indent
,
2505 if (!ordinary
|| !option
.funcscope
) scope
= Object
.create(scope
);
2506 nonadjacent(token
, nexttoken
);
2509 if (nexttoken
.id
=== '{') {
2512 if (nexttoken
.id
!== '}') {
2513 indent
+= option
.indent
;
2514 while (!ordinary
&& nexttoken
.from
> indent
) {
2515 indent
+= option
.indent
;
2520 for (d
in directive
) {
2521 if (is_own(directive
, d
)) {
2522 m
[d
] = directive
[d
];
2527 if (option
.strict
&& funct
['(context)']['(global)']) {
2528 if (!m
["use strict"] && !directive
["use strict"]) {
2529 warning("Missing \"use strict\" statement.");
2534 a
= statements(line
);
2540 indent
-= option
.indent
;
2541 if (line
!== nexttoken
.line
) {
2544 } else if (line
!== nexttoken
.line
) {
2548 indent
= old_indent
;
2549 } else if (!ordinary
) {
2550 error("Expected '{a}' and instead saw '{b}'.",
2551 nexttoken
, '{', nexttoken
.value
);
2553 if (!stmt
|| option
.curly
)
2554 warning("Expected '{a}' and instead saw '{b}'.",
2555 nexttoken
, '{', nexttoken
.value
);
2558 indent
+= option
.indent
;
2559 // test indentation only if statement is in new line
2560 a
= [statement(nexttoken
.line
=== token
.line
)];
2561 indent
-= option
.indent
;
2564 funct
['(verb)'] = null;
2565 if (!ordinary
|| !option
.funcscope
) scope
= s
;
2567 if (ordinary
&& option
.noempty
&& (!a
|| a
.length
=== 0)) {
2568 warning("Empty block.");
2574 function countMember(m
) {
2575 if (membersOnly
&& typeof membersOnly
[m
] !== 'boolean') {
2576 warning("Unexpected /*member '{a}'.", token
, m
);
2578 if (typeof member
[m
] === 'number') {
2586 function note_implied(token
) {
2587 var name
= token
.value
, line
= token
.line
, a
= implied
[name
];
2588 if (typeof a
=== 'function') {
2594 } else if (a
[a
.length
- 1] !== line
) {
2600 // Build the syntax table by declaring the syntactic elements of the language.
2602 type('(number)', function () {
2606 type('(string)', function () {
2610 syntax
['(identifier)'] = {
2611 type
: '(identifier)',
2619 if (typeof s
=== 'function') {
2620 // Protection against accidental inheritance.
2622 } else if (typeof s
=== 'boolean') {
2624 funct
= functions
[0];
2630 // The name is in scope and defined in the current function.
2632 // Change 'unused' to 'var', and reject labels.
2638 funct
[v
] = 'function';
2639 this['function'] = true;
2642 this['function'] = true;
2645 warning("'{a}' is a statement label.", token
, v
);
2648 } else if (funct
['(global)']) {
2649 // The name is not defined in the function. If we are in the global
2650 // scope, then we have an undefined variable.
2652 // Operators typeof and delete do not raise runtime errors even if
2653 // the base object of a reference is null so no need to display warning
2654 // if we're inside of typeof or delete.
2655 if (anonname
!== 'typeof' && anonname
!== 'delete' &&
2656 option
.undef
&& typeof predefined
[v
] !== 'boolean') {
2657 isundef(funct
, "'{a}' is not defined.", token
, v
);
2659 note_implied(token
);
2661 // If the name is already defined in the current
2662 // function, but not as outer, then there is a scope error.
2669 warning("'{a}' used out of scope.", token
, v
);
2672 warning("'{a}' is a statement label.", token
, v
);
2678 // If the name is defined in an outer function, make an outer entry,
2679 // and if it was unused, make it var.
2682 } else if (s
=== null) {
2683 warning("'{a}' is not allowed.", token
, v
);
2684 note_implied(token
);
2685 } else if (typeof s
!== 'object') {
2686 // Operators typeof and delete do not raise runtime errors even
2687 // if the base object of a reference is null so no need to
2688 // display warning if we're inside of typeof or delete.
2689 if (anonname
!== 'typeof' && anonname
!== 'delete' && option
.undef
) {
2690 isundef(funct
, "'{a}' is not defined.", token
, v
);
2693 note_implied(token
);
2698 this['function'] = true;
2700 funct
[v
] = s
['(global)'] ? 'global' : 'outer';
2705 funct
[v
] = s
['(global)'] ? 'global' : 'outer';
2709 funct
[v
] = s
['(global)'] ? 'global' : 'outer';
2712 warning("'{a}' is a statement label.", token
, v
);
2720 error("Expected an operator and instead saw '{a}'.",
2721 nexttoken
, nexttoken
.value
);
2725 type('(regexp)', function () {
2730 // ECMAScript parser
2734 delim('(end)').reach
= true;
2735 delim('</').reach
= true;
2739 delim('(error)').reach
= true;
2740 delim('}').reach
= true;
2743 delim('"').reach
= true;
2744 delim("'").reach
= true;
2746 delim(':').reach
= true;
2751 reserve('case').reach
= true;
2753 reserve('default').reach
= true;
2755 reservevar('arguments', function (x
) {
2756 if (directive
['use strict'] && funct
['(global)']) {
2757 warning("Strict violation.", x
);
2761 reservevar('false');
2762 reservevar('Infinity');
2765 reservevar('this', function (x
) {
2766 if (directive
['use strict'] && !option
.validthis
&& ((funct
['(statement)'] &&
2767 funct
['(name)'].charAt(0) > 'Z') || funct
['(global)'])) {
2768 warning("Possible strict violation.", x
);
2772 reservevar('undefined');
2773 assignop('=', 'assign', 20);
2774 assignop('+=', 'assignadd', 20);
2775 assignop('-=', 'assignsub', 20);
2776 assignop('*=', 'assignmult', 20);
2777 assignop('/=', 'assigndiv', 20).nud
= function () {
2778 error("A regular expression literal can be confused with '/='.");
2780 assignop('%=', 'assignmod', 20);
2781 bitwiseassignop('&=', 'assignbitand', 20);
2782 bitwiseassignop('|=', 'assignbitor', 20);
2783 bitwiseassignop('^=', 'assignbitxor', 20);
2784 bitwiseassignop('<<=', 'assignshiftleft', 20);
2785 bitwiseassignop('>>=', 'assignshiftright', 20);
2786 bitwiseassignop('>>>=', 'assignshiftrightunsigned', 20);
2787 infix('?', function (left
, that
) {
2789 that
.right
= expression(10);
2791 that
['else'] = expression(10);
2795 infix('||', 'or', 40);
2796 infix('&&', 'and', 50);
2797 bitwise('|', 'bitor', 70);
2798 bitwise('^', 'bitxor', 80);
2799 bitwise('&', 'bitand', 90);
2800 relation('==', function (left
, right
) {
2801 var eqnull
= option
.eqnull
&& (left
.value
=== 'null' || right
.value
=== 'null');
2803 if (!eqnull
&& option
.eqeqeq
)
2804 warning("Expected '{a}' and instead saw '{b}'.", this, '===', '==');
2805 else if (isPoorRelation(left
))
2806 warning("Use '{a}' to compare with '{b}'.", this, '===', left
.value
);
2807 else if (isPoorRelation(right
))
2808 warning("Use '{a}' to compare with '{b}'.", this, '===', right
.value
);
2813 relation('!=', function (left
, right
) {
2814 var eqnull
= option
.eqnull
&&
2815 (left
.value
=== 'null' || right
.value
=== 'null');
2817 if (!eqnull
&& option
.eqeqeq
) {
2818 warning("Expected '{a}' and instead saw '{b}'.",
2820 } else if (isPoorRelation(left
)) {
2821 warning("Use '{a}' to compare with '{b}'.",
2822 this, '!==', left
.value
);
2823 } else if (isPoorRelation(right
)) {
2824 warning("Use '{a}' to compare with '{b}'.",
2825 this, '!==', right
.value
);
2834 bitwise('<<', 'shiftleft', 120);
2835 bitwise('>>', 'shiftright', 120);
2836 bitwise('>>>', 'shiftrightunsigned', 120);
2837 infix('in', 'in', 120);
2838 infix('instanceof', 'instanceof', 120);
2839 infix('+', function (left
, that
) {
2840 var right
= expression(130);
2841 if (left
&& right
&& left
.id
=== '(string)' && right
.id
=== '(string)') {
2842 left
.value
+= right
.value
;
2843 left
.character
= right
.character
;
2844 if (!option
.scripturl
&& jx
.test(left
.value
)) {
2845 warning("JavaScript URL.", left
);
2854 prefix('+++', function () {
2855 warning("Confusing pluses.");
2856 this.right
= expression(150);
2857 this.arity
= 'unary';
2860 infix('+++', function (left
) {
2861 warning("Confusing pluses.");
2863 this.right
= expression(130);
2866 infix('-', 'sub', 130);
2868 prefix('---', function () {
2869 warning("Confusing minuses.");
2870 this.right
= expression(150);
2871 this.arity
= 'unary';
2874 infix('---', function (left
) {
2875 warning("Confusing minuses.");
2877 this.right
= expression(130);
2880 infix('*', 'mult', 140);
2881 infix('/', 'div', 140);
2882 infix('%', 'mod', 140);
2884 suffix('++', 'postinc');
2885 prefix('++', 'preinc');
2886 syntax
['++'].exps
= true;
2888 suffix('--', 'postdec');
2889 prefix('--', 'predec');
2890 syntax
['--'].exps
= true;
2891 prefix('delete', function () {
2892 var p
= expression(0);
2893 if (!p
|| (p
.id
!== '.' && p
.id
!== '[')) {
2894 warning("Variables should not be deleted.");
2900 prefix('~', function () {
2901 if (option
.bitwise
) {
2902 warning("Unexpected '{a}'.", this, '~');
2908 prefix('!', function () {
2909 this.right
= expression(150);
2910 this.arity
= 'unary';
2911 if (bang
[this.right
.id
] === true) {
2912 warning("Confusing use of '{a}'.", this, '!');
2916 prefix('typeof', 'typeof');
2917 prefix('new', function () {
2918 var c
= expression(155), i
;
2919 if (c
&& c
.id
!== 'function') {
2924 warning("Use the object literal notation {}.", token
);
2931 warning("Do not use {a} as a constructor.", token
, c
.value
);
2935 warning("The Function constructor is eval.");
2942 if (c
.id
!== 'function') {
2943 i
= c
.value
.substr(0, 1);
2944 if (option
.newcap
&& (i
< 'A' || i
> 'Z')) {
2945 warning("A constructor name should start with an uppercase letter.",
2951 if (c
.id
!== '.' && c
.id
!== '[' && c
.id
!== '(') {
2952 warning("Bad constructor.", token
);
2956 if (!option
.supernew
)
2957 warning("Weird construction. Delete 'new'.", this);
2959 adjacent(token
, nexttoken
);
2960 if (nexttoken
.id
!== '(' && !option
.supernew
) {
2961 warning("Missing '()' invoking a constructor.");
2966 syntax
['new'].exps
= true;
2968 prefix('void').exps
= true;
2970 infix('.', function (left
, that
) {
2971 adjacent(prevtoken
, token
);
2973 var m
= identifier();
2974 if (typeof m
=== 'string') {
2979 if (left
&& left
.value
=== 'arguments' && (m
=== 'callee' || m
=== 'caller')) {
2981 warning("Avoid arguments.{a}.", left
, m
);
2982 else if (directive
['use strict'])
2983 error('Strict violation.');
2984 } else if (!option
.evil
&& left
&& left
.value
=== 'document' &&
2985 (m
=== 'write' || m
=== 'writeln')) {
2986 warning("document.write can be a form of eval.", left
);
2988 if (!option
.evil
&& (m
=== 'eval' || m
=== 'execScript')) {
2989 warning('eval is evil.');
2994 infix('(', function (left
, that
) {
2995 if (prevtoken
.id
!== '}' && prevtoken
.id
!== ')') {
2996 nobreak(prevtoken
, token
);
2999 if (option
.immed
&& !left
.immed
&& left
.id
=== 'function') {
3000 warning("Wrap an immediate function invocation in parentheses " +
3001 "to assist the reader in understanding that the expression " +
3002 "is the result of a function, and not the function itself.");
3007 if (left
.type
=== '(identifier)') {
3008 if (left
.value
.match(/^[A-Z]([A-Z0-9_$]*[a-z][A-Za-z0-9_$]*)?$/)) {
3009 if (left
.value
!== 'Number' && left
.value
!== 'String' &&
3010 left
.value
!== 'Boolean' &&
3011 left
.value
!== 'Date') {
3012 if (left
.value
=== 'Math') {
3013 warning("Math is not a function.", left
);
3014 } else if (option
.newcap
) {
3016 "Missing 'new' prefix when invoking a constructor.", left
);
3022 if (nexttoken
.id
!== ')') {
3024 p
[p
.length
] = expression(10);
3026 if (nexttoken
.id
!== ',') {
3033 nospace(prevtoken
, token
);
3034 if (typeof left
=== 'object') {
3035 if (left
.value
=== 'parseInt' && n
=== 1) {
3036 warning("Missing radix parameter.", left
);
3039 if (left
.value
=== 'eval' || left
.value
=== 'Function' ||
3040 left
.value
=== 'execScript') {
3041 warning("eval is evil.", left
);
3042 } else if (p
[0] && p
[0].id
=== '(string)' &&
3043 (left
.value
=== 'setTimeout' ||
3044 left
.value
=== 'setInterval')) {
3046 "Implied eval is evil. Pass a function instead of a string.", left
);
3049 if (!left
.identifier
&& left
.id
!== '.' && left
.id
!== '[' &&
3050 left
.id
!== '(' && left
.id
!== '&&' && left
.id
!== '||' &&
3052 warning("Bad invocation.", left
);
3057 }, 155, true).exps
= true;
3059 prefix('(', function () {
3061 if (nexttoken
.id
=== 'function') {
3062 nexttoken
.immed
= true;
3064 var v
= expression(0);
3066 nospace(prevtoken
, token
);
3067 if (option
.immed
&& v
.id
=== 'function') {
3068 if (nexttoken
.id
=== '(') {
3070 "Move the invocation into the parens that contain the function.", nexttoken
);
3073 "Do not wrap function literals in parens unless they are to be immediately invoked.",
3080 infix('[', function (left
, that
) {
3081 nobreak(prevtoken
, token
);
3083 var e
= expression(0), s
;
3084 if (e
&& e
.type
=== '(string)') {
3085 if (!option
.evil
&& (e
.value
=== 'eval' || e
.value
=== 'execScript')) {
3086 warning("eval is evil.", that
);
3088 countMember(e
.value
);
3089 if (!option
.sub
&& ix
.test(e
.value
)) {
3090 s
= syntax
[e
.value
];
3091 if (!s
|| !s
.reserved
) {
3092 warning("['{a}'] is better written in dot notation.",
3098 nospace(prevtoken
, token
);
3104 prefix('[', function () {
3105 var b
= token
.line
!== nexttoken
.line
;
3108 indent
+= option
.indent
;
3109 if (nexttoken
.from
=== indent
+ option
.indent
) {
3110 indent
+= option
.indent
;
3113 while (nexttoken
.id
!== '(end)') {
3114 while (nexttoken
.id
=== ',') {
3115 warning("Extra comma.");
3118 if (nexttoken
.id
=== ']') {
3121 if (b
&& token
.line
!== nexttoken
.line
) {
3124 this.first
.push(expression(10));
3125 if (nexttoken
.id
=== ',') {
3127 if (nexttoken
.id
=== ']' && !option
.es5
) {
3128 warning("Extra comma.", token
);
3136 indent
-= option
.indent
;
3144 function property_name() {
3145 var id
= optionalidentifier(true);
3147 if (nexttoken
.id
=== '(string)') {
3148 id
= nexttoken
.value
;
3150 } else if (nexttoken
.id
=== '(number)') {
3151 id
= nexttoken
.value
.toString();
3159 function functionparams() {
3160 var i
, t
= nexttoken
, p
= [];
3163 if (nexttoken
.id
=== ')') {
3168 i
= identifier(true);
3170 addlabel(i
, 'parameter');
3171 if (nexttoken
.id
=== ',') {
3175 nospace(prevtoken
, token
);
3182 function doFunction(i
, statement
) {
3187 option
= Object
.create(option
);
3188 scope
= Object
.create(scope
);
3191 '(name)' : i
|| '"' + anonname
+ '"',
3192 '(line)' : nexttoken
.line
,
3193 '(context)' : funct
,
3197 '(statement)': statement
3200 token
.funct
= funct
;
3201 functions
.push(funct
);
3203 addlabel(i
, 'function');
3205 funct
['(params)'] = functionparams();
3207 block(false, false, true);
3210 funct
['(last)'] = token
.line
;
3211 funct
= funct
['(context)'];
3217 x
.nud
= function () {
3218 var b
, f
, i
, j
, p
, seen
= {}, t
;
3219 var prop
, acc
= {}; // Accessor methods
3221 function saveSetter(name
, token
) {
3225 acc
[name
].setter
= true;
3226 acc
[name
].setterToken
= token
;
3229 function saveGetter(name
) {
3233 acc
[name
].getter
= true;
3236 b
= token
.line
!== nexttoken
.line
;
3238 indent
+= option
.indent
;
3239 if (nexttoken
.from
=== indent
+ option
.indent
) {
3240 indent
+= option
.indent
;
3244 if (nexttoken
.id
=== '}') {
3250 if (nexttoken
.value
=== 'get' && peek().id
!== ':') {
3253 error("get/set are ES5 features.");
3255 i
= property_name();
3257 error("Missing property name.");
3261 adjacent(token
, nexttoken
);
3263 if (!option
.loopfunc
&& funct
['(loopage)']) {
3264 warning("Don't make functions within a loop.", t
);
3268 warning("Unexpected parameter '{a}' in get {b} function.", t
, p
[0], i
);
3270 adjacent(token
, nexttoken
);
3271 } else if (nexttoken
.value
=== 'set' && peek().id
!== ':') {
3274 error("get/set are ES5 features.");
3276 i
= property_name();
3278 error("Missing property name.");
3280 if (acc
[i
] && acc
[i
].setter
) {
3281 warning("Duplicate member '{a}'.", nexttoken
, i
);
3283 saveSetter(i
, nexttoken
);
3286 adjacent(token
, nexttoken
);
3289 if (!p
|| p
.length
!== 1 || p
[0] !== 'value') {
3290 warning("Expected (value) in set {a} function.", t
, i
);
3293 i
= property_name();
3294 if (typeof i
!== 'string') {
3298 nonadjacent(token
, nexttoken
);
3301 if (seen
[i
] === true) {
3302 warning("Duplicate member '{a}'.", nexttoken
, i
);
3306 if (nexttoken
.id
=== ',') {
3308 if (nexttoken
.id
=== ',') {
3309 warning("Extra comma.", token
);
3310 } else if (nexttoken
.id
=== '}' && !option
.es5
) {
3311 warning("Extra comma.", token
);
3318 indent
-= option
.indent
;
3323 // Check for lonely setters if in the ES5 mode.
3326 if (acc
.hasOwnProperty(prop
) && acc
[prop
].setter
&& !acc
[prop
].getter
) {
3327 warning("Setter is defined without getter.", acc
[prop
].setterToken
);
3333 x
.fud
= function () {
3334 error("Expected to see a statement and instead saw a block.", token
);
3338 // This Function is called when esnext option is set to true
3339 // it adds the `const` statement to JSHINT
3341 useESNextSyntax
= function () {
3342 var conststatement
= stmt('const', function (prefix
) {
3343 var id
, name
, value
;
3347 nonadjacent(token
, nexttoken
);
3349 if (funct
[id
] === "const") {
3350 warning("const '" + id
+ "' has already been declared");
3352 if (funct
['(global)'] && predefined
[id
] === false) {
3353 warning("Redefinition of '{a}'.", token
, id
);
3355 addlabel(id
, 'const');
3360 this.first
.push(token
);
3362 if (nexttoken
.id
!== "=") {
3364 "'{a}' is initialized to 'undefined'.", token
, id
);
3367 if (nexttoken
.id
=== '=') {
3368 nonadjacent(token
, nexttoken
);
3370 nonadjacent(token
, nexttoken
);
3371 if (nexttoken
.id
=== 'undefined') {
3372 warning("It is not necessary to initialize " +
3373 "'{a}' to 'undefined'.", token
, id
);
3375 if (peek(0).id
=== '=' && nexttoken
.identifier
) {
3376 error("Constant {a} was not declared correctly.",
3377 nexttoken
, nexttoken
.value
);
3379 value
= expression(0);
3383 if (nexttoken
.id
!== ',') {
3390 conststatement
.exps
= true;
3393 var varstatement
= stmt('var', function (prefix
) {
3394 // JavaScript does not have block scope. It only has function scope. So,
3395 // declaring a variable in a block can have unexpected consequences.
3396 var id
, name
, value
;
3398 if (funct
['(onevar)'] && option
.onevar
) {
3399 warning("Too many var statements.");
3400 } else if (!funct
['(global)']) {
3401 funct
['(onevar)'] = true;
3405 nonadjacent(token
, nexttoken
);
3407 if (option
.esnext
&& funct
[id
] === "const") {
3408 warning("const '" + id
+ "' has already been declared");
3410 if (funct
['(global)'] && predefined
[id
] === false) {
3411 warning("Redefinition of '{a}'.", token
, id
);
3413 addlabel(id
, 'unused');
3418 this.first
.push(token
);
3419 if (nexttoken
.id
=== '=') {
3420 nonadjacent(token
, nexttoken
);
3422 nonadjacent(token
, nexttoken
);
3423 if (nexttoken
.id
=== 'undefined') {
3424 warning("It is not necessary to initialize '{a}' to 'undefined'.", token
, id
);
3426 if (peek(0).id
=== '=' && nexttoken
.identifier
) {
3427 error("Variable {a} was not declared correctly.",
3428 nexttoken
, nexttoken
.value
);
3430 value
= expression(0);
3433 if (nexttoken
.id
!== ',') {
3440 varstatement
.exps
= true;
3442 blockstmt('function', function () {
3444 warning("Function declarations should not be placed in blocks. " +
3445 "Use a function expression or move the statement to the top of " +
3446 "the outer function.", token
);
3449 var i
= identifier();
3450 if (option
.esnext
&& funct
[i
] === "const") {
3451 warning("const '" + i
+ "' has already been declared");
3453 adjacent(token
, nexttoken
);
3454 addlabel(i
, 'unction');
3455 doFunction(i
, true);
3456 if (nexttoken
.id
=== '(' && nexttoken
.line
=== token
.line
) {
3458 "Function declarations are not invocable. Wrap the whole function invocation in parens.");
3463 prefix('function', function () {
3464 var i
= optionalidentifier();
3466 adjacent(token
, nexttoken
);
3468 nonadjacent(token
, nexttoken
);
3471 if (!option
.loopfunc
&& funct
['(loopage)']) {
3472 warning("Don't make functions within a loop.");
3477 blockstmt('if', function () {
3480 nonadjacent(this, t
);
3483 if (nexttoken
.id
=== '=') {
3485 warning("Expected a conditional expression and instead saw an assignment.");
3490 nospace(prevtoken
, token
);
3492 if (nexttoken
.id
=== 'else') {
3493 nonadjacent(token
, nexttoken
);
3495 if (nexttoken
.id
=== 'if' || nexttoken
.id
=== 'switch') {
3504 blockstmt('try', function () {
3508 if (nexttoken
.id
=== 'catch') {
3510 nonadjacent(token
, nexttoken
);
3513 scope
= Object
.create(s
);
3514 e
= nexttoken
.value
;
3515 if (nexttoken
.type
!== '(identifier)') {
3516 warning("Expected an identifier and instead saw '{a}'.",
3519 addlabel(e
, 'exception');
3527 if (nexttoken
.id
=== 'finally') {
3532 error("Expected '{a}' and instead saw '{b}'.",
3533 nexttoken
, 'catch', nexttoken
.value
);
3538 blockstmt('while', function () {
3540 funct
['(breakage)'] += 1;
3541 funct
['(loopage)'] += 1;
3543 nonadjacent(this, t
);
3546 if (nexttoken
.id
=== '=') {
3548 warning("Expected a conditional expression and instead saw an assignment.");
3553 nospace(prevtoken
, token
);
3555 funct
['(breakage)'] -= 1;
3556 funct
['(loopage)'] -= 1;
3562 blockstmt('switch', function () {
3565 funct
['(breakage)'] += 1;
3567 nonadjacent(this, t
);
3569 this.condition
= expression(20);
3571 nospace(prevtoken
, token
);
3572 nonadjacent(token
, nexttoken
);
3575 nonadjacent(token
, nexttoken
);
3576 indent
+= option
.indent
;
3579 switch (nexttoken
.id
) {
3581 switch (funct
['(verb)']) {
3590 // You can tell JSHint that you don't use break intentionally by
3591 // adding a comment /* falls through */ on a line just before
3593 if (!ft
.test(lines
[nexttoken
.line
- 2])) {
3595 "Expected a 'break' statement before 'case'.",
3599 indentation(-option
.indent
);
3601 this.cases
.push(expression(20));
3604 funct
['(verb)'] = 'case';
3607 switch (funct
['(verb)']) {
3614 if (!ft
.test(lines
[nexttoken
.line
- 2])) {
3616 "Expected a 'break' statement before 'default'.",
3620 indentation(-option
.indent
);
3626 indent
-= option
.indent
;
3629 if (this.cases
.length
=== 1 || this.condition
.id
=== 'true' ||
3630 this.condition
.id
=== 'false') {
3631 if (!option
.onecase
)
3632 warning("This 'switch' should be an 'if'.", this);
3634 funct
['(breakage)'] -= 1;
3635 funct
['(verb)'] = undefined
;
3638 error("Missing '{a}'.", nexttoken
, '}');
3644 error("Each value should have its own case label.");
3651 error("Missing ':' on a case clause.", token
);
3655 if (token
.id
=== ':') {
3657 error("Unexpected '{a}'.", token
, ':');
3660 error("Expected '{a}' and instead saw '{b}'.",
3661 nexttoken
, 'case', nexttoken
.value
);
3669 stmt('debugger', function () {
3670 if (!option
.debug
) {
3671 warning("All 'debugger' statements should be removed.");
3677 var x
= stmt('do', function () {
3678 funct
['(breakage)'] += 1;
3679 funct
['(loopage)'] += 1;
3680 this.first
= block(true);
3683 nonadjacent(token
, t
);
3687 if (nexttoken
.id
=== '=') {
3689 warning("Expected a conditional expression and instead saw an assignment.");
3694 nospace(prevtoken
, token
);
3695 funct
['(breakage)'] -= 1;
3696 funct
['(loopage)'] -= 1;
3703 blockstmt('for', function () {
3704 var s
, t
= nexttoken
;
3705 funct
['(breakage)'] += 1;
3706 funct
['(loopage)'] += 1;
3708 nonadjacent(this, t
);
3710 if (peek(nexttoken
.id
=== 'var' ? 1 : 0).id
=== 'in') {
3711 if (nexttoken
.id
=== 'var') {
3713 varstatement
.fud
.call(varstatement
, true);
3715 switch (funct
[nexttoken
.value
]) {
3717 funct
[nexttoken
.value
] = 'var';
3722 warning("Bad for in variable '{a}'.",
3723 nexttoken
, nexttoken
.value
);
3730 s
= block(true, true);
3731 if (option
.forin
&& s
&& (s
.length
> 1 || typeof s
[0] !== 'object' ||
3732 s
[0].value
!== 'if')) {
3733 warning("The body of a for in should be wrapped in an if statement to filter " +
3734 "unwanted properties from the prototype.", this);
3736 funct
['(breakage)'] -= 1;
3737 funct
['(loopage)'] -= 1;
3740 if (nexttoken
.id
!== ';') {
3741 if (nexttoken
.id
=== 'var') {
3743 varstatement
.fud
.call(varstatement
);
3746 expression(0, 'for');
3747 if (nexttoken
.id
!== ',') {
3756 if (nexttoken
.id
!== ';') {
3758 if (nexttoken
.id
=== '=') {
3760 warning("Expected a conditional expression and instead saw an assignment.");
3767 if (nexttoken
.id
=== ';') {
3768 error("Expected '{a}' and instead saw '{b}'.",
3769 nexttoken
, ')', ';');
3771 if (nexttoken
.id
!== ')') {
3773 expression(0, 'for');
3774 if (nexttoken
.id
!== ',') {
3781 nospace(prevtoken
, token
);
3783 funct
['(breakage)'] -= 1;
3784 funct
['(loopage)'] -= 1;
3790 stmt('break', function () {
3791 var v
= nexttoken
.value
;
3793 if (funct
['(breakage)'] === 0)
3794 warning("Unexpected '{a}'.", nexttoken
, this.value
);
3799 if (nexttoken
.id
!== ';') {
3800 if (token
.line
=== nexttoken
.line
) {
3801 if (funct
[v
] !== 'label') {
3802 warning("'{a}' is not a statement label.", nexttoken
, v
);
3803 } else if (scope
[v
] !== funct
) {
3804 warning("'{a}' is out of scope.", nexttoken
, v
);
3806 this.first
= nexttoken
;
3815 stmt('continue', function () {
3816 var v
= nexttoken
.value
;
3818 if (funct
['(breakage)'] === 0)
3819 warning("Unexpected '{a}'.", nexttoken
, this.value
);
3824 if (nexttoken
.id
!== ';') {
3825 if (token
.line
=== nexttoken
.line
) {
3826 if (funct
[v
] !== 'label') {
3827 warning("'{a}' is not a statement label.", nexttoken
, v
);
3828 } else if (scope
[v
] !== funct
) {
3829 warning("'{a}' is out of scope.", nexttoken
, v
);
3831 this.first
= nexttoken
;
3834 } else if (!funct
['(loopage)']) {
3835 warning("Unexpected '{a}'.", nexttoken
, this.value
);
3837 reachable('continue');
3842 stmt('return', function () {
3843 if (this.line
=== nexttoken
.line
) {
3844 if (nexttoken
.id
=== '(regexp)')
3845 warning("Wrap the /regexp/ literal in parens to disambiguate the slash operator.");
3847 if (nexttoken
.id
!== ';' && !nexttoken
.reach
) {
3848 nonadjacent(token
, nexttoken
);
3849 if (peek().value
=== "=" && !option
.boss
) {
3850 warningAt("Did you mean to return a conditional instead of an assignment?",
3851 token
.line
, token
.character
+ 1);
3853 this.first
= expression(0);
3855 } else if (!option
.asi
) {
3856 nolinebreak(this); // always warn (Line breaking error)
3858 reachable('return');
3863 stmt('throw', function () {
3865 nonadjacent(token
, nexttoken
);
3866 this.first
= expression(20);
3871 // Superfluous reserved words
3883 reserve('implements');
3884 reserve('interface');
3887 reserve('protected');
3894 function jsonValue() {
3896 function jsonObject() {
3897 var o
= {}, t
= nexttoken
;
3899 if (nexttoken
.id
!== '}') {
3901 if (nexttoken
.id
=== '(end)') {
3902 error("Missing '}' to match '{' from line {a}.",
3904 } else if (nexttoken
.id
=== '}') {
3905 warning("Unexpected comma.", token
);
3907 } else if (nexttoken
.id
=== ',') {
3908 error("Unexpected comma.", nexttoken
);
3909 } else if (nexttoken
.id
!== '(string)') {
3910 warning("Expected a string and instead saw {a}.",
3911 nexttoken
, nexttoken
.value
);
3913 if (o
[nexttoken
.value
] === true) {
3914 warning("Duplicate key '{a}'.",
3915 nexttoken
, nexttoken
.value
);
3916 } else if ((nexttoken
.value
=== '__proto__' &&
3917 !option
.proto
) || (nexttoken
.value
=== '__iterator__' &&
3918 !option
.iterator
)) {
3919 warning("The '{a}' key may produce unexpected results.",
3920 nexttoken
, nexttoken
.value
);
3922 o
[nexttoken
.value
] = true;
3927 if (nexttoken
.id
!== ',') {
3936 function jsonArray() {
3939 if (nexttoken
.id
!== ']') {
3941 if (nexttoken
.id
=== '(end)') {
3942 error("Missing ']' to match '[' from line {a}.",
3944 } else if (nexttoken
.id
=== ']') {
3945 warning("Unexpected comma.", token
);
3947 } else if (nexttoken
.id
=== ',') {
3948 error("Unexpected comma.", nexttoken
);
3951 if (nexttoken
.id
!== ',') {
3960 switch (nexttoken
.id
) {
3976 if (token
.character
!== nexttoken
.from
) {
3977 warning("Unexpected space after '-'.", token
);
3979 adjacent(token
, nexttoken
);
3980 advance('(number)');
3983 error("Expected a JSON value.", nexttoken
);
3988 // The actual JSHINT function itself.
3990 var itself
= function (s
, o
, g
) {
3994 predefined
= Object
.create(standard
);
3995 combine(predefined
, g
|| {});
3999 if (Array
.isArray(a
)) {
4000 for (i
= 0; i
< a
.length
; i
+= 1) {
4001 predefined
[a
[i
]] = true;
4003 } else if (typeof a
=== 'object') {
4005 for (i
= 0; i
< k
.length
; i
+= 1) {
4006 predefined
[k
[i
]] = !!a
[k
[i
]];
4014 option
.indent
= option
.indent
|| 4;
4015 option
.maxerr
= option
.maxerr
|| 50;
4018 for (i
= 0; i
< option
.indent
; i
+= 1) {
4022 global
= Object
.create(predefined
);
4026 '(name)': '(global)',
4031 functions
= [funct
];
4045 prevtoken
= token
= nexttoken
= syntax
['(begin)'];
4048 // combine the passed globals after we've assumed all our options
4049 combine(predefined
, g
|| {});
4053 switch (nexttoken
.id
) {
4056 option
.laxbreak
= true;
4062 if (directive
["use strict"] && !option
.globalstrict
) {
4063 warning("Use the function form of \"use strict\".", prevtoken
);
4071 var nt
= nexttoken
|| {};
4072 JSHINT
.errors
.push({
4075 line
: e
.line
|| nt
.line
,
4076 character
: e
.character
|| nt
.from
4081 for (i
= 0; i
< JSHINT
.undefs
.length
; i
+= 1) {
4082 k
= JSHINT
.undefs
[i
].slice(0);
4086 if (typeof scope
[a
] !== 'string' && typeof funct
[a
] !== 'string') {
4087 warning
.apply(warning
, k
);
4091 return JSHINT
.errors
.length
=== 0;
4095 itself
.data
= function () {
4097 var data
= { functions
: [], options
: option
}, fu
, globals
, implieds
= [], f
, i
, j
,
4098 members
= [], n
, unused
= [], v
;
4099 if (itself
.errors
.length
) {
4100 data
.errors
= itself
.errors
;
4107 for (n
in implied
) {
4108 if (is_own(implied
, n
)) {
4115 if (implieds
.length
> 0) {
4116 data
.implieds
= implieds
;
4119 if (urls
.length
> 0) {
4123 globals
= Object
.keys(scope
);
4124 if (globals
.length
> 0) {
4125 data
.globals
= globals
;
4128 for (i
= 1; i
< functions
.length
; i
+= 1) {
4131 for (j
= 0; j
< functionicity
.length
; j
+= 1) {
4132 fu
[functionicity
[j
]] = [];
4135 if (is_own(f
, n
) && n
.charAt(0) !== '(') {
4137 if (v
=== 'unction') {
4140 if (Array
.isArray(fu
[v
])) {
4142 if (v
=== 'unused') {
4146 'function': f
['(name)']
4152 for (j
= 0; j
< functionicity
.length
; j
+= 1) {
4153 if (fu
[functionicity
[j
]].length
=== 0) {
4154 delete fu
[functionicity
[j
]];
4157 fu
.name
= f
['(name)'];
4158 fu
.param
= f
['(params)'];
4159 fu
.line
= f
['(line)'];
4160 fu
.last
= f
['(last)'];
4161 data
.functions
.push(fu
);
4164 if (unused
.length
> 0) {
4165 data
.unused
= unused
;
4170 if (typeof member
[n
] === 'number') {
4171 data
.member
= member
;
4179 itself
.report
= function (option
) {
4180 var data
= itself
.data();
4182 var a
= [], c
, e
, err
, f
, i
, k
, l
, m
= '', n
, o
= [], s
;
4184 function detail(h
, array
) {
4185 var b
, i
, singularity
;
4187 o
.push('<div><i>' + h
+ '</i> ');
4188 array
= array
.sort();
4189 for (i
= 0; i
< array
.length
; i
+= 1) {
4190 if (array
[i
] !== singularity
) {
4191 singularity
= array
[i
];
4192 o
.push((b
? ', ' : '') + singularity
);
4201 if (data
.errors
|| data
.implieds
|| data
.unused
) {
4203 o
.push('<div id=errors><i>Error:</i>');
4205 for (i
= 0; i
< data
.errors
.length
; i
+= 1) {
4208 e
= c
.evidence
|| '';
4209 o
.push('<p>Problem' + (isFinite(c
.line
) ? ' at line ' +
4210 c
.line
+ ' character ' + c
.character
: '') +
4211 ': ' + c
.reason
.entityify() +
4212 '</p><p class=evidence>' +
4213 (e
&& (e
.length
> 80 ? e
.slice(0, 77) + '...' :
4214 e
).entityify()) + '</p>');
4219 if (data
.implieds
) {
4221 for (i
= 0; i
< data
.implieds
.length
; i
+= 1) {
4222 s
[i
] = '<code>' + data
.implieds
[i
].name
+ '</code> <i>' +
4223 data
.implieds
[i
].line
+ '</i>';
4225 o
.push('<p><i>Implied global:</i> ' + s.join(', ') + '</p>');
4230 for (i
= 0; i
< data
.unused
.length
; i
+= 1) {
4231 s
[i
] = '<code><u>' + data
.unused
[i
].name
+ '</u></code> <i>' +
4232 data
.unused
[i
].line
+ '</i> <code>' +
4233 data
.unused
[i
]['function'] + '</code>';
4235 o
.push('<p><i>Unused variable:</i> ' + s.join(', ') + '</p>');
4238 o
.push('<p>JSON: bad.</p>');
4245 o
.push('<br><div id=functions>');
4248 detail("URLs<br>", data
.urls
, '<br>');
4251 if (data
.json
&& !err
) {
4252 o
.push('<p>JSON: good.</p>');
4253 } else if (data
.globals
) {
4254 o
.push('<div><i>Global</i> ' +
4255 data
.globals
.sort().join(', ') + '</div>');
4257 o
.push('<div><i>No new global variables introduced.</i></div>');
4260 for (i
= 0; i
< data
.functions
.length
; i
+= 1) {
4261 f
= data
.functions
[i
];
4263 o
.push('<br><div class=function><i>' + f
.line
+ '-' +
4264 f
.last
+ '</i> ' + (f
.name
|| '') + '(' +
4265 (f
.param
? f
.param
.join(', ') : '') + ')</div>');
4266 detail('<big><b>Unused</b></big>', f
.unused
);
4267 detail('Closure', f
.closure
);
4268 detail('Variable', f
['var']);
4269 detail('Exception', f
.exception
);
4270 detail('Outer', f
.outer
);
4271 detail('Global', f
.global
);
4272 detail('Label', f
.label
);
4276 a
= Object
.keys(data
.member
);
4279 m
= '<br><pre id=members>/*members ';
4281 for (i
= 0; i
< a
.length
; i
+= 1) {
4284 if (l
+ n
.length
> 72) {
4290 if (data
.member
[k
] === 1) {
4291 n
= '<i>' + n
+ '</i>';
4293 if (i
< a
.length
- 1) {
4298 o
.push(m
+ '<br>*/</pre>');
4306 itself
.jshint
= itself
;
4307 itself
.edition
= '2011-04-16';
4312 // Make JSHINT a Node module, if possible.
4313 if (typeof exports
=== 'object' && exports
)
4314 exports
.JSHINT
= JSHINT
;