Mercurial > hg > ismi-richfaces
comparison src/main/webapp/imageServer/resources/js/jquery-ui-1.10.4/external/jshint.js @ 7:764f47286679
(none)
author | jurzua |
---|---|
date | Wed, 29 Oct 2014 14:28:34 +0000 |
parents | |
children |
comparison
equal
deleted
inserted
replaced
6:ded3bccf2cf9 | 7:764f47286679 |
---|---|
1 /*! | |
2 * JSHint, by JSHint Community. | |
3 * | |
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. | |
6 * | |
7 * JSHint is a derivative work of JSLint: | |
8 * | |
9 * Copyright (c) 2002 Douglas Crockford (www.JSLint.com) | |
10 * | |
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: | |
17 * | |
18 * The above copyright notice and this permission notice shall be included | |
19 * in all copies or substantial portions of the Software. | |
20 * | |
21 * The Software shall be used for Good, not Evil. | |
22 * | |
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. | |
30 * | |
31 */ | |
32 | |
33 /* | |
34 JSHINT is a global function. It takes two parameters. | |
35 | |
36 var myResult = JSHINT(source, option); | |
37 | |
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. | |
42 | |
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. | |
49 | |
50 If it checks out, JSHINT returns true. Otherwise, it returns false. | |
51 | |
52 If false, you can inspect JSHINT.errors to find out the problems. | |
53 JSHINT.errors is an array of objects containing these members: | |
54 | |
55 { | |
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 | |
58 reason : The problem | |
59 evidence : The text line in which the problem occurred | |
60 raw : The raw message before the details were inserted | |
61 a : The first detail | |
62 b : The second detail | |
63 c : The third detail | |
64 d : The fourth detail | |
65 } | |
66 | |
67 If a fatal error was found, a null will be the last element of the | |
68 JSHINT.errors array. | |
69 | |
70 You can request a data structure which contains JSHint's results. | |
71 | |
72 var myData = JSHINT.data(); | |
73 | |
74 It returns a structure with this form: | |
75 | |
76 { | |
77 errors: [ | |
78 { | |
79 line: NUMBER, | |
80 character: NUMBER, | |
81 reason: STRING, | |
82 evidence: STRING | |
83 } | |
84 ], | |
85 functions: [ | |
86 name: STRING, | |
87 line: NUMBER, | |
88 character: NUMBER, | |
89 last: NUMBER, | |
90 lastcharacter: NUMBER, | |
91 param: [ | |
92 STRING | |
93 ], | |
94 closure: [ | |
95 STRING | |
96 ], | |
97 var: [ | |
98 STRING | |
99 ], | |
100 exception: [ | |
101 STRING | |
102 ], | |
103 outer: [ | |
104 STRING | |
105 ], | |
106 unused: [ | |
107 STRING | |
108 ], | |
109 global: [ | |
110 STRING | |
111 ], | |
112 label: [ | |
113 STRING | |
114 ] | |
115 ], | |
116 globals: [ | |
117 STRING | |
118 ], | |
119 member: { | |
120 STRING: NUMBER | |
121 }, | |
122 unused: [ | |
123 { | |
124 name: STRING, | |
125 line: NUMBER | |
126 } | |
127 ], | |
128 implieds: [ | |
129 { | |
130 name: STRING, | |
131 line: NUMBER | |
132 } | |
133 ], | |
134 urls: [ | |
135 STRING | |
136 ], | |
137 json: BOOLEAN | |
138 } | |
139 | |
140 Empty arrays will not be included. | |
141 | |
142 */ | |
143 | |
144 /*jshint | |
145 evil: true, nomen: false, onevar: false, regexp: false, strict: true, boss: true, | |
146 undef: true, maxlen: 100, indent: 4, quotmark: double, unused: true | |
147 */ | |
148 | |
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*/ | |
223 | |
224 /*global exports: false */ | |
225 | |
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. | |
229 | |
230 var JSHINT = (function () { | |
231 "use strict"; | |
232 | |
233 var anonname, // The guessed name for anonymous functions. | |
234 | |
235 // These are operators that should not be used with the ! operator. | |
236 | |
237 bang = { | |
238 "<" : true, | |
239 "<=" : true, | |
240 "==" : true, | |
241 "===": true, | |
242 "!==": true, | |
243 "!=" : true, | |
244 ">" : true, | |
245 ">=" : true, | |
246 "+" : true, | |
247 "-" : true, | |
248 "*" : true, | |
249 "/" : true, | |
250 "%" : true | |
251 }, | |
252 | |
253 // These are the JSHint boolean options. | |
254 boolOptions = { | |
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, | |
264 // alert, etc.) | |
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 | |
275 // enables 'strict') | |
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 | |
285 // loops | |
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 | |
290 // disallowed | |
291 node : true, // if the Node.js environment globals should be | |
292 // predefined | |
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 | |
296 // be predefined | |
297 nomen : true, // if names should be checked | |
298 onevar : true, // if only one var statement per function should be | |
299 // allowed | |
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 | |
305 // predefined | |
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 | |
329 }, | |
330 | |
331 // These are the JSHint options that can take any value | |
332 // (we use this object to detect invalid options) | |
333 valOptions = { | |
334 maxlen : false, | |
335 indent : false, | |
336 maxerr : false, | |
337 predef : false, | |
338 quotmark : false, //'single'|'double'|true | |
339 scope : false, | |
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 | |
344 }, | |
345 | |
346 // These are JSHint boolean options which are shared with JSLint | |
347 // where the definition in JSHint is opposite JSLint | |
348 invertedOptions = { | |
349 bitwise : true, | |
350 forin : true, | |
351 newcap : true, | |
352 nomen : true, | |
353 plusplus : true, | |
354 regexp : true, | |
355 undef : true, | |
356 white : true, | |
357 | |
358 // Inverted and renamed, use JSHint name here | |
359 eqeqeq : true, | |
360 onevar : true | |
361 }, | |
362 | |
363 // These are JSHint boolean options which are shared with JSLint | |
364 // where the name has been changed but the effect is unchanged | |
365 renamedOptions = { | |
366 eqeq : "eqeqeq", | |
367 vars : "onevar", | |
368 windows : "wsh" | |
369 }, | |
370 | |
371 | |
372 // browser contains a set of global names which are commonly provided by a | |
373 // web browser environment. | |
374 browser = { | |
375 ArrayBuffer : false, | |
376 ArrayBufferView : false, | |
377 Audio : false, | |
378 Blob : false, | |
379 addEventListener : false, | |
380 applicationCache : false, | |
381 atob : false, | |
382 blur : false, | |
383 btoa : false, | |
384 clearInterval : false, | |
385 clearTimeout : false, | |
386 close : false, | |
387 closed : false, | |
388 DataView : false, | |
389 DOMParser : false, | |
390 defaultStatus : false, | |
391 document : false, | |
392 event : false, | |
393 FileReader : false, | |
394 Float32Array : false, | |
395 Float64Array : false, | |
396 FormData : false, | |
397 focus : false, | |
398 frames : false, | |
399 getComputedStyle : false, | |
400 HTMLElement : 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, | |
454 history : false, | |
455 Int16Array : false, | |
456 Int32Array : false, | |
457 Int8Array : false, | |
458 Image : false, | |
459 length : false, | |
460 localStorage : false, | |
461 location : false, | |
462 MessageChannel : false, | |
463 MessageEvent : false, | |
464 MessagePort : false, | |
465 moveBy : false, | |
466 moveTo : false, | |
467 MutationObserver : false, | |
468 name : false, | |
469 Node : false, | |
470 NodeFilter : false, | |
471 navigator : false, | |
472 onbeforeunload : true, | |
473 onblur : true, | |
474 onerror : true, | |
475 onfocus : true, | |
476 onload : true, | |
477 onresize : true, | |
478 onunload : true, | |
479 open : false, | |
480 openDatabase : false, | |
481 opener : false, | |
482 Option : false, | |
483 parent : false, | |
484 print : false, | |
485 removeEventListener : false, | |
486 resizeBy : false, | |
487 resizeTo : false, | |
488 screen : false, | |
489 scroll : false, | |
490 scrollBy : false, | |
491 scrollTo : false, | |
492 sessionStorage : false, | |
493 setInterval : false, | |
494 setTimeout : false, | |
495 SharedWorker : false, | |
496 status : false, | |
497 top : false, | |
498 Uint16Array : false, | |
499 Uint32Array : false, | |
500 Uint8Array : false, | |
501 WebSocket : false, | |
502 window : false, | |
503 Worker : false, | |
504 XMLHttpRequest : false, | |
505 XMLSerializer : false, | |
506 XPathEvaluator : false, | |
507 XPathException : false, | |
508 XPathExpression : false, | |
509 XPathNamespace : false, | |
510 XPathNSResolver : false, | |
511 XPathResult : false | |
512 }, | |
513 | |
514 couch = { | |
515 "require" : false, | |
516 respond : false, | |
517 getRow : false, | |
518 emit : false, | |
519 send : false, | |
520 start : false, | |
521 sum : false, | |
522 log : false, | |
523 exports : false, | |
524 module : false, | |
525 provides : false | |
526 }, | |
527 | |
528 declared, // Globals that were declared using /*global ... */ syntax. | |
529 | |
530 devel = { | |
531 alert : false, | |
532 confirm : false, | |
533 console : false, | |
534 Debug : false, | |
535 opera : false, | |
536 prompt : false | |
537 }, | |
538 | |
539 dojo = { | |
540 dojo : false, | |
541 dijit : false, | |
542 dojox : false, | |
543 define : false, | |
544 "require" : false | |
545 }, | |
546 | |
547 funct, // The current function | |
548 | |
549 functionicity = [ | |
550 "closure", "exception", "global", "label", | |
551 "outer", "unused", "var" | |
552 ], | |
553 | |
554 functions, // All of the functions | |
555 | |
556 global, // The global scope | |
557 implied, // Implied globals | |
558 inblock, | |
559 indent, | |
560 jsonmode, | |
561 | |
562 jquery = { | |
563 "$" : false, | |
564 jQuery : false | |
565 }, | |
566 | |
567 lines, | |
568 lookahead, | |
569 member, | |
570 membersOnly, | |
571 | |
572 mootools = { | |
573 "$" : false, | |
574 "$$" : false, | |
575 Asset : false, | |
576 Browser : false, | |
577 Chain : false, | |
578 Class : false, | |
579 Color : false, | |
580 Cookie : false, | |
581 Core : false, | |
582 Document : false, | |
583 DomReady : false, | |
584 DOMEvent : false, | |
585 DOMReady : false, | |
586 Drag : false, | |
587 Element : false, | |
588 Elements : false, | |
589 Event : false, | |
590 Events : false, | |
591 Fx : false, | |
592 Group : false, | |
593 Hash : false, | |
594 HtmlTable : false, | |
595 Iframe : false, | |
596 IframeShim : false, | |
597 InputValidator : false, | |
598 instanceOf : false, | |
599 Keyboard : false, | |
600 Locale : false, | |
601 Mask : false, | |
602 MooTools : false, | |
603 Native : false, | |
604 Options : false, | |
605 OverText : false, | |
606 Request : false, | |
607 Scroller : false, | |
608 Slick : false, | |
609 Slider : false, | |
610 Sortables : false, | |
611 Spinner : false, | |
612 Swiff : false, | |
613 Tips : false, | |
614 Type : false, | |
615 typeOf : false, | |
616 URI : false, | |
617 Window : false | |
618 }, | |
619 | |
620 nexttoken, | |
621 | |
622 node = { | |
623 __filename : false, | |
624 __dirname : false, | |
625 Buffer : false, | |
626 console : false, | |
627 exports : true, // In Node it is ok to exports = module.exports = foo(); | |
628 GLOBAL : false, | |
629 global : false, | |
630 module : false, | |
631 process : false, | |
632 require : false, | |
633 setTimeout : false, | |
634 clearTimeout : false, | |
635 setInterval : false, | |
636 clearInterval : false | |
637 }, | |
638 | |
639 noreach, | |
640 option, | |
641 predefined, // Global variables defined by option | |
642 prereg, | |
643 prevtoken, | |
644 | |
645 prototypejs = { | |
646 "$" : false, | |
647 "$$" : false, | |
648 "$A" : false, | |
649 "$F" : false, | |
650 "$H" : false, | |
651 "$R" : false, | |
652 "$break" : false, | |
653 "$continue" : false, | |
654 "$w" : false, | |
655 Abstract : false, | |
656 Ajax : false, | |
657 Class : false, | |
658 Enumerable : false, | |
659 Element : false, | |
660 Event : false, | |
661 Field : false, | |
662 Form : false, | |
663 Hash : false, | |
664 Insertion : false, | |
665 ObjectRange : false, | |
666 PeriodicalExecuter: false, | |
667 Position : false, | |
668 Prototype : false, | |
669 Selector : false, | |
670 Template : false, | |
671 Toggle : false, | |
672 Try : false, | |
673 Autocompleter : false, | |
674 Builder : false, | |
675 Control : false, | |
676 Draggable : false, | |
677 Draggables : false, | |
678 Droppables : false, | |
679 Effect : false, | |
680 Sortable : false, | |
681 SortableObserver : false, | |
682 Sound : false, | |
683 Scriptaculous : false | |
684 }, | |
685 | |
686 quotmark, | |
687 | |
688 rhino = { | |
689 defineClass : false, | |
690 deserialize : false, | |
691 gc : false, | |
692 help : false, | |
693 importPackage: false, | |
694 "java" : false, | |
695 load : false, | |
696 loadClass : false, | |
697 print : false, | |
698 quit : false, | |
699 readFile : false, | |
700 readUrl : false, | |
701 runCommand : false, | |
702 seal : false, | |
703 serialize : false, | |
704 spawn : false, | |
705 sync : false, | |
706 toint32 : false, | |
707 version : false | |
708 }, | |
709 | |
710 scope, // The current scope | |
711 stack, | |
712 | |
713 // standard contains the global names that are provided by the | |
714 // ECMAScript standard. | |
715 standard = { | |
716 Array : false, | |
717 Boolean : false, | |
718 Date : false, | |
719 decodeURI : false, | |
720 decodeURIComponent : false, | |
721 encodeURI : false, | |
722 encodeURIComponent : false, | |
723 Error : false, | |
724 "eval" : false, | |
725 EvalError : false, | |
726 Function : false, | |
727 hasOwnProperty : false, | |
728 isFinite : false, | |
729 isNaN : false, | |
730 JSON : false, | |
731 Map : false, | |
732 Math : false, | |
733 NaN : false, | |
734 Number : false, | |
735 Object : false, | |
736 parseInt : false, | |
737 parseFloat : false, | |
738 RangeError : false, | |
739 ReferenceError : false, | |
740 RegExp : false, | |
741 Set : false, | |
742 String : false, | |
743 SyntaxError : false, | |
744 TypeError : false, | |
745 URIError : false, | |
746 WeakMap : false | |
747 }, | |
748 | |
749 // widely adopted global names that are not part of ECMAScript standard | |
750 nonstandard = { | |
751 escape : false, | |
752 unescape : false | |
753 }, | |
754 | |
755 directive, | |
756 syntax = {}, | |
757 tab, | |
758 token, | |
759 unuseds, | |
760 urls, | |
761 useESNextSyntax, | |
762 warnings, | |
763 | |
764 worker = { | |
765 importScripts : true, | |
766 postMessage : true, | |
767 self : true | |
768 }, | |
769 | |
770 wsh = { | |
771 ActiveXObject : true, | |
772 Enumerator : true, | |
773 GetObject : true, | |
774 ScriptEngine : true, | |
775 ScriptEngineBuildVersion : true, | |
776 ScriptEngineMajorVersion : true, | |
777 ScriptEngineMinorVersion : true, | |
778 VBArray : true, | |
779 WSH : true, | |
780 WScript : true, | |
781 XDomainRequest : true | |
782 }, | |
783 | |
784 yui = { | |
785 YUI : false, | |
786 Y : false, | |
787 YUI_config : false | |
788 }; | |
789 // Regular expressions. Some of these are stupidly long. | |
790 var ax, cx, tx, nx, nxg, lx, ix, jx, ft; | |
791 (function () { | |
792 /*jshint maxlen:300 */ | |
793 | |
794 // unsafe comment or string | |
795 ax = /@cc|<\/?|script|\]\s*\]|<\s*!|</i; | |
796 | |
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]/; | |
799 | |
800 // token | |
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]+)?)/; | |
802 | |
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; | |
806 | |
807 // star slash | |
808 lx = /\*\//; | |
809 | |
810 // identifier | |
811 ix = /^([a-zA-Z_$][a-zA-Z0-9_$]*)$/; | |
812 | |
813 // javascript url | |
814 jx = /^(?:javascript|jscript|ecmascript|vbscript|mocha|livescript)\s*:/i; | |
815 | |
816 // catches /* falls through */ comments | |
817 ft = /^\s*\/\*\s*falls\sthrough\s*\*\/\s*$/; | |
818 }()); | |
819 | |
820 function F() {} // Used by Object.create | |
821 | |
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); | |
826 } | |
827 | |
828 function checkOption(name, t) { | |
829 if (valOptions[name] === undefined && boolOptions[name] === undefined) { | |
830 warning("Bad option: '" + name + "'.", t); | |
831 } | |
832 } | |
833 | |
834 function isString(obj) { | |
835 return Object.prototype.toString.call(obj) === "[object String]"; | |
836 } | |
837 | |
838 // Provide critical ES5 functions to ES3. | |
839 | |
840 if (typeof Array.isArray !== "function") { | |
841 Array.isArray = function (o) { | |
842 return Object.prototype.toString.apply(o) === "[object Array]"; | |
843 }; | |
844 } | |
845 | |
846 if (!Array.prototype.forEach) { | |
847 Array.prototype.forEach = function (fn, scope) { | |
848 var len = this.length; | |
849 | |
850 for (var i = 0; i < len; i++) { | |
851 fn.call(scope || this, this[i], i, this); | |
852 } | |
853 }; | |
854 } | |
855 | |
856 if (!Array.prototype.indexOf) { | |
857 Array.prototype.indexOf = function (searchElement /*, fromIndex */ ) { | |
858 if (this === null || this === undefined) { | |
859 throw new TypeError(); | |
860 } | |
861 | |
862 var t = new Object(this); | |
863 var len = t.length >>> 0; | |
864 | |
865 if (len === 0) { | |
866 return -1; | |
867 } | |
868 | |
869 var n = 0; | |
870 if (arguments.length > 0) { | |
871 n = Number(arguments[1]); | |
872 if (n != n) { // shortcut for verifying if it's NaN | |
873 n = 0; | |
874 } else if (n !== 0 && n != Infinity && n != -Infinity) { | |
875 n = (n > 0 || -1) * Math.floor(Math.abs(n)); | |
876 } | |
877 } | |
878 | |
879 if (n >= len) { | |
880 return -1; | |
881 } | |
882 | |
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) { | |
886 return k; | |
887 } | |
888 } | |
889 | |
890 return -1; | |
891 }; | |
892 } | |
893 | |
894 if (typeof Object.create !== "function") { | |
895 Object.create = function (o) { | |
896 F.prototype = o; | |
897 return new F(); | |
898 }; | |
899 } | |
900 | |
901 if (typeof Object.keys !== "function") { | |
902 Object.keys = function (o) { | |
903 var a = [], k; | |
904 for (k in o) { | |
905 if (is_own(o, k)) { | |
906 a.push(k); | |
907 } | |
908 } | |
909 return a; | |
910 }; | |
911 } | |
912 | |
913 // Non standard methods | |
914 | |
915 function isAlpha(str) { | |
916 return (str >= "a" && str <= "z\uffff") || | |
917 (str >= "A" && str <= "Z\uffff"); | |
918 } | |
919 | |
920 function isDigit(str) { | |
921 return (str >= "0" && str <= "9"); | |
922 } | |
923 | |
924 function isIdentifier(token, value) { | |
925 if (!token) | |
926 return false; | |
927 | |
928 if (!token.identifier || token.value !== value) | |
929 return false; | |
930 | |
931 return true; | |
932 } | |
933 | |
934 function supplant(str, data) { | |
935 return str.replace(/\{([^{}]*)\}/g, function (a, b) { | |
936 var r = data[b]; | |
937 return typeof r === "string" || typeof r === "number" ? r : a; | |
938 }); | |
939 } | |
940 | |
941 function combine(t, o) { | |
942 var n; | |
943 for (n in o) { | |
944 if (is_own(o, n) && !is_own(JSHINT.blacklist, n)) { | |
945 t[n] = o[n]; | |
946 } | |
947 } | |
948 } | |
949 | |
950 function updatePredefined() { | |
951 Object.keys(JSHINT.blacklist).forEach(function (key) { | |
952 delete predefined[key]; | |
953 }); | |
954 } | |
955 | |
956 function assume() { | |
957 if (option.couch) { | |
958 combine(predefined, couch); | |
959 } | |
960 | |
961 if (option.rhino) { | |
962 combine(predefined, rhino); | |
963 } | |
964 | |
965 if (option.prototypejs) { | |
966 combine(predefined, prototypejs); | |
967 } | |
968 | |
969 if (option.node) { | |
970 combine(predefined, node); | |
971 option.globalstrict = true; | |
972 } | |
973 | |
974 if (option.devel) { | |
975 combine(predefined, devel); | |
976 } | |
977 | |
978 if (option.dojo) { | |
979 combine(predefined, dojo); | |
980 } | |
981 | |
982 if (option.browser) { | |
983 combine(predefined, browser); | |
984 } | |
985 | |
986 if (option.nonstandard) { | |
987 combine(predefined, nonstandard); | |
988 } | |
989 | |
990 if (option.jquery) { | |
991 combine(predefined, jquery); | |
992 } | |
993 | |
994 if (option.mootools) { | |
995 combine(predefined, mootools); | |
996 } | |
997 | |
998 if (option.worker) { | |
999 combine(predefined, worker); | |
1000 } | |
1001 | |
1002 if (option.wsh) { | |
1003 combine(predefined, wsh); | |
1004 } | |
1005 | |
1006 if (option.esnext) { | |
1007 useESNextSyntax(); | |
1008 } | |
1009 | |
1010 if (option.globalstrict && option.strict !== false) { | |
1011 option.strict = true; | |
1012 } | |
1013 | |
1014 if (option.yui) { | |
1015 combine(predefined, yui); | |
1016 } | |
1017 } | |
1018 | |
1019 | |
1020 // Produce an error warning. | |
1021 function quit(message, line, chr) { | |
1022 var percentage = Math.floor((line / lines.length) * 100); | |
1023 | |
1024 throw { | |
1025 name: "JSHintError", | |
1026 line: line, | |
1027 character: chr, | |
1028 message: message + " (" + percentage + "% scanned).", | |
1029 raw: message | |
1030 }; | |
1031 } | |
1032 | |
1033 function isundef(scope, m, t, a) { | |
1034 return JSHINT.undefs.push([scope, m, t, a]); | |
1035 } | |
1036 | |
1037 function warning(m, t, a, b, c, d) { | |
1038 var ch, l, w; | |
1039 t = t || nexttoken; | |
1040 if (t.id === "(end)") { // `~ | |
1041 t = token; | |
1042 } | |
1043 l = t.line || 0; | |
1044 ch = t.from || 0; | |
1045 w = { | |
1046 id: "(error)", | |
1047 raw: m, | |
1048 evidence: lines[l - 1] || "", | |
1049 line: l, | |
1050 character: ch, | |
1051 scope: JSHINT.scope, | |
1052 a: a, | |
1053 b: b, | |
1054 c: c, | |
1055 d: d | |
1056 }; | |
1057 w.reason = supplant(m, w); | |
1058 JSHINT.errors.push(w); | |
1059 if (option.passfail) { | |
1060 quit("Stopping. ", l, ch); | |
1061 } | |
1062 warnings += 1; | |
1063 if (warnings >= option.maxerr) { | |
1064 quit("Too many errors.", l, ch); | |
1065 } | |
1066 return w; | |
1067 } | |
1068 | |
1069 function warningAt(m, l, ch, a, b, c, d) { | |
1070 return warning(m, { | |
1071 line: l, | |
1072 from: ch | |
1073 }, a, b, c, d); | |
1074 } | |
1075 | |
1076 function error(m, t, a, b, c, d) { | |
1077 warning(m, t, a, b, c, d); | |
1078 } | |
1079 | |
1080 function errorAt(m, l, ch, a, b, c, d) { | |
1081 return error(m, { | |
1082 line: l, | |
1083 from: ch | |
1084 }, a, b, c, d); | |
1085 } | |
1086 | |
1087 // Tracking of "internal" scripts, like eval containing a static string | |
1088 function addInternalSrc(elem, src) { | |
1089 var i; | |
1090 i = { | |
1091 id: "(internal)", | |
1092 elem: elem, | |
1093 value: src | |
1094 }; | |
1095 JSHINT.internals.push(i); | |
1096 return i; | |
1097 } | |
1098 | |
1099 | |
1100 // lexical analysis and token construction | |
1101 | |
1102 var lex = (function lex() { | |
1103 var character, from, line, s; | |
1104 | |
1105 // Private lex methods | |
1106 | |
1107 function nextLine() { | |
1108 var at, | |
1109 match, | |
1110 tw; // trailing whitespace check | |
1111 | |
1112 if (line >= lines.length) | |
1113 return false; | |
1114 | |
1115 character = 1; | |
1116 s = lines[line]; | |
1117 line += 1; | |
1118 | |
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; | |
1126 } else { | |
1127 at = s.search(/ \t|\t [^\*]/); | |
1128 } | |
1129 | |
1130 if (at >= 0) | |
1131 warningAt("Mixed spaces and tabs.", line, at + 1); | |
1132 | |
1133 s = s.replace(/\t/g, tab); | |
1134 at = s.search(cx); | |
1135 | |
1136 if (at >= 0) | |
1137 warningAt("Unsafe character.", line, at); | |
1138 | |
1139 if (option.maxlen && option.maxlen < s.length) | |
1140 warningAt("Line too long.", line, s.length); | |
1141 | |
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); | |
1146 } | |
1147 return true; | |
1148 } | |
1149 | |
1150 // Produce a token object. The token inherits from a syntax symbol. | |
1151 | |
1152 function it(type, value) { | |
1153 var i, t; | |
1154 | |
1155 function checkName(name) { | |
1156 if (!option.proto && name === "__proto__") { | |
1157 warningAt("The '{a}' property is deprecated.", line, from, name); | |
1158 return; | |
1159 } | |
1160 | |
1161 if (!option.iterator && name === "__iterator__") { | |
1162 warningAt("'{a}' is only available in JavaScript 1.7.", line, from, name); | |
1163 return; | |
1164 } | |
1165 | |
1166 // Check for dangling underscores unless we're in Node | |
1167 // environment and this identifier represents built-in | |
1168 // Node globals with underscores. | |
1169 | |
1170 var hasDangling = /^(_+.*|.*_+)$/.test(name); | |
1171 | |
1172 if (option.nomen && hasDangling && name !== "_") { | |
1173 if (option.node && token.id !== "." && /^(__dirname|__filename)$/.test(name)) | |
1174 return; | |
1175 | |
1176 warningAt("Unexpected {a} in '{b}'.", line, from, "dangling '_'", name); | |
1177 return; | |
1178 } | |
1179 | |
1180 // Check for non-camelcase names. Names like MY_VAR and | |
1181 // _myVar are okay though. | |
1182 | |
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); | |
1186 } | |
1187 } | |
1188 } | |
1189 | |
1190 if (type === "(color)" || type === "(range)") { | |
1191 t = {type: type}; | |
1192 } else if (type === "(punctuator)" || | |
1193 (type === "(identifier)" && is_own(syntax, value))) { | |
1194 t = syntax[value] || syntax["(error)"]; | |
1195 } else { | |
1196 t = syntax[type]; | |
1197 } | |
1198 | |
1199 t = Object.create(t); | |
1200 | |
1201 if (type === "(string)" || type === "(range)") { | |
1202 if (!option.scripturl && jx.test(value)) { | |
1203 warningAt("Script URL.", line, from); | |
1204 } | |
1205 } | |
1206 | |
1207 if (type === "(identifier)") { | |
1208 t.identifier = true; | |
1209 checkName(value); | |
1210 } | |
1211 | |
1212 t.value = value; | |
1213 t.line = line; | |
1214 t.character = character; | |
1215 t.from = from; | |
1216 i = t.id; | |
1217 if (i !== "(endline)") { | |
1218 prereg = i && | |
1219 (("(,=:[!&|?{};".indexOf(i.charAt(i.length - 1)) >= 0) || | |
1220 i === "return" || | |
1221 i === "case"); | |
1222 } | |
1223 return t; | |
1224 } | |
1225 | |
1226 // Public lex methods | |
1227 return { | |
1228 init: function (source) { | |
1229 if (typeof source === "string") { | |
1230 lines = source | |
1231 .replace(/\r\n/g, "\n") | |
1232 .replace(/\r/g, "\n") | |
1233 .split("\n"); | |
1234 } else { | |
1235 lines = source; | |
1236 } | |
1237 | |
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) === "#!") | |
1241 lines[0] = ""; | |
1242 | |
1243 line = 0; | |
1244 nextLine(); | |
1245 from = 1; | |
1246 }, | |
1247 | |
1248 range: function (begin, end) { | |
1249 var c, value = ""; | |
1250 from = character; | |
1251 if (s.charAt(0) !== begin) { | |
1252 errorAt("Expected '{a}' and instead saw '{b}'.", | |
1253 line, character, begin, s.charAt(0)); | |
1254 } | |
1255 for (;;) { | |
1256 s = s.slice(1); | |
1257 character += 1; | |
1258 c = s.charAt(0); | |
1259 switch (c) { | |
1260 case "": | |
1261 errorAt("Missing '{a}'.", line, character, c); | |
1262 break; | |
1263 case end: | |
1264 s = s.slice(1); | |
1265 character += 1; | |
1266 return it("(range)", value); | |
1267 case "\\": | |
1268 warningAt("Unexpected '{a}'.", line, character, c); | |
1269 } | |
1270 value += c; | |
1271 } | |
1272 | |
1273 }, | |
1274 | |
1275 | |
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; | |
1279 | |
1280 function match(x) { | |
1281 var r = x.exec(s), r1; | |
1282 | |
1283 if (r) { | |
1284 l = r[0].length; | |
1285 r1 = r[1]; | |
1286 c = r1.charAt(0); | |
1287 s = s.substr(l); | |
1288 from = character + l - r1.length; | |
1289 character += l; | |
1290 return r1; | |
1291 } | |
1292 } | |
1293 | |
1294 function string(x) { | |
1295 var c, j, r = "", allowNewLine = false; | |
1296 | |
1297 if (jsonmode && x !== "\"") { | |
1298 warningAt("Strings must use doublequote.", | |
1299 line, character); | |
1300 } | |
1301 | |
1302 if (option.quotmark) { | |
1303 if (option.quotmark === "single" && x !== "'") { | |
1304 warningAt("Strings must use singlequote.", | |
1305 line, character); | |
1306 } else if (option.quotmark === "double" && x !== "\"") { | |
1307 warningAt("Strings must use doublequote.", | |
1308 line, character); | |
1309 } else if (option.quotmark === true) { | |
1310 quotmark = quotmark || x; | |
1311 if (quotmark !== x) { | |
1312 warningAt("Mixed double and single quotes.", | |
1313 line, character); | |
1314 } | |
1315 } | |
1316 } | |
1317 | |
1318 function esc(n) { | |
1319 var i = parseInt(s.substr(j + 1, n), 16); | |
1320 j += n; | |
1321 if (i >= 32 && i <= 126 && | |
1322 i !== 34 && i !== 92 && i !== 39) { | |
1323 warningAt("Unnecessary escapement.", line, character); | |
1324 } | |
1325 character += n; | |
1326 c = String.fromCharCode(i); | |
1327 } | |
1328 | |
1329 j = 0; | |
1330 | |
1331 unclosedString: | |
1332 for (;;) { | |
1333 while (j >= s.length) { | |
1334 j = 0; | |
1335 | |
1336 var cl = line, cf = from; | |
1337 if (!nextLine()) { | |
1338 errorAt("Unclosed string.", cl, cf); | |
1339 break unclosedString; | |
1340 } | |
1341 | |
1342 if (allowNewLine) { | |
1343 allowNewLine = false; | |
1344 } else { | |
1345 warningAt("Unclosed string.", cl, cf); | |
1346 } | |
1347 } | |
1348 | |
1349 c = s.charAt(j); | |
1350 if (c === x) { | |
1351 character += 1; | |
1352 s = s.substr(j + 1); | |
1353 return it("(string)", r, x); | |
1354 } | |
1355 | |
1356 if (c < " ") { | |
1357 if (c === "\n" || c === "\r") { | |
1358 break; | |
1359 } | |
1360 warningAt("Control character in string: {a}.", | |
1361 line, character + j, s.slice(0, j)); | |
1362 } else if (c === "\\") { | |
1363 j += 1; | |
1364 character += 1; | |
1365 c = s.charAt(j); | |
1366 n = s.charAt(j + 1); | |
1367 switch (c) { | |
1368 case "\\": | |
1369 case "\"": | |
1370 case "/": | |
1371 break; | |
1372 case "\'": | |
1373 if (jsonmode) { | |
1374 warningAt("Avoid \\'.", line, character); | |
1375 } | |
1376 break; | |
1377 case "b": | |
1378 c = "\b"; | |
1379 break; | |
1380 case "f": | |
1381 c = "\f"; | |
1382 break; | |
1383 case "n": | |
1384 c = "\n"; | |
1385 break; | |
1386 case "r": | |
1387 c = "\r"; | |
1388 break; | |
1389 case "t": | |
1390 c = "\t"; | |
1391 break; | |
1392 case "0": | |
1393 c = "\0"; | |
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"]) { | |
1398 warningAt( | |
1399 "Octal literals are not allowed in strict mode.", | |
1400 line, character); | |
1401 } | |
1402 break; | |
1403 case "u": | |
1404 esc(4); | |
1405 break; | |
1406 case "v": | |
1407 if (jsonmode) { | |
1408 warningAt("Avoid \\v.", line, character); | |
1409 } | |
1410 c = "\v"; | |
1411 break; | |
1412 case "x": | |
1413 if (jsonmode) { | |
1414 warningAt("Avoid \\x-.", line, character); | |
1415 } | |
1416 esc(2); | |
1417 break; | |
1418 case "": | |
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) { | |
1424 if (jsonmode) { | |
1425 warningAt("Avoid EOL escapement.", line, character); | |
1426 } | |
1427 c = ""; | |
1428 character -= 1; | |
1429 break; | |
1430 } | |
1431 warningAt("Bad escapement of EOL. Use option multistr if needed.", | |
1432 line, character); | |
1433 break; | |
1434 case "!": | |
1435 if (s.charAt(j - 2) === "<") | |
1436 break; | |
1437 /*falls through*/ | |
1438 default: | |
1439 warningAt("Bad escapement.", line, character); | |
1440 } | |
1441 } | |
1442 r += c; | |
1443 character += 1; | |
1444 j += 1; | |
1445 } | |
1446 } | |
1447 | |
1448 for (;;) { | |
1449 if (!s) { | |
1450 return it(nextLine() ? "(endline)" : "(end)", ""); | |
1451 } | |
1452 | |
1453 t = match(tx); | |
1454 | |
1455 if (!t) { | |
1456 t = ""; | |
1457 c = ""; | |
1458 while (s && s < "!") { | |
1459 s = s.substr(1); | |
1460 } | |
1461 if (s) { | |
1462 errorAt("Unexpected '{a}'.", line, character, s.substr(0, 1)); | |
1463 s = ""; | |
1464 } | |
1465 } else { | |
1466 | |
1467 // identifier | |
1468 | |
1469 if (isAlpha(c) || c === "_" || c === "$") { | |
1470 return it("(identifier)", t); | |
1471 } | |
1472 | |
1473 // number | |
1474 | |
1475 if (isDigit(c)) { | |
1476 if (!isFinite(Number(t))) { | |
1477 warningAt("Bad number '{a}'.", | |
1478 line, character, t); | |
1479 } | |
1480 if (isAlpha(s.substr(0, 1))) { | |
1481 warningAt("Missing space after '{a}'.", | |
1482 line, character, t); | |
1483 } | |
1484 if (c === "0") { | |
1485 d = t.substr(1, 1); | |
1486 if (isDigit(d)) { | |
1487 if (token.id !== ".") { | |
1488 warningAt("Don't use extra leading zeros '{a}'.", | |
1489 line, character, t); | |
1490 } | |
1491 } else if (jsonmode && (d === "x" || d === "X")) { | |
1492 warningAt("Avoid 0x-. '{a}'.", | |
1493 line, character, t); | |
1494 } | |
1495 } | |
1496 if (t.substr(t.length - 1) === ".") { | |
1497 warningAt( | |
1498 "A trailing decimal point can be confused with a dot '{a}'.", line, character, t); | |
1499 } | |
1500 return it("(number)", t); | |
1501 } | |
1502 switch (t) { | |
1503 | |
1504 // string | |
1505 | |
1506 case "\"": | |
1507 case "'": | |
1508 return string(t); | |
1509 | |
1510 // // comment | |
1511 | |
1512 case "//": | |
1513 s = ""; | |
1514 token.comment = true; | |
1515 break; | |
1516 | |
1517 // /* comment | |
1518 | |
1519 case "/*": | |
1520 for (;;) { | |
1521 i = s.search(lx); | |
1522 if (i >= 0) { | |
1523 break; | |
1524 } | |
1525 if (!nextLine()) { | |
1526 errorAt("Unclosed comment.", line, character); | |
1527 } | |
1528 } | |
1529 s = s.substr(i + 2); | |
1530 token.comment = true; | |
1531 break; | |
1532 | |
1533 // /*members /*jshint /*global | |
1534 | |
1535 case "/*members": | |
1536 case "/*member": | |
1537 case "/*jshint": | |
1538 case "/*jslint": | |
1539 case "/*global": | |
1540 case "*/": | |
1541 return { | |
1542 value: t, | |
1543 type: "special", | |
1544 line: line, | |
1545 character: character, | |
1546 from: from | |
1547 }; | |
1548 | |
1549 case "": | |
1550 break; | |
1551 // / | |
1552 case "/": | |
1553 if (s.charAt(0) === "=") { | |
1554 errorAt("A regular expression literal can be confused with '/='.", | |
1555 line, from); | |
1556 } | |
1557 | |
1558 if (prereg) { | |
1559 depth = 0; | |
1560 captures = 0; | |
1561 l = 0; | |
1562 for (;;) { | |
1563 b = true; | |
1564 c = s.charAt(l); | |
1565 l += 1; | |
1566 switch (c) { | |
1567 case "": | |
1568 errorAt("Unclosed regular expression.", line, from); | |
1569 return quit("Stopping.", line, from); | |
1570 case "/": | |
1571 if (depth > 0) { | |
1572 warningAt("{a} unterminated regular expression " + | |
1573 "group(s).", line, from + l, depth); | |
1574 } | |
1575 c = s.substr(0, l - 1); | |
1576 q = { | |
1577 g: true, | |
1578 i: true, | |
1579 m: true | |
1580 }; | |
1581 while (q[s.charAt(l)] === true) { | |
1582 q[s.charAt(l)] = false; | |
1583 l += 1; | |
1584 } | |
1585 character += l; | |
1586 s = s.substr(l); | |
1587 q = s.charAt(0); | |
1588 if (q === "/" || q === "*") { | |
1589 errorAt("Confusing regular expression.", | |
1590 line, from); | |
1591 } | |
1592 return it("(regexp)", c); | |
1593 case "\\": | |
1594 c = s.charAt(l); | |
1595 if (c < " ") { | |
1596 warningAt( | |
1597 "Unexpected control character in regular expression.", line, from + l); | |
1598 } else if (c === "<") { | |
1599 warningAt( | |
1600 "Unexpected escaped character '{a}' in regular expression.", line, from + l, c); | |
1601 } | |
1602 l += 1; | |
1603 break; | |
1604 case "(": | |
1605 depth += 1; | |
1606 b = false; | |
1607 if (s.charAt(l) === "?") { | |
1608 l += 1; | |
1609 switch (s.charAt(l)) { | |
1610 case ":": | |
1611 case "=": | |
1612 case "!": | |
1613 l += 1; | |
1614 break; | |
1615 default: | |
1616 warningAt( | |
1617 "Expected '{a}' and instead saw '{b}'.", line, from + l, ":", s.charAt(l)); | |
1618 } | |
1619 } else { | |
1620 captures += 1; | |
1621 } | |
1622 break; | |
1623 case "|": | |
1624 b = false; | |
1625 break; | |
1626 case ")": | |
1627 if (depth === 0) { | |
1628 warningAt("Unescaped '{a}'.", | |
1629 line, from + l, ")"); | |
1630 } else { | |
1631 depth -= 1; | |
1632 } | |
1633 break; | |
1634 case " ": | |
1635 q = 1; | |
1636 while (s.charAt(l) === " ") { | |
1637 l += 1; | |
1638 q += 1; | |
1639 } | |
1640 if (q > 1) { | |
1641 warningAt( | |
1642 "Spaces are hard to count. Use {{a}}.", line, from + l, q); | |
1643 } | |
1644 break; | |
1645 case "[": | |
1646 c = s.charAt(l); | |
1647 if (c === "^") { | |
1648 l += 1; | |
1649 if (s.charAt(l) === "]") { | |
1650 errorAt("Unescaped '{a}'.", | |
1651 line, from + l, "^"); | |
1652 } | |
1653 } | |
1654 if (c === "]") { | |
1655 warningAt("Empty class.", line, | |
1656 from + l - 1); | |
1657 } | |
1658 isLiteral = false; | |
1659 isInRange = false; | |
1660 klass: | |
1661 do { | |
1662 c = s.charAt(l); | |
1663 l += 1; | |
1664 switch (c) { | |
1665 case "[": | |
1666 case "^": | |
1667 warningAt("Unescaped '{a}'.", | |
1668 line, from + l, c); | |
1669 if (isInRange) { | |
1670 isInRange = false; | |
1671 } else { | |
1672 isLiteral = true; | |
1673 } | |
1674 break; | |
1675 case "-": | |
1676 if (isLiteral && !isInRange) { | |
1677 isLiteral = false; | |
1678 isInRange = true; | |
1679 } else if (isInRange) { | |
1680 isInRange = false; | |
1681 } else if (s.charAt(l) === "]") { | |
1682 isInRange = true; | |
1683 } else { | |
1684 if (option.regexdash !== (l === 2 || (l === 3 && | |
1685 s.charAt(1) === "^"))) { | |
1686 warningAt("Unescaped '{a}'.", | |
1687 line, from + l - 1, "-"); | |
1688 } | |
1689 isLiteral = true; | |
1690 } | |
1691 break; | |
1692 case "]": | |
1693 if (isInRange && !option.regexdash) { | |
1694 warningAt("Unescaped '{a}'.", | |
1695 line, from + l - 1, "-"); | |
1696 } | |
1697 break klass; | |
1698 case "\\": | |
1699 c = s.charAt(l); | |
1700 if (c < " ") { | |
1701 warningAt( | |
1702 "Unexpected control character in regular expression.", line, from + l); | |
1703 } else if (c === "<") { | |
1704 warningAt( | |
1705 "Unexpected escaped character '{a}' in regular expression.", line, from + l, c); | |
1706 } | |
1707 l += 1; | |
1708 | |
1709 // \w, \s and \d are never part of a character range | |
1710 if (/[wsd]/i.test(c)) { | |
1711 if (isInRange) { | |
1712 warningAt("Unescaped '{a}'.", | |
1713 line, from + l, "-"); | |
1714 isInRange = false; | |
1715 } | |
1716 isLiteral = false; | |
1717 } else if (isInRange) { | |
1718 isInRange = false; | |
1719 } else { | |
1720 isLiteral = true; | |
1721 } | |
1722 break; | |
1723 case "/": | |
1724 warningAt("Unescaped '{a}'.", | |
1725 line, from + l - 1, "/"); | |
1726 | |
1727 if (isInRange) { | |
1728 isInRange = false; | |
1729 } else { | |
1730 isLiteral = true; | |
1731 } | |
1732 break; | |
1733 case "<": | |
1734 if (isInRange) { | |
1735 isInRange = false; | |
1736 } else { | |
1737 isLiteral = true; | |
1738 } | |
1739 break; | |
1740 default: | |
1741 if (isInRange) { | |
1742 isInRange = false; | |
1743 } else { | |
1744 isLiteral = true; | |
1745 } | |
1746 } | |
1747 } while (c); | |
1748 break; | |
1749 case ".": | |
1750 if (option.regexp) { | |
1751 warningAt("Insecure '{a}'.", line, | |
1752 from + l, c); | |
1753 } | |
1754 break; | |
1755 case "]": | |
1756 case "?": | |
1757 case "{": | |
1758 case "}": | |
1759 case "+": | |
1760 case "*": | |
1761 warningAt("Unescaped '{a}'.", line, | |
1762 from + l, c); | |
1763 } | |
1764 if (b) { | |
1765 switch (s.charAt(l)) { | |
1766 case "?": | |
1767 case "+": | |
1768 case "*": | |
1769 l += 1; | |
1770 if (s.charAt(l) === "?") { | |
1771 l += 1; | |
1772 } | |
1773 break; | |
1774 case "{": | |
1775 l += 1; | |
1776 c = s.charAt(l); | |
1777 if (c < "0" || c > "9") { | |
1778 warningAt( | |
1779 "Expected a number and instead saw '{a}'.", line, from + l, c); | |
1780 break; // No reason to continue checking numbers. | |
1781 } | |
1782 l += 1; | |
1783 low = +c; | |
1784 for (;;) { | |
1785 c = s.charAt(l); | |
1786 if (c < "0" || c > "9") { | |
1787 break; | |
1788 } | |
1789 l += 1; | |
1790 low = +c + (low * 10); | |
1791 } | |
1792 high = low; | |
1793 if (c === ",") { | |
1794 l += 1; | |
1795 high = Infinity; | |
1796 c = s.charAt(l); | |
1797 if (c >= "0" && c <= "9") { | |
1798 l += 1; | |
1799 high = +c; | |
1800 for (;;) { | |
1801 c = s.charAt(l); | |
1802 if (c < "0" || c > "9") { | |
1803 break; | |
1804 } | |
1805 l += 1; | |
1806 high = +c + (high * 10); | |
1807 } | |
1808 } | |
1809 } | |
1810 if (s.charAt(l) !== "}") { | |
1811 warningAt( | |
1812 "Expected '{a}' and instead saw '{b}'.", line, from + l, "}", c); | |
1813 } else { | |
1814 l += 1; | |
1815 } | |
1816 if (s.charAt(l) === "?") { | |
1817 l += 1; | |
1818 } | |
1819 if (low > high) { | |
1820 warningAt( | |
1821 "'{a}' should not be greater than '{b}'.", line, from + l, low, high); | |
1822 } | |
1823 } | |
1824 } | |
1825 } | |
1826 c = s.substr(0, l - 1); | |
1827 character += l; | |
1828 s = s.substr(l); | |
1829 return it("(regexp)", c); | |
1830 } | |
1831 return it("(punctuator)", t); | |
1832 | |
1833 // punctuator | |
1834 | |
1835 case "#": | |
1836 return it("(punctuator)", t); | |
1837 default: | |
1838 return it("(punctuator)", t); | |
1839 } | |
1840 } | |
1841 } | |
1842 } | |
1843 }; | |
1844 }()); | |
1845 | |
1846 | |
1847 function addlabel(t, type, token) { | |
1848 if (t === "hasOwnProperty") { | |
1849 warning("'hasOwnProperty' is a really bad name."); | |
1850 } | |
1851 | |
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); | |
1857 } | |
1858 } | |
1859 } | |
1860 | |
1861 if (is_own(funct, t) && !funct["(global)"]) { | |
1862 if (funct[t] === true) { | |
1863 if (option.latedef) | |
1864 warning("'{a}' was used before it was defined.", nexttoken, t); | |
1865 } else { | |
1866 if (!option.shadow && type !== "exception") { | |
1867 warning("'{a}' is already defined.", nexttoken, t); | |
1868 } | |
1869 } | |
1870 } | |
1871 | |
1872 funct[t] = type; | |
1873 | |
1874 if (token) { | |
1875 funct["(tokens)"][t] = token; | |
1876 } | |
1877 | |
1878 if (funct["(global)"]) { | |
1879 global[t] = funct; | |
1880 if (is_own(implied, t)) { | |
1881 if (option.latedef) | |
1882 warning("'{a}' was used before it was defined.", nexttoken, t); | |
1883 delete implied[t]; | |
1884 } | |
1885 } else { | |
1886 scope[t] = funct; | |
1887 } | |
1888 } | |
1889 | |
1890 | |
1891 function doOption() { | |
1892 var nt = nexttoken; | |
1893 var o = nt.value; | |
1894 var quotmarkValue = option.quotmark; | |
1895 var predef = {}; | |
1896 var b, obj, filter, t, tn, v, minus; | |
1897 | |
1898 switch (o) { | |
1899 case "*/": | |
1900 error("Unbegun comment."); | |
1901 break; | |
1902 case "/*members": | |
1903 case "/*member": | |
1904 o = "/*members"; | |
1905 if (!membersOnly) { | |
1906 membersOnly = {}; | |
1907 } | |
1908 obj = membersOnly; | |
1909 option.quotmark = false; | |
1910 break; | |
1911 case "/*jshint": | |
1912 case "/*jslint": | |
1913 obj = option; | |
1914 filter = boolOptions; | |
1915 break; | |
1916 case "/*global": | |
1917 obj = predef; | |
1918 break; | |
1919 default: | |
1920 error("What?"); | |
1921 } | |
1922 | |
1923 t = lex.token(); | |
1924 | |
1925 loop: | |
1926 for (;;) { | |
1927 minus = false; | |
1928 for (;;) { | |
1929 if (t.type === "special" && t.value === "*/") { | |
1930 break loop; | |
1931 } | |
1932 if (t.id !== "(endline)" && t.id !== ",") { | |
1933 break; | |
1934 } | |
1935 t = lex.token(); | |
1936 } | |
1937 | |
1938 if (o === "/*global" && t.value === "-") { | |
1939 minus = true; | |
1940 t = lex.token(); | |
1941 } | |
1942 | |
1943 if (t.type !== "(string)" && t.type !== "(identifier)" && o !== "/*members") { | |
1944 error("Bad option.", t); | |
1945 } | |
1946 | |
1947 v = lex.token(); | |
1948 if (v.id === ":") { | |
1949 v = lex.token(); | |
1950 | |
1951 if (obj === membersOnly) { | |
1952 error("Expected '{a}' and instead saw '{b}'.", t, "*/", ":"); | |
1953 } | |
1954 | |
1955 if (o === "/*jshint") { | |
1956 checkOption(t.value, t); | |
1957 } | |
1958 | |
1959 var numericVals = [ | |
1960 "maxstatements", | |
1961 "maxparams", | |
1962 "maxdepth", | |
1963 "maxcomplexity", | |
1964 "maxerr", | |
1965 "maxlen", | |
1966 "indent" | |
1967 ]; | |
1968 | |
1969 if (numericVals.indexOf(t.value) > -1 && (o === "/*jshint" || o === "/*jslint")) { | |
1970 b = +v.value; | |
1971 | |
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); | |
1974 } | |
1975 | |
1976 if (t.value === "indent") | |
1977 obj.white = true; | |
1978 | |
1979 obj[t.value] = b; | |
1980 } else if (t.value === "validthis") { | |
1981 if (funct["(global)"]) { | |
1982 error("Option 'validthis' can't be used in a global scope."); | |
1983 } else { | |
1984 if (v.value === "true" || v.value === "false") | |
1985 obj[t.value] = v.value === "true"; | |
1986 else | |
1987 error("Bad option value.", v); | |
1988 } | |
1989 } else if (t.value === "quotmark" && (o === "/*jshint")) { | |
1990 switch (v.value) { | |
1991 case "true": | |
1992 obj.quotmark = true; | |
1993 break; | |
1994 case "false": | |
1995 obj.quotmark = false; | |
1996 break; | |
1997 case "double": | |
1998 case "single": | |
1999 obj.quotmark = v.value; | |
2000 break; | |
2001 default: | |
2002 error("Bad option value.", v); | |
2003 } | |
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) { | |
2009 obj[tn] = !obj[tn]; | |
2010 } | |
2011 } else { | |
2012 obj[t.value] = v.value === "true"; | |
2013 } | |
2014 | |
2015 if (t.value === "newcap") | |
2016 obj["(explicitNewcap)"] = true; | |
2017 } else { | |
2018 error("Bad option value.", v); | |
2019 } | |
2020 t = lex.token(); | |
2021 } else { | |
2022 if (o === "/*jshint" || o === "/*jslint") { | |
2023 error("Missing option value.", t); | |
2024 } | |
2025 | |
2026 obj[t.value] = false; | |
2027 | |
2028 if (o === "/*global" && minus === true) { | |
2029 JSHINT.blacklist[t.value] = t.value; | |
2030 updatePredefined(); | |
2031 } | |
2032 | |
2033 t = v; | |
2034 } | |
2035 } | |
2036 | |
2037 if (o === "/*members") { | |
2038 option.quotmark = quotmarkValue; | |
2039 } | |
2040 | |
2041 combine(predefined, predef); | |
2042 | |
2043 for (var key in predef) { | |
2044 if (is_own(predef, key)) { | |
2045 declared[key] = nt; | |
2046 } | |
2047 } | |
2048 | |
2049 if (filter) { | |
2050 assume(); | |
2051 } | |
2052 } | |
2053 | |
2054 | |
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 ... | |
2058 // from | |
2059 // for ( var i = ... | |
2060 | |
2061 function peek(p) { | |
2062 var i = p || 0, j = 0, t; | |
2063 | |
2064 while (j <= i) { | |
2065 t = lookahead[j]; | |
2066 if (!t) { | |
2067 t = lookahead[j] = lex.token(); | |
2068 } | |
2069 j += 1; | |
2070 } | |
2071 return t; | |
2072 } | |
2073 | |
2074 | |
2075 | |
2076 // Produce the next token. It looks for programming errors. | |
2077 | |
2078 function advance(id, t) { | |
2079 switch (token.id) { | |
2080 case "(number)": | |
2081 if (nexttoken.id === ".") { | |
2082 warning("A dot following a number can be confused with a decimal point.", token); | |
2083 } | |
2084 break; | |
2085 case "-": | |
2086 if (nexttoken.id === "-" || nexttoken.id === "--") { | |
2087 warning("Confusing minusses."); | |
2088 } | |
2089 break; | |
2090 case "+": | |
2091 if (nexttoken.id === "+" || nexttoken.id === "++") { | |
2092 warning("Confusing plusses."); | |
2093 } | |
2094 break; | |
2095 } | |
2096 | |
2097 if (token.type === "(string)" || token.identifier) { | |
2098 anonname = token.value; | |
2099 } | |
2100 | |
2101 if (id && nexttoken.id !== id) { | |
2102 if (t) { | |
2103 if (nexttoken.id === "(end)") { | |
2104 warning("Unmatched '{a}'.", t, t.id); | |
2105 } else { | |
2106 warning("Expected '{a}' to match '{b}' from line {c} and instead saw '{d}'.", | |
2107 nexttoken, id, t.id, t.line, nexttoken.value); | |
2108 } | |
2109 } else if (nexttoken.type !== "(identifier)" || | |
2110 nexttoken.value !== id) { | |
2111 warning("Expected '{a}' and instead saw '{b}'.", | |
2112 nexttoken, id, nexttoken.value); | |
2113 } | |
2114 } | |
2115 | |
2116 prevtoken = token; | |
2117 token = nexttoken; | |
2118 for (;;) { | |
2119 nexttoken = lookahead.shift() || lex.token(); | |
2120 if (nexttoken.id === "(end)" || nexttoken.id === "(error)") { | |
2121 return; | |
2122 } | |
2123 if (nexttoken.type === "special") { | |
2124 doOption(); | |
2125 } else { | |
2126 if (nexttoken.id !== "(endline)") { | |
2127 break; | |
2128 } | |
2129 } | |
2130 } | |
2131 } | |
2132 | |
2133 | |
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. | |
2139 | |
2140 // .nud Null denotation | |
2141 // .fud First null denotation | |
2142 // .led Left denotation | |
2143 // lbp Left binding power | |
2144 // rbp Right binding power | |
2145 | |
2146 // They are elements of the parsing method called Top Down Operator Precedence. | |
2147 | |
2148 function expression(rbp, initial) { | |
2149 var left, isArray = false, isObject = false; | |
2150 | |
2151 if (nexttoken.id === "(end)") | |
2152 error("Unexpected early end of program.", token); | |
2153 | |
2154 advance(); | |
2155 if (initial) { | |
2156 anonname = "anonymous"; | |
2157 funct["(verb)"] = token.value; | |
2158 } | |
2159 if (initial === true && token.fud) { | |
2160 left = token.fud(); | |
2161 } else { | |
2162 if (token.nud) { | |
2163 left = token.nud(); | |
2164 } else { | |
2165 if (nexttoken.type === "(number)" && token.id === ".") { | |
2166 warning("A leading decimal point can be confused with a dot: '.{a}'.", | |
2167 token, nexttoken.value); | |
2168 advance(); | |
2169 return token; | |
2170 } else { | |
2171 error("Expected an identifier and instead saw '{a}'.", | |
2172 token, token.id); | |
2173 } | |
2174 } | |
2175 while (rbp < nexttoken.lbp) { | |
2176 isArray = token.value === "Array"; | |
2177 isObject = token.value === "Object"; | |
2178 | |
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 === ".")) { | |
2188 isArray = false; | |
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) { | |
2192 isObject = false; | |
2193 } | |
2194 } | |
2195 } | |
2196 | |
2197 advance(); | |
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); | |
2202 if (token.led) { | |
2203 left = token.led(left); | |
2204 } else { | |
2205 error("Expected an operator and instead saw '{a}'.", | |
2206 token, token.id); | |
2207 } | |
2208 } | |
2209 } | |
2210 return left; | |
2211 } | |
2212 | |
2213 | |
2214 // Functions for conformance of style. | |
2215 | |
2216 function adjacent(left, right) { | |
2217 left = left || token; | |
2218 right = right || nexttoken; | |
2219 if (option.white) { | |
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); | |
2223 } | |
2224 } | |
2225 } | |
2226 | |
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); | |
2232 } | |
2233 } | |
2234 | |
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); | |
2241 } | |
2242 } | |
2243 } | |
2244 | |
2245 function nonadjacent(left, right) { | |
2246 if (option.white) { | |
2247 left = left || token; | |
2248 right = right || nexttoken; | |
2249 if (left.value === ";" && right.value === ";") { | |
2250 return; | |
2251 } | |
2252 if (left.line === right.line && left.character === right.from) { | |
2253 left.from += (left.character - left.from); | |
2254 warning("Missing space after '{a}'.", | |
2255 left, left.value); | |
2256 } | |
2257 } | |
2258 } | |
2259 | |
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}'.", | |
2271 left, left.value); | |
2272 } | |
2273 } | |
2274 } | |
2275 | |
2276 function indentation(bias) { | |
2277 var i; | |
2278 if (option.white && nexttoken.id !== "(end)") { | |
2279 i = indent + (bias || 0); | |
2280 if (nexttoken.from !== i) { | |
2281 warning( | |
2282 "Expected '{a}' to have an indentation at {b} instead at {c}.", | |
2283 nexttoken, nexttoken.value, i, nexttoken.from); | |
2284 } | |
2285 } | |
2286 } | |
2287 | |
2288 function nolinebreak(t) { | |
2289 t = t || token; | |
2290 if (t.line !== nexttoken.line) { | |
2291 warning("Line breaking error '{a}'.", t, t.value); | |
2292 } | |
2293 } | |
2294 | |
2295 | |
2296 function comma() { | |
2297 if (token.line !== nexttoken.line) { | |
2298 if (!option.laxcomma) { | |
2299 if (comma.first) { | |
2300 warning("Comma warnings can be turned off with 'laxcomma'"); | |
2301 comma.first = false; | |
2302 } | |
2303 warning("Bad line breaking before '{a}'.", token, nexttoken.id); | |
2304 } | |
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); | |
2308 } | |
2309 advance(","); | |
2310 nonadjacent(token, nexttoken); | |
2311 } | |
2312 | |
2313 | |
2314 // Functional constructors for making the symbols that will be inherited by | |
2315 // tokens. | |
2316 | |
2317 function symbol(s, p) { | |
2318 var x = syntax[s]; | |
2319 if (!x || typeof x !== "object") { | |
2320 syntax[s] = x = { | |
2321 id: s, | |
2322 lbp: p, | |
2323 value: s | |
2324 }; | |
2325 } | |
2326 return x; | |
2327 } | |
2328 | |
2329 | |
2330 function delim(s) { | |
2331 return symbol(s, 0); | |
2332 } | |
2333 | |
2334 | |
2335 function stmt(s, f) { | |
2336 var x = delim(s); | |
2337 x.identifier = x.reserved = true; | |
2338 x.fud = f; | |
2339 return x; | |
2340 } | |
2341 | |
2342 | |
2343 function blockstmt(s, f) { | |
2344 var x = stmt(s, f); | |
2345 x.block = true; | |
2346 return x; | |
2347 } | |
2348 | |
2349 | |
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; | |
2354 } | |
2355 return x; | |
2356 } | |
2357 | |
2358 | |
2359 function prefix(s, f) { | |
2360 var x = symbol(s, 150); | |
2361 reserveName(x); | |
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); | |
2371 } | |
2372 } | |
2373 return this; | |
2374 }; | |
2375 return x; | |
2376 } | |
2377 | |
2378 | |
2379 function type(s, f) { | |
2380 var x = delim(s); | |
2381 x.type = s; | |
2382 x.nud = f; | |
2383 return x; | |
2384 } | |
2385 | |
2386 | |
2387 function reserve(s, f) { | |
2388 var x = type(s, f); | |
2389 x.identifier = x.reserved = true; | |
2390 return x; | |
2391 } | |
2392 | |
2393 | |
2394 function reservevar(s, v) { | |
2395 return reserve(s, function () { | |
2396 if (typeof v === "function") { | |
2397 v(this); | |
2398 } | |
2399 return this; | |
2400 }); | |
2401 } | |
2402 | |
2403 | |
2404 function infix(s, f, p, w) { | |
2405 var x = symbol(s, p); | |
2406 reserveName(x); | |
2407 x.led = function (left) { | |
2408 if (!w) { | |
2409 nobreaknonadjacent(prevtoken, token); | |
2410 nonadjacent(token, nexttoken); | |
2411 } | |
2412 if (s === "in" && left.id === "!") { | |
2413 warning("Confusing use of '{a}'.", left, "!"); | |
2414 } | |
2415 if (typeof f === "function") { | |
2416 return f(left, this); | |
2417 } else { | |
2418 this.left = left; | |
2419 this.right = expression(p); | |
2420 return this; | |
2421 } | |
2422 }; | |
2423 return x; | |
2424 } | |
2425 | |
2426 | |
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); | |
2433 | |
2434 if (isIdentifier(left, "NaN") || isIdentifier(right, "NaN")) { | |
2435 warning("Use the isNaN function to compare with NaN.", this); | |
2436 } else if (f) { | |
2437 f.apply(this, [left, right]); | |
2438 } | |
2439 if (left.id === "!") { | |
2440 warning("Confusing use of '{a}'.", left, "!"); | |
2441 } | |
2442 if (right.id === "!") { | |
2443 warning("Confusing use of '{a}'.", right, "!"); | |
2444 } | |
2445 this.left = left; | |
2446 this.right = right; | |
2447 return this; | |
2448 }; | |
2449 return x; | |
2450 } | |
2451 | |
2452 | |
2453 function isPoorRelation(node) { | |
2454 return 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"); | |
2461 } | |
2462 | |
2463 | |
2464 function assignop(s) { | |
2465 symbol(s, 20).exps = true; | |
2466 | |
2467 return infix(s, function (left, that) { | |
2468 that.left = left; | |
2469 | |
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); | |
2475 } | |
2476 | |
2477 if (left) { | |
2478 if (option.esnext && funct[left.value] === "const") { | |
2479 warning("Attempting to override '{a}' which is a constant", left, left.value); | |
2480 } | |
2481 | |
2482 if (left.id === "." || left.id === "[") { | |
2483 if (!left.left || left.left.value === "arguments") { | |
2484 warning("Bad assignment.", that); | |
2485 } | |
2486 that.right = expression(19); | |
2487 return that; | |
2488 } else if (left.identifier && !left.reserved) { | |
2489 if (funct[left.value] === "exception") { | |
2490 warning("Do not assign to the exception parameter.", left); | |
2491 } | |
2492 that.right = expression(19); | |
2493 return that; | |
2494 } | |
2495 | |
2496 if (left === syntax["function"]) { | |
2497 warning( | |
2498 "Expected an identifier in an assignment and instead saw a function invocation.", | |
2499 token); | |
2500 } | |
2501 } | |
2502 | |
2503 error("Bad assignment.", that); | |
2504 }, 20); | |
2505 } | |
2506 | |
2507 | |
2508 function bitwise(s, f, p) { | |
2509 var x = symbol(s, p); | |
2510 reserveName(x); | |
2511 x.led = (typeof f === "function") ? f : function (left) { | |
2512 if (option.bitwise) { | |
2513 warning("Unexpected use of '{a}'.", this, this.id); | |
2514 } | |
2515 this.left = left; | |
2516 this.right = expression(p); | |
2517 return this; | |
2518 }; | |
2519 return x; | |
2520 } | |
2521 | |
2522 | |
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); | |
2528 } | |
2529 nonadjacent(prevtoken, token); | |
2530 nonadjacent(token, nexttoken); | |
2531 if (left) { | |
2532 if (left.id === "." || left.id === "[" || | |
2533 (left.identifier && !left.reserved)) { | |
2534 expression(19); | |
2535 return that; | |
2536 } | |
2537 if (left === syntax["function"]) { | |
2538 warning( | |
2539 "Expected an identifier in an assignment, and instead saw a function invocation.", | |
2540 token); | |
2541 } | |
2542 return that; | |
2543 } | |
2544 error("Bad assignment.", that); | |
2545 }, 20); | |
2546 } | |
2547 | |
2548 | |
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); | |
2557 } | |
2558 this.left = left; | |
2559 return this; | |
2560 }; | |
2561 return x; | |
2562 } | |
2563 | |
2564 | |
2565 // fnparam means that this identifier is being defined as a function | |
2566 // argument (see identifier()) | |
2567 function optionalidentifier(fnparam) { | |
2568 if (nexttoken.identifier) { | |
2569 advance(); | |
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).", | |
2576 token, token.id); | |
2577 } | |
2578 } | |
2579 return token.value; | |
2580 } | |
2581 } | |
2582 | |
2583 // fnparam means that this identifier is being defined as a function | |
2584 // argument | |
2585 function identifier(fnparam) { | |
2586 var i = optionalidentifier(fnparam); | |
2587 if (i) { | |
2588 return i; | |
2589 } | |
2590 if (token.id === "function" && nexttoken.id === "(") { | |
2591 warning("Missing name in function declaration."); | |
2592 } else { | |
2593 error("Expected an identifier and instead saw '{a}'.", | |
2594 nexttoken, nexttoken.value); | |
2595 } | |
2596 } | |
2597 | |
2598 | |
2599 function reachable(s) { | |
2600 var i = 0, t; | |
2601 if (nexttoken.id !== ";" || noreach) { | |
2602 return; | |
2603 } | |
2604 for (;;) { | |
2605 t = peek(i); | |
2606 if (t.reach) { | |
2607 return; | |
2608 } | |
2609 if (t.id !== "(endline)") { | |
2610 if (t.id === "function") { | |
2611 if (!option.latedef) { | |
2612 break; | |
2613 } | |
2614 warning( | |
2615 "Inner functions should be listed at the top of the outer function.", t); | |
2616 break; | |
2617 } | |
2618 warning("Unreachable '{a}' after '{b}'.", t, t.value, s); | |
2619 break; | |
2620 } | |
2621 i += 1; | |
2622 } | |
2623 } | |
2624 | |
2625 | |
2626 function statement(noindent) { | |
2627 var i = indent, r, s = scope, t = nexttoken; | |
2628 | |
2629 if (t.id === ";") { | |
2630 advance(";"); | |
2631 return; | |
2632 } | |
2633 | |
2634 // Is this a labelled statement? | |
2635 | |
2636 if (t.identifier && !t.reserved && peek().id === ":") { | |
2637 advance(); | |
2638 advance(":"); | |
2639 scope = Object.create(s); | |
2640 addlabel(t.value, "label"); | |
2641 | |
2642 if (!nexttoken.labelled && nexttoken.value !== "{") { | |
2643 warning("Label '{a}' on {b} statement.", nexttoken, t.value, nexttoken.value); | |
2644 } | |
2645 | |
2646 if (jx.test(t.value + ":")) { | |
2647 warning("Label '{a}' looks like a javascript url.", t, t.value); | |
2648 } | |
2649 | |
2650 nexttoken.label = t.value; | |
2651 t = nexttoken; | |
2652 } | |
2653 | |
2654 // Is it a lonely block? | |
2655 | |
2656 if (t.id === "{") { | |
2657 block(true, true); | |
2658 return; | |
2659 } | |
2660 | |
2661 // Parse the statement. | |
2662 | |
2663 if (!noindent) { | |
2664 indentation(); | |
2665 } | |
2666 r = expression(0, true); | |
2667 | |
2668 // Look for the final semicolon. | |
2669 | |
2670 if (!t.block) { | |
2671 if (!option.expr && (!r || !r.exps)) { | |
2672 warning("Expected an assignment or function call and instead saw an expression.", | |
2673 token); | |
2674 } else if (option.nonew && r.id === "(" && r.left.id === "new") { | |
2675 warning("Do not use 'new' for side effects.", t); | |
2676 } | |
2677 | |
2678 if (nexttoken.id === ",") { | |
2679 return comma(); | |
2680 } | |
2681 | |
2682 if (nexttoken.id !== ";") { | |
2683 if (!option.asi) { | |
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); | |
2690 } | |
2691 } | |
2692 } else { | |
2693 adjacent(token, nexttoken); | |
2694 advance(";"); | |
2695 nonadjacent(token, nexttoken); | |
2696 } | |
2697 } | |
2698 | |
2699 // Restore the indentation. | |
2700 | |
2701 indent = i; | |
2702 scope = s; | |
2703 return r; | |
2704 } | |
2705 | |
2706 | |
2707 function statements(startLine) { | |
2708 var a = [], p; | |
2709 | |
2710 while (!nexttoken.reach && nexttoken.id !== "(end)") { | |
2711 if (nexttoken.id === ";") { | |
2712 p = peek(); | |
2713 if (!p || p.id !== "(") { | |
2714 warning("Unnecessary semicolon."); | |
2715 } | |
2716 advance(";"); | |
2717 } else { | |
2718 a.push(statement(startLine === nexttoken.line)); | |
2719 } | |
2720 } | |
2721 return a; | |
2722 } | |
2723 | |
2724 | |
2725 /* | |
2726 * read all directives | |
2727 * recognizes a simple form of asi, but always | |
2728 * warns, if it is used | |
2729 */ | |
2730 function directives() { | |
2731 var i, p, pn; | |
2732 | |
2733 for (;;) { | |
2734 if (nexttoken.id === "(string)") { | |
2735 p = peek(0); | |
2736 if (p.id === "(endline)") { | |
2737 i = 1; | |
2738 do { | |
2739 pn = peek(i); | |
2740 i = i + 1; | |
2741 } while (pn.id === "(endline)"); | |
2742 | |
2743 if (pn.id !== ";") { | |
2744 if (pn.id !== "(string)" && pn.id !== "(number)" && | |
2745 pn.id !== "(regexp)" && pn.identifier !== true && | |
2746 pn.id !== "}") { | |
2747 break; | |
2748 } | |
2749 warning("Missing semicolon.", nexttoken); | |
2750 } else { | |
2751 p = pn; | |
2752 } | |
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 !== ";") { | |
2757 break; | |
2758 } | |
2759 | |
2760 indentation(); | |
2761 advance(); | |
2762 if (directive[token.value]) { | |
2763 warning("Unnecessary directive \"{a}\".", token, token.value); | |
2764 } | |
2765 | |
2766 if (token.value === "use strict") { | |
2767 if (!option["(explicitNewcap)"]) | |
2768 option.newcap = true; | |
2769 option.undef = true; | |
2770 } | |
2771 | |
2772 // there's no directive negation, so always set to true | |
2773 directive[token.value] = true; | |
2774 | |
2775 if (p.id === ";") { | |
2776 advance(";"); | |
2777 } | |
2778 continue; | |
2779 } | |
2780 break; | |
2781 } | |
2782 } | |
2783 | |
2784 | |
2785 /* | |
2786 * Parses a single block. A block is a sequence of statements wrapped in | |
2787 * braces. | |
2788 * | |
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 | |
2792 */ | |
2793 function block(ordinary, stmt, isfunc) { | |
2794 var a, | |
2795 b = inblock, | |
2796 old_indent = indent, | |
2797 m, | |
2798 s = scope, | |
2799 t, | |
2800 line, | |
2801 d; | |
2802 | |
2803 inblock = ordinary; | |
2804 | |
2805 if (!ordinary || !option.funcscope) | |
2806 scope = Object.create(scope); | |
2807 | |
2808 nonadjacent(token, nexttoken); | |
2809 t = nexttoken; | |
2810 | |
2811 var metrics = funct["(metrics)"]; | |
2812 metrics.nestedBlockDepth += 1; | |
2813 metrics.verifyMaxNestedBlockDepthPerFunction(); | |
2814 | |
2815 if (nexttoken.id === "{") { | |
2816 advance("{"); | |
2817 line = token.line; | |
2818 if (nexttoken.id !== "}") { | |
2819 indent += option.indent; | |
2820 while (!ordinary && nexttoken.from > indent) { | |
2821 indent += option.indent; | |
2822 } | |
2823 | |
2824 if (isfunc) { | |
2825 m = {}; | |
2826 for (d in directive) { | |
2827 if (is_own(directive, d)) { | |
2828 m[d] = directive[d]; | |
2829 } | |
2830 } | |
2831 directives(); | |
2832 | |
2833 if (option.strict && funct["(context)"]["(global)"]) { | |
2834 if (!m["use strict"] && !directive["use strict"]) { | |
2835 warning("Missing \"use strict\" statement."); | |
2836 } | |
2837 } | |
2838 } | |
2839 | |
2840 a = statements(line); | |
2841 | |
2842 metrics.statementCount += a.length; | |
2843 | |
2844 if (isfunc) { | |
2845 directive = m; | |
2846 } | |
2847 | |
2848 indent -= option.indent; | |
2849 if (line !== nexttoken.line) { | |
2850 indentation(); | |
2851 } | |
2852 } else if (line !== nexttoken.line) { | |
2853 indentation(); | |
2854 } | |
2855 advance("}", t); | |
2856 indent = old_indent; | |
2857 } else if (!ordinary) { | |
2858 error("Expected '{a}' and instead saw '{b}'.", | |
2859 nexttoken, "{", nexttoken.value); | |
2860 } else { | |
2861 if (!stmt || option.curly) | |
2862 warning("Expected '{a}' and instead saw '{b}'.", | |
2863 nexttoken, "{", nexttoken.value); | |
2864 | |
2865 noreach = true; | |
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; | |
2870 noreach = false; | |
2871 } | |
2872 funct["(verb)"] = null; | |
2873 if (!ordinary || !option.funcscope) scope = s; | |
2874 inblock = b; | |
2875 if (ordinary && option.noempty && (!a || a.length === 0)) { | |
2876 warning("Empty block."); | |
2877 } | |
2878 metrics.nestedBlockDepth -= 1; | |
2879 return a; | |
2880 } | |
2881 | |
2882 | |
2883 function countMember(m) { | |
2884 if (membersOnly && typeof membersOnly[m] !== "boolean") { | |
2885 warning("Unexpected /*member '{a}'.", token, m); | |
2886 } | |
2887 if (typeof member[m] === "number") { | |
2888 member[m] += 1; | |
2889 } else { | |
2890 member[m] = 1; | |
2891 } | |
2892 } | |
2893 | |
2894 | |
2895 function note_implied(token) { | |
2896 var name = token.value, line = token.line, a = implied[name]; | |
2897 if (typeof a === "function") { | |
2898 a = false; | |
2899 } | |
2900 | |
2901 if (!a) { | |
2902 a = [line]; | |
2903 implied[name] = a; | |
2904 } else if (a[a.length - 1] !== line) { | |
2905 a.push(line); | |
2906 } | |
2907 } | |
2908 | |
2909 | |
2910 // Build the syntax table by declaring the syntactic elements of the language. | |
2911 | |
2912 type("(number)", function () { | |
2913 return this; | |
2914 }); | |
2915 | |
2916 type("(string)", function () { | |
2917 return this; | |
2918 }); | |
2919 | |
2920 syntax["(identifier)"] = { | |
2921 type: "(identifier)", | |
2922 lbp: 0, | |
2923 identifier: true, | |
2924 nud: function () { | |
2925 var v = this.value, | |
2926 s = scope[v], | |
2927 f; | |
2928 | |
2929 if (typeof s === "function") { | |
2930 // Protection against accidental inheritance. | |
2931 s = undefined; | |
2932 } else if (typeof s === "boolean") { | |
2933 f = funct; | |
2934 funct = functions[0]; | |
2935 addlabel(v, "var"); | |
2936 s = funct; | |
2937 funct = f; | |
2938 } | |
2939 | |
2940 // The name is in scope and defined in the current function. | |
2941 if (funct === s) { | |
2942 // Change 'unused' to 'var', and reject labels. | |
2943 switch (funct[v]) { | |
2944 case "unused": | |
2945 funct[v] = "var"; | |
2946 break; | |
2947 case "unction": | |
2948 funct[v] = "function"; | |
2949 this["function"] = true; | |
2950 break; | |
2951 case "function": | |
2952 this["function"] = true; | |
2953 break; | |
2954 case "label": | |
2955 warning("'{a}' is a statement label.", token, v); | |
2956 break; | |
2957 } | |
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. | |
2961 // | |
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. | |
2965 | |
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 === "["))) { | |
2971 | |
2972 isundef(funct, "'{a}' is not defined.", token, v); | |
2973 } | |
2974 } | |
2975 | |
2976 note_implied(token); | |
2977 } else { | |
2978 // If the name is already defined in the current | |
2979 // function, but not as outer, then there is a scope error. | |
2980 | |
2981 switch (funct[v]) { | |
2982 case "closure": | |
2983 case "function": | |
2984 case "var": | |
2985 case "unused": | |
2986 warning("'{a}' used out of scope.", token, v); | |
2987 break; | |
2988 case "label": | |
2989 warning("'{a}' is a statement label.", token, v); | |
2990 break; | |
2991 case "outer": | |
2992 case "global": | |
2993 break; | |
2994 default: | |
2995 // If the name is defined in an outer function, make an outer entry, | |
2996 // and if it was unused, make it var. | |
2997 if (s === true) { | |
2998 funct[v] = true; | |
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. | |
3006 if (option.undef) { | |
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") || | |
3010 (nexttoken && | |
3011 (nexttoken.value === "." || nexttoken.value === "["))) { | |
3012 | |
3013 isundef(funct, "'{a}' is not defined.", token, v); | |
3014 } | |
3015 } | |
3016 funct[v] = true; | |
3017 note_implied(token); | |
3018 } else { | |
3019 switch (s[v]) { | |
3020 case "function": | |
3021 case "unction": | |
3022 this["function"] = true; | |
3023 s[v] = "closure"; | |
3024 funct[v] = s["(global)"] ? "global" : "outer"; | |
3025 break; | |
3026 case "var": | |
3027 case "unused": | |
3028 s[v] = "closure"; | |
3029 funct[v] = s["(global)"] ? "global" : "outer"; | |
3030 break; | |
3031 case "closure": | |
3032 funct[v] = s["(global)"] ? "global" : "outer"; | |
3033 break; | |
3034 case "label": | |
3035 warning("'{a}' is a statement label.", token, v); | |
3036 } | |
3037 } | |
3038 } | |
3039 } | |
3040 return this; | |
3041 }, | |
3042 led: function () { | |
3043 error("Expected an operator and instead saw '{a}'.", | |
3044 nexttoken, nexttoken.value); | |
3045 } | |
3046 }; | |
3047 | |
3048 type("(regexp)", function () { | |
3049 return this; | |
3050 }); | |
3051 | |
3052 | |
3053 // ECMAScript parser | |
3054 | |
3055 delim("(endline)"); | |
3056 delim("(begin)"); | |
3057 delim("(end)").reach = true; | |
3058 delim("</").reach = true; | |
3059 delim("<!"); | |
3060 delim("<!--"); | |
3061 delim("-->"); | |
3062 delim("(error)").reach = true; | |
3063 delim("}").reach = true; | |
3064 delim(")"); | |
3065 delim("]"); | |
3066 delim("\"").reach = true; | |
3067 delim("'").reach = true; | |
3068 delim(";"); | |
3069 delim(":").reach = true; | |
3070 delim(","); | |
3071 delim("#"); | |
3072 delim("@"); | |
3073 reserve("else"); | |
3074 reserve("case").reach = true; | |
3075 reserve("catch"); | |
3076 reserve("default").reach = true; | |
3077 reserve("finally"); | |
3078 reservevar("arguments", function (x) { | |
3079 if (directive["use strict"] && funct["(global)"]) { | |
3080 warning("Strict violation.", x); | |
3081 } | |
3082 }); | |
3083 reservevar("eval"); | |
3084 reservevar("false"); | |
3085 reservevar("Infinity"); | |
3086 reservevar("null"); | |
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); | |
3091 } | |
3092 }); | |
3093 reservevar("true"); | |
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 '/='."); | |
3101 }; | |
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) { | |
3110 that.left = left; | |
3111 that.right = expression(10); | |
3112 advance(":"); | |
3113 that["else"] = expression(10); | |
3114 return that; | |
3115 }, 30); | |
3116 | |
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"); | |
3124 | |
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); | |
3131 | |
3132 return this; | |
3133 }); | |
3134 relation("==="); | |
3135 relation("!=", function (left, right) { | |
3136 var eqnull = option.eqnull && | |
3137 (left.value === "null" || right.value === "null"); | |
3138 | |
3139 if (!eqnull && option.eqeqeq) { | |
3140 warning("Expected '{a}' and instead saw '{b}'.", | |
3141 this, "!==", "!="); | |
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); | |
3148 } | |
3149 return this; | |
3150 }); | |
3151 relation("!=="); | |
3152 relation("<"); | |
3153 relation(">"); | |
3154 relation("<="); | |
3155 relation(">="); | |
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); | |
3168 } | |
3169 return left; | |
3170 } | |
3171 that.left = left; | |
3172 that.right = right; | |
3173 return that; | |
3174 }, 130); | |
3175 prefix("+", "num"); | |
3176 prefix("+++", function () { | |
3177 warning("Confusing pluses."); | |
3178 this.right = expression(150); | |
3179 this.arity = "unary"; | |
3180 return this; | |
3181 }); | |
3182 infix("+++", function (left) { | |
3183 warning("Confusing pluses."); | |
3184 this.left = left; | |
3185 this.right = expression(130); | |
3186 return this; | |
3187 }, 130); | |
3188 infix("-", "sub", 130); | |
3189 prefix("-", "neg"); | |
3190 prefix("---", function () { | |
3191 warning("Confusing minuses."); | |
3192 this.right = expression(150); | |
3193 this.arity = "unary"; | |
3194 return this; | |
3195 }); | |
3196 infix("---", function (left) { | |
3197 warning("Confusing minuses."); | |
3198 this.left = left; | |
3199 this.right = expression(130); | |
3200 return this; | |
3201 }, 130); | |
3202 infix("*", "mult", 140); | |
3203 infix("/", "div", 140); | |
3204 infix("%", "mod", 140); | |
3205 | |
3206 suffix("++", "postinc"); | |
3207 prefix("++", "preinc"); | |
3208 syntax["++"].exps = true; | |
3209 | |
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."); | |
3217 } | |
3218 this.first = p; | |
3219 return this; | |
3220 }).exps = true; | |
3221 | |
3222 prefix("~", function () { | |
3223 if (option.bitwise) { | |
3224 warning("Unexpected '{a}'.", this, "~"); | |
3225 } | |
3226 expression(150); | |
3227 return this; | |
3228 }); | |
3229 | |
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, "!"); | |
3235 } | |
3236 return this; | |
3237 }); | |
3238 prefix("typeof", "typeof"); | |
3239 prefix("new", function () { | |
3240 var c = expression(155), i; | |
3241 if (c && c.id !== "function") { | |
3242 if (c.identifier) { | |
3243 c["new"] = true; | |
3244 switch (c.value) { | |
3245 case "Number": | |
3246 case "String": | |
3247 case "Boolean": | |
3248 case "Math": | |
3249 case "JSON": | |
3250 warning("Do not use {a} as a constructor.", prevtoken, c.value); | |
3251 break; | |
3252 case "Function": | |
3253 if (!option.evil) { | |
3254 warning("The Function constructor is eval."); | |
3255 } | |
3256 break; | |
3257 case "Date": | |
3258 case "RegExp": | |
3259 break; | |
3260 default: | |
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.", | |
3265 token); | |
3266 } | |
3267 } | |
3268 } | |
3269 } else { | |
3270 if (c.id !== "." && c.id !== "[" && c.id !== "(") { | |
3271 warning("Bad constructor.", token); | |
3272 } | |
3273 } | |
3274 } else { | |
3275 if (!option.supernew) | |
3276 warning("Weird construction. Delete 'new'.", this); | |
3277 } | |
3278 adjacent(token, nexttoken); | |
3279 if (nexttoken.id !== "(" && !option.supernew) { | |
3280 warning("Missing '()' invoking a constructor.", | |
3281 token, token.value); | |
3282 } | |
3283 this.first = c; | |
3284 return this; | |
3285 }); | |
3286 syntax["new"].exps = true; | |
3287 | |
3288 prefix("void").exps = true; | |
3289 | |
3290 infix(".", function (left, that) { | |
3291 adjacent(prevtoken, token); | |
3292 nobreak(); | |
3293 var m = identifier(); | |
3294 if (typeof m === "string") { | |
3295 countMember(m); | |
3296 } | |
3297 that.left = left; | |
3298 that.right = m; | |
3299 if (left && left.value === "arguments" && (m === "callee" || m === "caller")) { | |
3300 if (option.noarg) | |
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); | |
3307 } | |
3308 if (!option.evil && (m === "eval" || m === "execScript")) { | |
3309 warning("eval is evil."); | |
3310 } | |
3311 return that; | |
3312 }, 160, true); | |
3313 | |
3314 infix("(", function (left, that) { | |
3315 if (prevtoken.id !== "}" && prevtoken.id !== ")") { | |
3316 nobreak(prevtoken, token); | |
3317 } | |
3318 nospace(); | |
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."); | |
3323 } | |
3324 var n = 0, | |
3325 p = []; | |
3326 if (left) { | |
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); | |
3334 } | |
3335 } | |
3336 } | |
3337 } | |
3338 } | |
3339 if (nexttoken.id !== ")") { | |
3340 for (;;) { | |
3341 p[p.length] = expression(10); | |
3342 n += 1; | |
3343 if (nexttoken.id !== ",") { | |
3344 break; | |
3345 } | |
3346 comma(); | |
3347 } | |
3348 } | |
3349 advance(")"); | |
3350 nospace(prevtoken, token); | |
3351 if (typeof left === "object") { | |
3352 if (left.value === "parseInt" && n === 1) { | |
3353 warning("Missing radix parameter.", token); | |
3354 } | |
3355 if (!option.evil) { | |
3356 if (left.value === "eval" || left.value === "Function" || | |
3357 left.value === "execScript") { | |
3358 warning("eval is evil.", left); | |
3359 | |
3360 if (p[0] && [0].id === "(string)") { | |
3361 addInternalSrc(left, p[0].value); | |
3362 } | |
3363 } else if (p[0] && p[0].id === "(string)" && | |
3364 (left.value === "setTimeout" || | |
3365 left.value === "setInterval")) { | |
3366 warning( | |
3367 "Implied eval is evil. Pass a function instead of a string.", left); | |
3368 addInternalSrc(left, p[0].value); | |
3369 | |
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")) { | |
3376 warning( | |
3377 "Implied eval is evil. Pass a function instead of a string.", left); | |
3378 addInternalSrc(left, p[0].value); | |
3379 } | |
3380 } | |
3381 if (!left.identifier && left.id !== "." && left.id !== "[" && | |
3382 left.id !== "(" && left.id !== "&&" && left.id !== "||" && | |
3383 left.id !== "?") { | |
3384 warning("Bad invocation.", left); | |
3385 } | |
3386 } | |
3387 that.left = left; | |
3388 return that; | |
3389 }, 155, true).exps = true; | |
3390 | |
3391 prefix("(", function () { | |
3392 nospace(); | |
3393 if (nexttoken.id === "function") { | |
3394 nexttoken.immed = true; | |
3395 } | |
3396 var v = expression(0); | |
3397 advance(")", this); | |
3398 nospace(prevtoken, token); | |
3399 if (option.immed && v.id === "function") { | |
3400 if (nexttoken.id !== "(" && | |
3401 (nexttoken.id !== "." || (peek().value !== "call" && peek().value !== "apply"))) { | |
3402 warning( | |
3403 "Do not wrap function literals in parens unless they are to be immediately invoked.", | |
3404 this); | |
3405 } | |
3406 } | |
3407 | |
3408 return v; | |
3409 }); | |
3410 | |
3411 infix("[", function (left, that) { | |
3412 nobreak(prevtoken, token); | |
3413 nospace(); | |
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); | |
3418 } | |
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); | |
3425 } | |
3426 } | |
3427 } | |
3428 advance("]", that); | |
3429 nospace(prevtoken, token); | |
3430 that.left = left; | |
3431 that.right = e; | |
3432 return that; | |
3433 }, 160, true); | |
3434 | |
3435 prefix("[", function () { | |
3436 var b = token.line !== nexttoken.line; | |
3437 this.first = []; | |
3438 if (b) { | |
3439 indent += option.indent; | |
3440 if (nexttoken.from === indent + option.indent) { | |
3441 indent += option.indent; | |
3442 } | |
3443 } | |
3444 while (nexttoken.id !== "(end)") { | |
3445 while (nexttoken.id === ",") { | |
3446 if (!option.es5) | |
3447 warning("Extra comma."); | |
3448 advance(","); | |
3449 } | |
3450 if (nexttoken.id === "]") { | |
3451 break; | |
3452 } | |
3453 if (b && token.line !== nexttoken.line) { | |
3454 indentation(); | |
3455 } | |
3456 this.first.push(expression(10)); | |
3457 if (nexttoken.id === ",") { | |
3458 comma(); | |
3459 if (nexttoken.id === "]" && !option.es5) { | |
3460 warning("Extra comma.", token); | |
3461 break; | |
3462 } | |
3463 } else { | |
3464 break; | |
3465 } | |
3466 } | |
3467 if (b) { | |
3468 indent -= option.indent; | |
3469 indentation(); | |
3470 } | |
3471 advance("]", this); | |
3472 return this; | |
3473 }, 160); | |
3474 | |
3475 | |
3476 function property_name() { | |
3477 var id = optionalidentifier(true); | |
3478 if (!id) { | |
3479 if (nexttoken.id === "(string)") { | |
3480 id = nexttoken.value; | |
3481 advance(); | |
3482 } else if (nexttoken.id === "(number)") { | |
3483 id = nexttoken.value.toString(); | |
3484 advance(); | |
3485 } | |
3486 } | |
3487 return id; | |
3488 } | |
3489 | |
3490 | |
3491 function functionparams() { | |
3492 var next = nexttoken; | |
3493 var params = []; | |
3494 var ident; | |
3495 | |
3496 advance("("); | |
3497 nospace(); | |
3498 | |
3499 if (nexttoken.id === ")") { | |
3500 advance(")"); | |
3501 return; | |
3502 } | |
3503 | |
3504 for (;;) { | |
3505 ident = identifier(true); | |
3506 params.push(ident); | |
3507 addlabel(ident, "unused", token); | |
3508 if (nexttoken.id === ",") { | |
3509 comma(); | |
3510 } else { | |
3511 advance(")", next); | |
3512 nospace(prevtoken, token); | |
3513 return params; | |
3514 } | |
3515 } | |
3516 } | |
3517 | |
3518 | |
3519 function doFunction(name, statement) { | |
3520 var f; | |
3521 var oldOption = option; | |
3522 var oldScope = scope; | |
3523 | |
3524 option = Object.create(option); | |
3525 scope = Object.create(scope); | |
3526 | |
3527 funct = { | |
3528 "(name)" : name || "\"" + anonname + "\"", | |
3529 "(line)" : nexttoken.line, | |
3530 "(character)": nexttoken.character, | |
3531 "(context)" : funct, | |
3532 "(breakage)" : 0, | |
3533 "(loopage)" : 0, | |
3534 "(metrics)" : createMetrics(nexttoken), | |
3535 "(scope)" : scope, | |
3536 "(statement)": statement, | |
3537 "(tokens)" : {} | |
3538 }; | |
3539 | |
3540 f = funct; | |
3541 token.funct = funct; | |
3542 | |
3543 functions.push(funct); | |
3544 | |
3545 if (name) { | |
3546 addlabel(name, "function"); | |
3547 } | |
3548 | |
3549 funct["(params)"] = functionparams(); | |
3550 funct["(metrics)"].verifyMaxParametersPerFunction(funct["(params)"]); | |
3551 | |
3552 block(false, false, true); | |
3553 | |
3554 funct["(metrics)"].verifyMaxStatementsPerFunction(); | |
3555 funct["(metrics)"].verifyMaxComplexityPerFunction(); | |
3556 | |
3557 scope = oldScope; | |
3558 option = oldOption; | |
3559 funct["(last)"] = token.line; | |
3560 funct["(lastcharacter)"] = token.character; | |
3561 funct = funct["(context)"]; | |
3562 | |
3563 return f; | |
3564 } | |
3565 | |
3566 function createMetrics(functionStartToken) { | |
3567 return { | |
3568 statementCount: 0, | |
3569 nestedBlockDepth: -1, | |
3570 ComplexityCount: 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); | |
3576 } | |
3577 }, | |
3578 | |
3579 verifyMaxParametersPerFunction: function (params) { | |
3580 params = params || []; | |
3581 | |
3582 if (option.maxparams && params.length > option.maxparams) { | |
3583 var message = "Too many parameters per function (" + params.length + ")."; | |
3584 warning(message, functionStartToken); | |
3585 } | |
3586 }, | |
3587 | |
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 + ")."; | |
3593 warning(message); | |
3594 } | |
3595 }, | |
3596 | |
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); | |
3603 } | |
3604 } | |
3605 }; | |
3606 } | |
3607 | |
3608 function increaseComplexityCount() { | |
3609 funct["(metrics)"].ComplexityCount += 1; | |
3610 } | |
3611 | |
3612 | |
3613 (function (x) { | |
3614 x.nud = function () { | |
3615 var b, f, i, p, t; | |
3616 var props = {}; // All properties, including accessors | |
3617 | |
3618 function saveProperty(name, token) { | |
3619 if (props[name] && is_own(props, name)) | |
3620 warning("Duplicate member '{a}'.", nexttoken, i); | |
3621 else | |
3622 props[name] = {}; | |
3623 | |
3624 props[name].basic = true; | |
3625 props[name].basicToken = token; | |
3626 } | |
3627 | |
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); | |
3632 } else { | |
3633 props[name] = {}; | |
3634 } | |
3635 | |
3636 props[name].setter = true; | |
3637 props[name].setterToken = token; | |
3638 } | |
3639 | |
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); | |
3644 } else { | |
3645 props[name] = {}; | |
3646 } | |
3647 | |
3648 props[name].getter = true; | |
3649 props[name].getterToken = token; | |
3650 } | |
3651 | |
3652 b = token.line !== nexttoken.line; | |
3653 if (b) { | |
3654 indent += option.indent; | |
3655 if (nexttoken.from === indent + option.indent) { | |
3656 indent += option.indent; | |
3657 } | |
3658 } | |
3659 for (;;) { | |
3660 if (nexttoken.id === "}") { | |
3661 break; | |
3662 } | |
3663 if (b) { | |
3664 indentation(); | |
3665 } | |
3666 if (nexttoken.value === "get" && peek().id !== ":") { | |
3667 advance("get"); | |
3668 if (!option.es5) { | |
3669 error("get/set are ES5 features."); | |
3670 } | |
3671 i = property_name(); | |
3672 if (!i) { | |
3673 error("Missing property name."); | |
3674 } | |
3675 saveGetter(i); | |
3676 t = nexttoken; | |
3677 adjacent(token, nexttoken); | |
3678 f = doFunction(); | |
3679 p = f["(params)"]; | |
3680 if (p) { | |
3681 warning("Unexpected parameter '{a}' in get {b} function.", t, p[0], i); | |
3682 } | |
3683 adjacent(token, nexttoken); | |
3684 } else if (nexttoken.value === "set" && peek().id !== ":") { | |
3685 advance("set"); | |
3686 if (!option.es5) { | |
3687 error("get/set are ES5 features."); | |
3688 } | |
3689 i = property_name(); | |
3690 if (!i) { | |
3691 error("Missing property name."); | |
3692 } | |
3693 saveSetter(i, nexttoken); | |
3694 t = nexttoken; | |
3695 adjacent(token, nexttoken); | |
3696 f = doFunction(); | |
3697 p = f["(params)"]; | |
3698 if (!p || p.length !== 1) { | |
3699 warning("Expected a single parameter in set {a} function.", t, i); | |
3700 } | |
3701 } else { | |
3702 i = property_name(); | |
3703 saveProperty(i, nexttoken); | |
3704 if (typeof i !== "string") { | |
3705 break; | |
3706 } | |
3707 advance(":"); | |
3708 nonadjacent(token, nexttoken); | |
3709 expression(10); | |
3710 } | |
3711 | |
3712 countMember(i); | |
3713 if (nexttoken.id === ",") { | |
3714 comma(); | |
3715 if (nexttoken.id === ",") { | |
3716 warning("Extra comma.", token); | |
3717 } else if (nexttoken.id === "}" && !option.es5) { | |
3718 warning("Extra comma.", token); | |
3719 } | |
3720 } else { | |
3721 break; | |
3722 } | |
3723 } | |
3724 if (b) { | |
3725 indent -= option.indent; | |
3726 indentation(); | |
3727 } | |
3728 advance("}", this); | |
3729 | |
3730 // Check for lonely setters if in the ES5 mode. | |
3731 if (option.es5) { | |
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); | |
3735 } | |
3736 } | |
3737 } | |
3738 return this; | |
3739 }; | |
3740 x.fud = function () { | |
3741 error("Expected to see a statement and instead saw a block.", token); | |
3742 }; | |
3743 }(delim("{"))); | |
3744 | |
3745 // This Function is called when esnext option is set to true | |
3746 // it adds the `const` statement to JSHINT | |
3747 | |
3748 useESNextSyntax = function () { | |
3749 var conststatement = stmt("const", function (prefix) { | |
3750 var id, name, value; | |
3751 | |
3752 this.first = []; | |
3753 for (;;) { | |
3754 nonadjacent(token, nexttoken); | |
3755 id = identifier(); | |
3756 if (funct[id] === "const") { | |
3757 warning("const '" + id + "' has already been declared"); | |
3758 } | |
3759 if (funct["(global)"] && predefined[id] === false) { | |
3760 warning("Redefinition of '{a}'.", token, id); | |
3761 } | |
3762 addlabel(id, "const"); | |
3763 if (prefix) { | |
3764 break; | |
3765 } | |
3766 name = token; | |
3767 this.first.push(token); | |
3768 | |
3769 if (nexttoken.id !== "=") { | |
3770 warning("const " + | |
3771 "'{a}' is initialized to 'undefined'.", token, id); | |
3772 } | |
3773 | |
3774 if (nexttoken.id === "=") { | |
3775 nonadjacent(token, nexttoken); | |
3776 advance("="); | |
3777 nonadjacent(token, nexttoken); | |
3778 if (nexttoken.id === "undefined") { | |
3779 warning("It is not necessary to initialize " + | |
3780 "'{a}' to 'undefined'.", token, id); | |
3781 } | |
3782 if (peek(0).id === "=" && nexttoken.identifier) { | |
3783 error("Constant {a} was not declared correctly.", | |
3784 nexttoken, nexttoken.value); | |
3785 } | |
3786 value = expression(0); | |
3787 name.first = value; | |
3788 } | |
3789 | |
3790 if (nexttoken.id !== ",") { | |
3791 break; | |
3792 } | |
3793 comma(); | |
3794 } | |
3795 return this; | |
3796 }); | |
3797 conststatement.exps = true; | |
3798 }; | |
3799 | |
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; | |
3804 | |
3805 if (funct["(onevar)"] && option.onevar) { | |
3806 warning("Too many var statements."); | |
3807 } else if (!funct["(global)"]) { | |
3808 funct["(onevar)"] = true; | |
3809 } | |
3810 | |
3811 this.first = []; | |
3812 | |
3813 for (;;) { | |
3814 nonadjacent(token, nexttoken); | |
3815 id = identifier(); | |
3816 | |
3817 if (option.esnext && funct[id] === "const") { | |
3818 warning("const '" + id + "' has already been declared"); | |
3819 } | |
3820 | |
3821 if (funct["(global)"] && predefined[id] === false) { | |
3822 warning("Redefinition of '{a}'.", token, id); | |
3823 } | |
3824 | |
3825 addlabel(id, "unused", token); | |
3826 | |
3827 if (prefix) { | |
3828 break; | |
3829 } | |
3830 | |
3831 name = token; | |
3832 this.first.push(token); | |
3833 | |
3834 if (nexttoken.id === "=") { | |
3835 nonadjacent(token, nexttoken); | |
3836 advance("="); | |
3837 nonadjacent(token, nexttoken); | |
3838 if (nexttoken.id === "undefined") { | |
3839 warning("It is not necessary to initialize '{a}' to 'undefined'.", token, id); | |
3840 } | |
3841 if (peek(0).id === "=" && nexttoken.identifier) { | |
3842 error("Variable {a} was not declared correctly.", | |
3843 nexttoken, nexttoken.value); | |
3844 } | |
3845 value = expression(0); | |
3846 name.first = value; | |
3847 } | |
3848 if (nexttoken.id !== ",") { | |
3849 break; | |
3850 } | |
3851 comma(); | |
3852 } | |
3853 return this; | |
3854 }); | |
3855 varstatement.exps = true; | |
3856 | |
3857 blockstmt("function", function () { | |
3858 if (inblock) { | |
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); | |
3862 | |
3863 } | |
3864 var i = identifier(); | |
3865 if (option.esnext && funct[i] === "const") { | |
3866 warning("const '" + i + "' has already been declared"); | |
3867 } | |
3868 adjacent(token, nexttoken); | |
3869 addlabel(i, "unction", token); | |
3870 | |
3871 doFunction(i, { statement: true }); | |
3872 if (nexttoken.id === "(" && nexttoken.line === token.line) { | |
3873 error( | |
3874 "Function declarations are not invocable. Wrap the whole function invocation in parens."); | |
3875 } | |
3876 return this; | |
3877 }); | |
3878 | |
3879 prefix("function", function () { | |
3880 var i = optionalidentifier(); | |
3881 if (i) { | |
3882 adjacent(token, nexttoken); | |
3883 } else { | |
3884 nonadjacent(token, nexttoken); | |
3885 } | |
3886 doFunction(i); | |
3887 if (!option.loopfunc && funct["(loopage)"]) { | |
3888 warning("Don't make functions within a loop."); | |
3889 } | |
3890 return this; | |
3891 }); | |
3892 | |
3893 blockstmt("if", function () { | |
3894 var t = nexttoken; | |
3895 increaseComplexityCount(); | |
3896 advance("("); | |
3897 nonadjacent(this, t); | |
3898 nospace(); | |
3899 expression(20); | |
3900 if (nexttoken.id === "=") { | |
3901 if (!option.boss) | |
3902 warning("Expected a conditional expression and instead saw an assignment."); | |
3903 advance("="); | |
3904 expression(20); | |
3905 } | |
3906 advance(")", t); | |
3907 nospace(prevtoken, token); | |
3908 block(true, true); | |
3909 if (nexttoken.id === "else") { | |
3910 nonadjacent(token, nexttoken); | |
3911 advance("else"); | |
3912 if (nexttoken.id === "if" || nexttoken.id === "switch") { | |
3913 statement(true); | |
3914 } else { | |
3915 block(true, true); | |
3916 } | |
3917 } | |
3918 return this; | |
3919 }); | |
3920 | |
3921 blockstmt("try", function () { | |
3922 var b; | |
3923 | |
3924 function doCatch() { | |
3925 var oldScope = scope; | |
3926 var e; | |
3927 | |
3928 advance("catch"); | |
3929 nonadjacent(token, nexttoken); | |
3930 advance("("); | |
3931 | |
3932 scope = Object.create(oldScope); | |
3933 | |
3934 e = nexttoken.value; | |
3935 if (nexttoken.type !== "(identifier)") { | |
3936 e = null; | |
3937 warning("Expected an identifier and instead saw '{a}'.", nexttoken, e); | |
3938 } | |
3939 | |
3940 advance(); | |
3941 advance(")"); | |
3942 | |
3943 funct = { | |
3944 "(name)" : "(catch)", | |
3945 "(line)" : nexttoken.line, | |
3946 "(character)": nexttoken.character, | |
3947 "(context)" : funct, | |
3948 "(breakage)" : funct["(breakage)"], | |
3949 "(loopage)" : funct["(loopage)"], | |
3950 "(scope)" : scope, | |
3951 "(statement)": false, | |
3952 "(metrics)" : createMetrics(nexttoken), | |
3953 "(catch)" : true, | |
3954 "(tokens)" : {} | |
3955 }; | |
3956 | |
3957 if (e) { | |
3958 addlabel(e, "exception"); | |
3959 } | |
3960 | |
3961 token.funct = funct; | |
3962 functions.push(funct); | |
3963 | |
3964 block(false); | |
3965 | |
3966 scope = oldScope; | |
3967 | |
3968 funct["(last)"] = token.line; | |
3969 funct["(lastcharacter)"] = token.character; | |
3970 funct = funct["(context)"]; | |
3971 } | |
3972 | |
3973 block(false); | |
3974 | |
3975 if (nexttoken.id === "catch") { | |
3976 increaseComplexityCount(); | |
3977 doCatch(); | |
3978 b = true; | |
3979 } | |
3980 | |
3981 if (nexttoken.id === "finally") { | |
3982 advance("finally"); | |
3983 block(false); | |
3984 return; | |
3985 } else if (!b) { | |
3986 error("Expected '{a}' and instead saw '{b}'.", | |
3987 nexttoken, "catch", nexttoken.value); | |
3988 } | |
3989 | |
3990 return this; | |
3991 }); | |
3992 | |
3993 blockstmt("while", function () { | |
3994 var t = nexttoken; | |
3995 funct["(breakage)"] += 1; | |
3996 funct["(loopage)"] += 1; | |
3997 increaseComplexityCount(); | |
3998 advance("("); | |
3999 nonadjacent(this, t); | |
4000 nospace(); | |
4001 expression(20); | |
4002 if (nexttoken.id === "=") { | |
4003 if (!option.boss) | |
4004 warning("Expected a conditional expression and instead saw an assignment."); | |
4005 advance("="); | |
4006 expression(20); | |
4007 } | |
4008 advance(")", t); | |
4009 nospace(prevtoken, token); | |
4010 block(true, true); | |
4011 funct["(breakage)"] -= 1; | |
4012 funct["(loopage)"] -= 1; | |
4013 return this; | |
4014 }).labelled = true; | |
4015 | |
4016 blockstmt("with", function () { | |
4017 var t = nexttoken; | |
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); | |
4022 } | |
4023 | |
4024 advance("("); | |
4025 nonadjacent(this, t); | |
4026 nospace(); | |
4027 expression(0); | |
4028 advance(")", t); | |
4029 nospace(prevtoken, token); | |
4030 block(true, true); | |
4031 | |
4032 return this; | |
4033 }); | |
4034 | |
4035 blockstmt("switch", function () { | |
4036 var t = nexttoken, | |
4037 g = false; | |
4038 funct["(breakage)"] += 1; | |
4039 advance("("); | |
4040 nonadjacent(this, t); | |
4041 nospace(); | |
4042 this.condition = expression(20); | |
4043 advance(")", t); | |
4044 nospace(prevtoken, token); | |
4045 nonadjacent(token, nexttoken); | |
4046 t = nexttoken; | |
4047 advance("{"); | |
4048 nonadjacent(token, nexttoken); | |
4049 indent += option.indent; | |
4050 this.cases = []; | |
4051 for (;;) { | |
4052 switch (nexttoken.id) { | |
4053 case "case": | |
4054 switch (funct["(verb)"]) { | |
4055 case "break": | |
4056 case "case": | |
4057 case "continue": | |
4058 case "return": | |
4059 case "switch": | |
4060 case "throw": | |
4061 break; | |
4062 default: | |
4063 // You can tell JSHint that you don't use break intentionally by | |
4064 // adding a comment /* falls through */ on a line just before | |
4065 // the next `case`. | |
4066 if (!ft.test(lines[nexttoken.line - 2])) { | |
4067 warning( | |
4068 "Expected a 'break' statement before 'case'.", | |
4069 token); | |
4070 } | |
4071 } | |
4072 indentation(-option.indent); | |
4073 advance("case"); | |
4074 this.cases.push(expression(20)); | |
4075 increaseComplexityCount(); | |
4076 g = true; | |
4077 advance(":"); | |
4078 funct["(verb)"] = "case"; | |
4079 break; | |
4080 case "default": | |
4081 switch (funct["(verb)"]) { | |
4082 case "break": | |
4083 case "continue": | |
4084 case "return": | |
4085 case "throw": | |
4086 break; | |
4087 default: | |
4088 if (!ft.test(lines[nexttoken.line - 2])) { | |
4089 warning( | |
4090 "Expected a 'break' statement before 'default'.", | |
4091 token); | |
4092 } | |
4093 } | |
4094 indentation(-option.indent); | |
4095 advance("default"); | |
4096 g = true; | |
4097 advance(":"); | |
4098 break; | |
4099 case "}": | |
4100 indent -= option.indent; | |
4101 indentation(); | |
4102 advance("}", t); | |
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); | |
4107 } | |
4108 funct["(breakage)"] -= 1; | |
4109 funct["(verb)"] = undefined; | |
4110 return; | |
4111 case "(end)": | |
4112 error("Missing '{a}'.", nexttoken, "}"); | |
4113 return; | |
4114 default: | |
4115 if (g) { | |
4116 switch (token.id) { | |
4117 case ",": | |
4118 error("Each value should have its own case label."); | |
4119 return; | |
4120 case ":": | |
4121 g = false; | |
4122 statements(); | |
4123 break; | |
4124 default: | |
4125 error("Missing ':' on a case clause.", token); | |
4126 return; | |
4127 } | |
4128 } else { | |
4129 if (token.id === ":") { | |
4130 advance(":"); | |
4131 error("Unexpected '{a}'.", token, ":"); | |
4132 statements(); | |
4133 } else { | |
4134 error("Expected '{a}' and instead saw '{b}'.", | |
4135 nexttoken, "case", nexttoken.value); | |
4136 return; | |
4137 } | |
4138 } | |
4139 } | |
4140 } | |
4141 }).labelled = true; | |
4142 | |
4143 stmt("debugger", function () { | |
4144 if (!option.debug) { | |
4145 warning("All 'debugger' statements should be removed."); | |
4146 } | |
4147 return this; | |
4148 }).exps = true; | |
4149 | |
4150 (function () { | |
4151 var x = stmt("do", function () { | |
4152 funct["(breakage)"] += 1; | |
4153 funct["(loopage)"] += 1; | |
4154 increaseComplexityCount(); | |
4155 | |
4156 this.first = block(true); | |
4157 advance("while"); | |
4158 var t = nexttoken; | |
4159 nonadjacent(token, t); | |
4160 advance("("); | |
4161 nospace(); | |
4162 expression(20); | |
4163 if (nexttoken.id === "=") { | |
4164 if (!option.boss) | |
4165 warning("Expected a conditional expression and instead saw an assignment."); | |
4166 advance("="); | |
4167 expression(20); | |
4168 } | |
4169 advance(")", t); | |
4170 nospace(prevtoken, token); | |
4171 funct["(breakage)"] -= 1; | |
4172 funct["(loopage)"] -= 1; | |
4173 return this; | |
4174 }); | |
4175 x.labelled = true; | |
4176 x.exps = true; | |
4177 }()); | |
4178 | |
4179 blockstmt("for", function () { | |
4180 var s, t = nexttoken; | |
4181 funct["(breakage)"] += 1; | |
4182 funct["(loopage)"] += 1; | |
4183 increaseComplexityCount(); | |
4184 advance("("); | |
4185 nonadjacent(this, t); | |
4186 nospace(); | |
4187 if (peek(nexttoken.id === "var" ? 1 : 0).id === "in") { | |
4188 if (nexttoken.id === "var") { | |
4189 advance("var"); | |
4190 varstatement.fud.call(varstatement, true); | |
4191 } else { | |
4192 switch (funct[nexttoken.value]) { | |
4193 case "unused": | |
4194 funct[nexttoken.value] = "var"; | |
4195 break; | |
4196 case "var": | |
4197 break; | |
4198 default: | |
4199 warning("Bad for in variable '{a}'.", | |
4200 nexttoken, nexttoken.value); | |
4201 } | |
4202 advance(); | |
4203 } | |
4204 advance("in"); | |
4205 expression(20); | |
4206 advance(")", t); | |
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); | |
4212 } | |
4213 funct["(breakage)"] -= 1; | |
4214 funct["(loopage)"] -= 1; | |
4215 return this; | |
4216 } else { | |
4217 if (nexttoken.id !== ";") { | |
4218 if (nexttoken.id === "var") { | |
4219 advance("var"); | |
4220 varstatement.fud.call(varstatement); | |
4221 } else { | |
4222 for (;;) { | |
4223 expression(0, "for"); | |
4224 if (nexttoken.id !== ",") { | |
4225 break; | |
4226 } | |
4227 comma(); | |
4228 } | |
4229 } | |
4230 } | |
4231 nolinebreak(token); | |
4232 advance(";"); | |
4233 if (nexttoken.id !== ";") { | |
4234 expression(20); | |
4235 if (nexttoken.id === "=") { | |
4236 if (!option.boss) | |
4237 warning("Expected a conditional expression and instead saw an assignment."); | |
4238 advance("="); | |
4239 expression(20); | |
4240 } | |
4241 } | |
4242 nolinebreak(token); | |
4243 advance(";"); | |
4244 if (nexttoken.id === ";") { | |
4245 error("Expected '{a}' and instead saw '{b}'.", | |
4246 nexttoken, ")", ";"); | |
4247 } | |
4248 if (nexttoken.id !== ")") { | |
4249 for (;;) { | |
4250 expression(0, "for"); | |
4251 if (nexttoken.id !== ",") { | |
4252 break; | |
4253 } | |
4254 comma(); | |
4255 } | |
4256 } | |
4257 advance(")", t); | |
4258 nospace(prevtoken, token); | |
4259 block(true, true); | |
4260 funct["(breakage)"] -= 1; | |
4261 funct["(loopage)"] -= 1; | |
4262 return this; | |
4263 } | |
4264 }).labelled = true; | |
4265 | |
4266 | |
4267 stmt("break", function () { | |
4268 var v = nexttoken.value; | |
4269 | |
4270 if (funct["(breakage)"] === 0) | |
4271 warning("Unexpected '{a}'.", nexttoken, this.value); | |
4272 | |
4273 if (!option.asi) | |
4274 nolinebreak(this); | |
4275 | |
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); | |
4282 } | |
4283 this.first = nexttoken; | |
4284 advance(); | |
4285 } | |
4286 } | |
4287 reachable("break"); | |
4288 return this; | |
4289 }).exps = true; | |
4290 | |
4291 | |
4292 stmt("continue", function () { | |
4293 var v = nexttoken.value; | |
4294 | |
4295 if (funct["(breakage)"] === 0) | |
4296 warning("Unexpected '{a}'.", nexttoken, this.value); | |
4297 | |
4298 if (!option.asi) | |
4299 nolinebreak(this); | |
4300 | |
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); | |
4307 } | |
4308 this.first = nexttoken; | |
4309 advance(); | |
4310 } | |
4311 } else if (!funct["(loopage)"]) { | |
4312 warning("Unexpected '{a}'.", nexttoken, this.value); | |
4313 } | |
4314 reachable("continue"); | |
4315 return this; | |
4316 }).exps = true; | |
4317 | |
4318 | |
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."); | |
4323 | |
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); | |
4329 } | |
4330 this.first = expression(0); | |
4331 } | |
4332 } else if (!option.asi) { | |
4333 nolinebreak(this); // always warn (Line breaking error) | |
4334 } | |
4335 reachable("return"); | |
4336 return this; | |
4337 }).exps = true; | |
4338 | |
4339 | |
4340 stmt("throw", function () { | |
4341 nolinebreak(this); | |
4342 nonadjacent(token, nexttoken); | |
4343 this.first = expression(20); | |
4344 reachable("throw"); | |
4345 return this; | |
4346 }).exps = true; | |
4347 | |
4348 // Superfluous reserved words | |
4349 | |
4350 reserve("class"); | |
4351 reserve("const"); | |
4352 reserve("enum"); | |
4353 reserve("export"); | |
4354 reserve("extends"); | |
4355 reserve("import"); | |
4356 reserve("super"); | |
4357 | |
4358 reserve("let"); | |
4359 reserve("yield"); | |
4360 reserve("implements"); | |
4361 reserve("interface"); | |
4362 reserve("package"); | |
4363 reserve("private"); | |
4364 reserve("protected"); | |
4365 reserve("public"); | |
4366 reserve("static"); | |
4367 | |
4368 | |
4369 // Parse JSON | |
4370 | |
4371 function jsonValue() { | |
4372 | |
4373 function jsonObject() { | |
4374 var o = {}, t = nexttoken; | |
4375 advance("{"); | |
4376 if (nexttoken.id !== "}") { | |
4377 for (;;) { | |
4378 if (nexttoken.id === "(end)") { | |
4379 error("Missing '}' to match '{' from line {a}.", | |
4380 nexttoken, t.line); | |
4381 } else if (nexttoken.id === "}") { | |
4382 warning("Unexpected comma.", token); | |
4383 break; | |
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); | |
4389 } | |
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); | |
4398 } else { | |
4399 o[nexttoken.value] = true; | |
4400 } | |
4401 advance(); | |
4402 advance(":"); | |
4403 jsonValue(); | |
4404 if (nexttoken.id !== ",") { | |
4405 break; | |
4406 } | |
4407 advance(","); | |
4408 } | |
4409 } | |
4410 advance("}"); | |
4411 } | |
4412 | |
4413 function jsonArray() { | |
4414 var t = nexttoken; | |
4415 advance("["); | |
4416 if (nexttoken.id !== "]") { | |
4417 for (;;) { | |
4418 if (nexttoken.id === "(end)") { | |
4419 error("Missing ']' to match '[' from line {a}.", | |
4420 nexttoken, t.line); | |
4421 } else if (nexttoken.id === "]") { | |
4422 warning("Unexpected comma.", token); | |
4423 break; | |
4424 } else if (nexttoken.id === ",") { | |
4425 error("Unexpected comma.", nexttoken); | |
4426 } | |
4427 jsonValue(); | |
4428 if (nexttoken.id !== ",") { | |
4429 break; | |
4430 } | |
4431 advance(","); | |
4432 } | |
4433 } | |
4434 advance("]"); | |
4435 } | |
4436 | |
4437 switch (nexttoken.id) { | |
4438 case "{": | |
4439 jsonObject(); | |
4440 break; | |
4441 case "[": | |
4442 jsonArray(); | |
4443 break; | |
4444 case "true": | |
4445 case "false": | |
4446 case "null": | |
4447 case "(number)": | |
4448 case "(string)": | |
4449 advance(); | |
4450 break; | |
4451 case "-": | |
4452 advance("-"); | |
4453 if (token.character !== nexttoken.from) { | |
4454 warning("Unexpected space after '-'.", token); | |
4455 } | |
4456 adjacent(token, nexttoken); | |
4457 advance("(number)"); | |
4458 break; | |
4459 default: | |
4460 error("Expected a JSON value.", nexttoken); | |
4461 } | |
4462 } | |
4463 | |
4464 | |
4465 // The actual JSHINT function itself. | |
4466 var itself = function (s, o, g) { | |
4467 var a, i, k, x; | |
4468 var optionKeys; | |
4469 var newOptionObj = {}; | |
4470 | |
4471 if (o && o.scope) { | |
4472 JSHINT.scope = o.scope; | |
4473 } else { | |
4474 JSHINT.errors = []; | |
4475 JSHINT.undefs = []; | |
4476 JSHINT.internals = []; | |
4477 JSHINT.blacklist = {}; | |
4478 JSHINT.scope = "(main)"; | |
4479 } | |
4480 | |
4481 predefined = Object.create(standard); | |
4482 declared = Object.create(null); | |
4483 combine(predefined, g || {}); | |
4484 | |
4485 if (o) { | |
4486 a = o.predef; | |
4487 if (a) { | |
4488 if (!Array.isArray(a) && typeof a === "object") { | |
4489 a = Object.keys(a); | |
4490 } | |
4491 a.forEach(function (item) { | |
4492 var slice; | |
4493 if (item[0] === "-") { | |
4494 slice = item.slice(1); | |
4495 JSHINT.blacklist[slice] = slice; | |
4496 } else { | |
4497 predefined[item] = true; | |
4498 } | |
4499 }); | |
4500 } | |
4501 | |
4502 optionKeys = Object.keys(o); | |
4503 for (x = 0; x < optionKeys.length; x++) { | |
4504 newOptionObj[optionKeys[x]] = o[optionKeys[x]]; | |
4505 | |
4506 if (optionKeys[x] === "newcap" && o[optionKeys[x]] === false) | |
4507 newOptionObj["(explicitNewcap)"] = true; | |
4508 | |
4509 if (optionKeys[x] === "indent") | |
4510 newOptionObj.white = true; | |
4511 } | |
4512 } | |
4513 | |
4514 option = newOptionObj; | |
4515 | |
4516 option.indent = option.indent || 4; | |
4517 option.maxerr = option.maxerr || 50; | |
4518 | |
4519 tab = ""; | |
4520 for (i = 0; i < option.indent; i += 1) { | |
4521 tab += " "; | |
4522 } | |
4523 indent = 1; | |
4524 global = Object.create(predefined); | |
4525 scope = global; | |
4526 funct = { | |
4527 "(global)": true, | |
4528 "(name)": "(global)", | |
4529 "(scope)": scope, | |
4530 "(breakage)": 0, | |
4531 "(loopage)": 0, | |
4532 "(tokens)": {}, | |
4533 "(metrics)": createMetrics(nexttoken) | |
4534 }; | |
4535 functions = [funct]; | |
4536 urls = []; | |
4537 stack = null; | |
4538 member = {}; | |
4539 membersOnly = null; | |
4540 implied = {}; | |
4541 inblock = false; | |
4542 lookahead = []; | |
4543 jsonmode = false; | |
4544 warnings = 0; | |
4545 lines = []; | |
4546 unuseds = []; | |
4547 | |
4548 if (!isString(s) && !Array.isArray(s)) { | |
4549 errorAt("Input is neither a string nor an array of strings.", 0); | |
4550 return false; | |
4551 } | |
4552 | |
4553 if (isString(s) && /^\s*$/g.test(s)) { | |
4554 errorAt("Input is an empty string.", 0); | |
4555 return false; | |
4556 } | |
4557 | |
4558 if (s.length === 0) { | |
4559 errorAt("Input is an empty array.", 0); | |
4560 return false; | |
4561 } | |
4562 | |
4563 lex.init(s); | |
4564 | |
4565 prereg = true; | |
4566 directive = {}; | |
4567 | |
4568 prevtoken = token = nexttoken = syntax["(begin)"]; | |
4569 | |
4570 // Check options | |
4571 for (var name in o) { | |
4572 if (is_own(o, name)) { | |
4573 checkOption(name, token); | |
4574 } | |
4575 } | |
4576 | |
4577 assume(); | |
4578 | |
4579 // combine the passed globals after we've assumed all our options | |
4580 combine(predefined, g || {}); | |
4581 | |
4582 //reset values | |
4583 comma.first = true; | |
4584 quotmark = undefined; | |
4585 | |
4586 try { | |
4587 advance(); | |
4588 switch (nexttoken.id) { | |
4589 case "{": | |
4590 case "[": | |
4591 option.laxbreak = true; | |
4592 jsonmode = true; | |
4593 jsonValue(); | |
4594 break; | |
4595 default: | |
4596 directives(); | |
4597 if (directive["use strict"] && !option.globalstrict) { | |
4598 warning("Use the function form of \"use strict\".", prevtoken); | |
4599 } | |
4600 | |
4601 statements(); | |
4602 } | |
4603 advance((nexttoken && nexttoken.value !== ".") ? "(end)" : undefined); | |
4604 | |
4605 var markDefined = function (name, context) { | |
4606 do { | |
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. | |
4613 | |
4614 if (context[name] === "unused") | |
4615 context[name] = "var"; | |
4616 else if (context[name] === "unction") | |
4617 context[name] = "closure"; | |
4618 | |
4619 return true; | |
4620 } | |
4621 | |
4622 context = context["(context)"]; | |
4623 } while (context); | |
4624 | |
4625 return false; | |
4626 }; | |
4627 | |
4628 var clearImplied = function (name, line) { | |
4629 if (!implied[name]) | |
4630 return; | |
4631 | |
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]); | |
4636 } | |
4637 | |
4638 if (newImplied.length === 0) | |
4639 delete implied[name]; | |
4640 else | |
4641 implied[name] = newImplied; | |
4642 }; | |
4643 | |
4644 var warnUnused = function (name, token) { | |
4645 var line = token.line; | |
4646 var chr = token.character; | |
4647 | |
4648 if (option.unused) | |
4649 warningAt("'{a}' is defined but never used.", line, chr, name); | |
4650 | |
4651 unuseds.push({ | |
4652 name: name, | |
4653 line: line, | |
4654 character: chr | |
4655 }); | |
4656 }; | |
4657 | |
4658 var checkUnused = function (func, key) { | |
4659 var type = func[key]; | |
4660 var token = func["(tokens)"][key]; | |
4661 | |
4662 if (key.charAt(0) === "(") | |
4663 return; | |
4664 | |
4665 if (type !== "unused" && type !== "unction") | |
4666 return; | |
4667 | |
4668 // Params are checked separately from other variables. | |
4669 if (func["(params)"] && func["(params)"].indexOf(key) !== -1) | |
4670 return; | |
4671 | |
4672 warnUnused(key, token); | |
4673 }; | |
4674 | |
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); | |
4678 | |
4679 if (markDefined(k[2].value, k[0])) { | |
4680 clearImplied(k[2].value, k[2].line); | |
4681 } else { | |
4682 warning.apply(warning, k.slice(1)); | |
4683 } | |
4684 } | |
4685 | |
4686 functions.forEach(function (func) { | |
4687 for (var key in func) { | |
4688 if (is_own(func, key)) { | |
4689 checkUnused(func, key); | |
4690 } | |
4691 } | |
4692 | |
4693 if (!func["(params)"]) | |
4694 return; | |
4695 | |
4696 var params = func["(params)"].slice(); | |
4697 var param = params.pop(); | |
4698 var type; | |
4699 | |
4700 while (param) { | |
4701 type = func[param]; | |
4702 | |
4703 // 'undefined' is a special case for (function (window, undefined) { ... })(); | |
4704 // patterns. | |
4705 | |
4706 if (param === "undefined") | |
4707 return; | |
4708 | |
4709 if (type !== "unused" && type !== "unction") | |
4710 return; | |
4711 | |
4712 warnUnused(param, func["(tokens)"][param]); | |
4713 param = params.pop(); | |
4714 } | |
4715 }); | |
4716 | |
4717 for (var key in declared) { | |
4718 if (is_own(declared, key) && !is_own(global, key)) { | |
4719 warnUnused(key, declared[key]); | |
4720 } | |
4721 } | |
4722 } catch (e) { | |
4723 if (e) { | |
4724 var nt = nexttoken || {}; | |
4725 JSHINT.errors.push({ | |
4726 raw : e.raw, | |
4727 reason : e.message, | |
4728 line : e.line || nt.line, | |
4729 character : e.character || nt.from | |
4730 }, null); | |
4731 } | |
4732 } | |
4733 | |
4734 // Loop over the listed "internals", and check them as well. | |
4735 | |
4736 if (JSHINT.scope === "(main)") { | |
4737 o = o || {}; | |
4738 | |
4739 for (i = 0; i < JSHINT.internals.length; i += 1) { | |
4740 k = JSHINT.internals[i]; | |
4741 o.scope = k.elem; | |
4742 itself(k.value, o, g); | |
4743 } | |
4744 } | |
4745 | |
4746 return JSHINT.errors.length === 0; | |
4747 }; | |
4748 | |
4749 // Data summary. | |
4750 itself.data = function () { | |
4751 var data = { | |
4752 functions: [], | |
4753 options: option | |
4754 }; | |
4755 var implieds = []; | |
4756 var members = []; | |
4757 var fu, f, i, j, n, globals; | |
4758 | |
4759 if (itself.errors.length) { | |
4760 data.errors = itself.errors; | |
4761 } | |
4762 | |
4763 if (jsonmode) { | |
4764 data.json = true; | |
4765 } | |
4766 | |
4767 for (n in implied) { | |
4768 if (is_own(implied, n)) { | |
4769 implieds.push({ | |
4770 name: n, | |
4771 line: implied[n] | |
4772 }); | |
4773 } | |
4774 } | |
4775 | |
4776 if (implieds.length > 0) { | |
4777 data.implieds = implieds; | |
4778 } | |
4779 | |
4780 if (urls.length > 0) { | |
4781 data.urls = urls; | |
4782 } | |
4783 | |
4784 globals = Object.keys(scope); | |
4785 if (globals.length > 0) { | |
4786 data.globals = globals; | |
4787 } | |
4788 | |
4789 for (i = 1; i < functions.length; i += 1) { | |
4790 f = functions[i]; | |
4791 fu = {}; | |
4792 | |
4793 for (j = 0; j < functionicity.length; j += 1) { | |
4794 fu[functionicity[j]] = []; | |
4795 } | |
4796 | |
4797 for (j = 0; j < functionicity.length; j += 1) { | |
4798 if (fu[functionicity[j]].length === 0) { | |
4799 delete fu[functionicity[j]]; | |
4800 } | |
4801 } | |
4802 | |
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); | |
4810 } | |
4811 | |
4812 if (unuseds.length > 0) { | |
4813 data.unused = unuseds; | |
4814 } | |
4815 | |
4816 members = []; | |
4817 for (n in member) { | |
4818 if (typeof member[n] === "number") { | |
4819 data.member = member; | |
4820 break; | |
4821 } | |
4822 } | |
4823 | |
4824 return data; | |
4825 }; | |
4826 | |
4827 itself.jshint = itself; | |
4828 | |
4829 return itself; | |
4830 }()); | |
4831 | |
4832 // Make JSHINT a Node module, if possible. | |
4833 if (typeof exports === "object" && exports) { | |
4834 exports.JSHINT = JSHINT; | |
4835 } |