aboutsummaryrefslogtreecommitdiffstats
path: root/apps/files/js
diff options
context:
space:
mode:
authordependabot[bot] <support@dependabot.com>2018-10-22 17:11:57 +0000
committerdependabot[bot] <support@dependabot.com>2018-10-22 17:11:57 +0000
commit54802275259d65dd2835bc8c6738d5c0b2aa66bb (patch)
tree03eb3a9858e8bd1b872f36683a8d19071e23a235 /apps/files/js
parentf737ae719de2e2af534bce03a8920bbb96aa8700 (diff)
downloadnextcloud-server-54802275259d65dd2835bc8c6738d5c0b2aa66bb.tar.gz
nextcloud-server-54802275259d65dd2835bc8c6738d5c0b2aa66bb.zip
Update sabre/dav requirement from 3.2.2 to 3.2.3 in /build/integration
Updates the requirements on [sabre/dav](https://github.com/sabre-io/dav) to permit the latest version. - [Release notes](https://github.com/sabre-io/dav/releases) - [Changelog](https://github.com/sabre-io/dav/blob/master/CHANGELOG.md) - [Commits](https://github.com/sabre-io/dav/commits/3.2.3) Signed-off-by: dependabot[bot] <support@dependabot.com>
Diffstat (limited to 'apps/files/js')
0 files changed, 0 insertions, 0 deletions
pan class="o">&& x !== '"') { warningAt("Strings must use doublequote.", line, character); } function esc(n) { var i = parseInt(s.substr(j + 1, n), 16); j += n; if (i >= 32 && i <= 126 && i !== 34 && i !== 92 && i !== 39) { warningAt("Unnecessary escapement.", line, character); } character += n; c = String.fromCharCode(i); } j = 0; unclosedString: for (;;) { while (j >= s.length) { j = 0; var cl = line, cf = from; if (!nextLine()) { errorAt("Unclosed string.", cl, cf); break unclosedString; } if (allowNewLine) { allowNewLine = false; } else { warningAt("Unclosed string.", cl, cf); } } c = s.charAt(j); if (c === x) { character += 1; s = s.substr(j + 1); return it('(string)', r, x); } if (c < ' ') { if (c === '\n' || c === '\r') { break; } warningAt("Control character in string: {a}.", line, character + j, s.slice(0, j)); } else if (c === '\\') { j += 1; character += 1; c = s.charAt(j); switch (c) { case '\\': case '"': case '/': break; case '\'': if (jsonmode) { warningAt("Avoid \\'.", line, character); } break; case 'b': c = '\b'; break; case 'f': c = '\f'; break; case 'n': c = '\n'; break; case 'r': c = '\r'; break; case 't': c = '\t'; break; case 'u': esc(4); break; case 'v': if (jsonmode) { warningAt("Avoid \\v.", line, character); } c = '\v'; break; case 'x': if (jsonmode) { warningAt("Avoid \\x-.", line, character); } esc(2); break; case '': // last character is escape character // always allow new line if escaped, but show // warning if option is not set allowNewLine = true; if (option.multistr) { if (jsonmode) { warningAt("Avoid EOL escapement.", line, character); } c = ''; character -= 1; break; } warningAt("Bad escapement of EOL. Use option multistr if needed.", line, character); break; default: warningAt("Bad escapement.", line, character); } } r += c; character += 1; j += 1; } } for (;;) { if (!s) { return it(nextLine() ? '(endline)' : '(end)', ''); } t = match(tx); if (!t) { t = ''; c = ''; while (s && s < '!') { s = s.substr(1); } if (s) { errorAt("Unexpected '{a}'.", line, character, s.substr(0, 1)); s = ''; } } else { // identifier if (c.isAlpha() || c === '_' || c === '$') { return it('(identifier)', t); } // number if (c.isDigit()) { if (!isFinite(Number(t))) { warningAt("Bad number '{a}'.", line, character, t); } if (s.substr(0, 1).isAlpha()) { warningAt("Missing space after '{a}'.", line, character, t); } if (c === '0') { d = t.substr(1, 1); if (d.isDigit()) { if (token.id !== '.') { warningAt("Don't use extra leading zeros '{a}'.", line, character, t); } } else if (jsonmode && (d === 'x' || d === 'X')) { warningAt("Avoid 0x-. '{a}'.", line, character, t); } } if (t.substr(t.length - 1) === '.') { warningAt( "A trailing decimal point can be confused with a dot '{a}'.", line, character, t); } return it('(number)', t); } switch (t) { // string case '"': case "'": return string(t); // // comment case '//': s = ''; token.comment = true; break; // /* comment case '/*': for (;;) { i = s.search(lx); if (i >= 0) { break; } if (!nextLine()) { errorAt("Unclosed comment.", line, character); } } character += i + 2; if (s.substr(i, 1) === '/') { errorAt("Nested comment.", line, character); } s = s.substr(i + 2); token.comment = true; break; // /*members /*jshint /*global case '/*members': case '/*member': case '/*jshint': case '/*jslint': case '/*global': case '*/': return { value: t, type: 'special', line: line, character: character, from: from }; case '': break; // / case '/': if (token.id === '/=') { errorAt("A regular expression literal can be confused with '/='.", line, from); } if (prereg) { depth = 0; captures = 0; l = 0; for (;;) { b = true; c = s.charAt(l); l += 1; switch (c) { case '': errorAt("Unclosed regular expression.", line, from); return quit('Stopping.', line, from); case '/': if (depth > 0) { warningAt("{a} unterminated regular expression " + "group(s).", line, from + l, depth); } c = s.substr(0, l - 1); q = { g: true, i: true, m: true }; while (q[s.charAt(l)] === true) { q[s.charAt(l)] = false; l += 1; } character += l; s = s.substr(l); q = s.charAt(0); if (q === '/' || q === '*') { errorAt("Confusing regular expression.", line, from); } return it('(regexp)', c); case '\\': c = s.charAt(l); if (c < ' ') { warningAt( "Unexpected control character in regular expression.", line, from + l); } else if (c === '<') { warningAt( "Unexpected escaped character '{a}' in regular expression.", line, from + l, c); } l += 1; break; case '(': depth += 1; b = false; if (s.charAt(l) === '?') { l += 1; switch (s.charAt(l)) { case ':': case '=': case '!': l += 1; break; default: warningAt( "Expected '{a}' and instead saw '{b}'.", line, from + l, ':', s.charAt(l)); } } else { captures += 1; } break; case '|': b = false; break; case ')': if (depth === 0) { warningAt("Unescaped '{a}'.", line, from + l, ')'); } else { depth -= 1; } break; case ' ': q = 1; while (s.charAt(l) === ' ') { l += 1; q += 1; } if (q > 1) { warningAt( "Spaces are hard to count. Use {{a}}.", line, from + l, q); } break; case '[': c = s.charAt(l); if (c === '^') { l += 1; if (option.regexp) { warningAt("Insecure '{a}'.", line, from + l, c); } else if (s.charAt(l) === ']') { errorAt("Unescaped '{a}'.", line, from + l, '^'); } } if (c === ']') { warningAt("Empty class.", line, from + l - 1); } isLiteral = false; isInRange = false; klass: do { c = s.charAt(l); l += 1; switch (c) { case '[': case '^': warningAt("Unescaped '{a}'.", line, from + l, c); if (isInRange) { isInRange = false; } else { isLiteral = true; } break; case '-': if (isLiteral && !isInRange) { isLiteral = false; isInRange = true; } else if (isInRange) { isInRange = false; } else if (s.charAt(l) === ']') { isInRange = true; } else { if (option.regexdash !== (l === 2 || (l === 3 && s.charAt(1) === '^'))) { warningAt("Unescaped '{a}'.", line, from + l - 1, '-'); } isLiteral = true; } break; case ']': if (isInRange && !option.regexdash) { warningAt("Unescaped '{a}'.", line, from + l - 1, '-'); } break klass; case '\\': c = s.charAt(l); if (c < ' ') { warningAt( "Unexpected control character in regular expression.", line, from + l); } else if (c === '<') { warningAt( "Unexpected escaped character '{a}' in regular expression.", line, from + l, c); } l += 1; // \w, \s and \d are never part of a character range if (/[wsd]/i.test(c)) { if (isInRange) { warningAt("Unescaped '{a}'.", line, from + l, '-'); isInRange = false; } isLiteral = false; } else if (isInRange) { isInRange = false; } else { isLiteral = true; } break; case '/': warningAt("Unescaped '{a}'.", line, from + l - 1, '/'); if (isInRange) { isInRange = false; } else { isLiteral = true; } break; case '<': if (isInRange) { isInRange = false; } else { isLiteral = true; } break; default: if (isInRange) { isInRange = false; } else { isLiteral = true; } } } while (c); break; case '.': if (option.regexp) { warningAt("Insecure '{a}'.", line, from + l, c); } break; case ']': case '?': case '{': case '}': case '+': case '*': warningAt("Unescaped '{a}'.", line, from + l, c); } if (b) { switch (s.charAt(l)) { case '?': case '+': case '*': l += 1; if (s.charAt(l) === '?') { l += 1; } break; case '{': l += 1; c = s.charAt(l); if (c < '0' || c > '9') { warningAt( "Expected a number and instead saw '{a}'.", line, from + l, c); } l += 1; low = +c; for (;;) { c = s.charAt(l); if (c < '0' || c > '9') { break; } l += 1; low = +c + (low * 10); } high = low; if (c === ',') { l += 1; high = Infinity; c = s.charAt(l); if (c >= '0' && c <= '9') { l += 1; high = +c; for (;;) { c = s.charAt(l); if (c < '0' || c > '9') { break; } l += 1; high = +c + (high * 10); } } } if (s.charAt(l) !== '}') { warningAt( "Expected '{a}' and instead saw '{b}'.", line, from + l, '}', c); } else { l += 1; } if (s.charAt(l) === '?') { l += 1; } if (low > high) { warningAt( "'{a}' should not be greater than '{b}'.", line, from + l, low, high); } } } } c = s.substr(0, l - 1); character += l; s = s.substr(l); return it('(regexp)', c); } return it('(punctuator)', t); // punctuator case '#': return it('(punctuator)', t); default: return it('(punctuator)', t); } } } } }; }()); function addlabel(t, type) { if (t === 'hasOwnProperty') { warning("'hasOwnProperty' is a really bad name."); } // Define t in the current function in the current scope. if (is_own(funct, t) && !funct['(global)']) { if (funct[t] === true) { if (option.latedef) warning("'{a}' was used before it was defined.", nexttoken, t); } else { if (!option.shadow && type !== "exception") warning("'{a}' is already defined.", nexttoken, t); } } funct[t] = type; if (funct['(global)']) { global[t] = funct; if (is_own(implied, t)) { if (option.latedef) warning("'{a}' was used before it was defined.", nexttoken, t); delete implied[t]; } } else { scope[t] = funct; } } function doOption() { var b, obj, filter, o = nexttoken.value, t, v; switch (o) { case '*/': error("Unbegun comment."); break; case '/*members': case '/*member': o = '/*members'; if (!membersOnly) { membersOnly = {}; } obj = membersOnly; break; case '/*jshint': case '/*jslint': obj = option; filter = boolOptions; break; case '/*global': obj = predefined; break; default: error("What?"); } t = lex.token(); loop: for (;;) { for (;;) { if (t.type === 'special' && t.value === '*/') { break loop; } if (t.id !== '(endline)' && t.id !== ',') { break; } t = lex.token(); } if (t.type !== '(string)' && t.type !== '(identifier)' && o !== '/*members') { error("Bad option.", t); } v = lex.token(); if (v.id === ':') { v = lex.token(); if (obj === membersOnly) { error("Expected '{a}' and instead saw '{b}'.", t, '*/', ':'); } if (t.value === 'indent' && (o === '/*jshint' || o === '/*jslint')) { b = +v.value; if (typeof b !== 'number' || !isFinite(b) || b <= 0 || Math.floor(b) !== b) { error("Expected a small integer and instead saw '{a}'.", v, v.value); } obj.white = true; obj.indent = b; } else if (t.value === 'maxerr' && (o === '/*jshint' || o === '/*jslint')) { b = +v.value; if (typeof b !== 'number' || !isFinite(b) || b <= 0 || Math.floor(b) !== b) { error("Expected a small integer and instead saw '{a}'.", v, v.value); } obj.maxerr = b; } else if (t.value === 'maxlen' && (o === '/*jshint' || o === '/*jslint')) { b = +v.value; if (typeof b !== 'number' || !isFinite(b) || b <= 0 || Math.floor(b) !== b) { error("Expected a small integer and instead saw '{a}'.", v, v.value); } obj.maxlen = b; } else if (t.value === 'validthis') { if (funct['(global)']) { error("Option 'validthis' can't be used in a global scope."); } else { if (v.value === 'true' || v.value === 'false') obj[t.value] = v.value === 'true'; else error("Bad option value.", v); } } else if (v.value === 'true') { obj[t.value] = true; } else if (v.value === 'false') { obj[t.value] = false; } else { error("Bad option value.", v); } t = lex.token(); } else { if (o === '/*jshint' || o === '/*jslint') { error("Missing option value.", t); } obj[t.value] = false; t = v; } } if (filter) { assume(); } } // We need a peek function. If it has an argument, it peeks that much farther // ahead. It is used to distinguish // for ( var i in ... // from // for ( var i = ... function peek(p) { var i = p || 0, j = 0, t; while (j <= i) { t = lookahead[j]; if (!t) { t = lookahead[j] = lex.token(); } j += 1; } return t; } // Produce the next token. It looks for programming errors. function advance(id, t) { switch (token.id) { case '(number)': if (nexttoken.id === '.') { warning("A dot following a number can be confused with a decimal point.", token); } break; case '-': if (nexttoken.id === '-' || nexttoken.id === '--') { warning("Confusing minusses."); } break; case '+': if (nexttoken.id === '+' || nexttoken.id === '++') { warning("Confusing plusses."); } break; } if (token.type === '(string)' || token.identifier) { anonname = token.value; } if (id && nexttoken.id !== id) { if (t) { if (nexttoken.id === '(end)') { warning("Unmatched '{a}'.", t, t.id); } else { warning("Expected '{a}' to match '{b}' from line {c} and instead saw '{d}'.", nexttoken, id, t.id, t.line, nexttoken.value); } } else if (nexttoken.type !== '(identifier)' || nexttoken.value !== id) { warning("Expected '{a}' and instead saw '{b}'.", nexttoken, id, nexttoken.value); } } prevtoken = token; token = nexttoken; for (;;) { nexttoken = lookahead.shift() || lex.token(); if (nexttoken.id === '(end)' || nexttoken.id === '(error)') { return; } if (nexttoken.type === 'special') { doOption(); } else { if (nexttoken.id !== '(endline)') { break; } } } } // This is the heart of JSHINT, the Pratt parser. In addition to parsing, it // is looking for ad hoc lint patterns. We add .fud to Pratt's model, which is // like .nud except that it is only used on the first token of a statement. // Having .fud makes it much easier to define statement-oriented languages like // JavaScript. I retained Pratt's nomenclature. // .nud Null denotation // .fud First null denotation // .led Left denotation // lbp Left binding power // rbp Right binding power // They are elements of the parsing method called Top Down Operator Precedence. function expression(rbp, initial) { var left, isArray = false; if (nexttoken.id === '(end)') error("Unexpected early end of program.", token); advance(); if (initial) { anonname = 'anonymous'; funct['(verb)'] = token.value; } if (initial === true && token.fud) { left = token.fud(); } else { if (token.nud) { left = token.nud(); } else { if (nexttoken.type === '(number)' && token.id === '.') { warning("A leading decimal point can be confused with a dot: '.{a}'.", token, nexttoken.value); advance(); return token; } else { error("Expected an identifier and instead saw '{a}'.", token, token.id); } } while (rbp < nexttoken.lbp) { isArray = token.value === 'Array'; advance(); if (isArray && token.id === '(' && nexttoken.id === ')') warning("Use the array literal notation [].", token); if (token.led) { left = token.led(left); } else { error("Expected an operator and instead saw '{a}'.", token, token.id); } } } return left; } // Functions for conformance of style. function adjacent(left, right) { left = left || token; right = right || nexttoken; if (option.white) { if (left.character !== right.from && left.line === right.line) { left.from += (left.character - left.from); warning("Unexpected space after '{a}'.", left, left.value); } } } function nobreak(left, right) { left = left || token; right = right || nexttoken; if (option.white && (left.character !== right.from || left.line !== right.line)) { warning("Unexpected space before '{a}'.", right, right.value); } } function nospace(left, right) { left = left || token; right = right || nexttoken; if (option.white && !left.comment) { if (left.line === right.line) { adjacent(left, right); } } } function nonadjacent(left, right) { if (option.white) { left = left || token; right = right || nexttoken; if (left.line === right.line && left.character === right.from) { left.from += (left.character - left.from); warning("Missing space after '{a}'.", left, left.value); } } } function nobreaknonadjacent(left, right) { left = left || token; right = right || nexttoken; if (!option.laxbreak && left.line !== right.line) { warning("Bad line breaking before '{a}'.", right, right.id); } else if (option.white) { left = left || token; right = right || nexttoken; if (left.character === right.from) { left.from += (left.character - left.from); warning("Missing space after '{a}'.", left, left.value); } } } function indentation(bias) { var i; if (option.white && nexttoken.id !== '(end)') { i = indent + (bias || 0); if (nexttoken.from !== i) { warning( "Expected '{a}' to have an indentation at {b} instead at {c}.", nexttoken, nexttoken.value, i, nexttoken.from); } } } function nolinebreak(t) { t = t || token; if (t.line !== nexttoken.line) { warning("Line breaking error '{a}'.", t, t.value); } } function comma() { if (token.line !== nexttoken.line) { if (!option.laxcomma) { if (comma.first) { warning("Comma warnings can be turned off with 'laxcomma'"); comma.first = false; } warning("Bad line breaking before '{a}'.", token, nexttoken.id); } } else if (!token.comment && token.character !== nexttoken.from && option.white) { token.from += (token.character - token.from); warning("Unexpected space after '{a}'.", token, token.value); } advance(','); nonadjacent(token, nexttoken); } comma.first = true; // Functional constructors for making the symbols that will be inherited by // tokens. function symbol(s, p) { var x = syntax[s]; if (!x || typeof x !== 'object') { syntax[s] = x = { id: s, lbp: p, value: s }; } return x; } function delim(s) { return symbol(s, 0); } function stmt(s, f) { var x = delim(s); x.identifier = x.reserved = true; x.fud = f; return x; } function blockstmt(s, f) { var x = stmt(s, f); x.block = true; return x; } function reserveName(x) { var c = x.id.charAt(0); if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')) { x.identifier = x.reserved = true; } return x; } function prefix(s, f) { var x = symbol(s, 150); reserveName(x); x.nud = (typeof f === 'function') ? f : function () { this.right = expression(150); this.arity = 'unary'; if (this.id === '++' || this.id === '--') { if (option.plusplus) { warning("Unexpected use of '{a}'.", this, this.id); } else if ((!this.right.identifier || this.right.reserved) && this.right.id !== '.' && this.right.id !== '[') { warning("Bad operand.", this); } } return this; }; return x; } function type(s, f) { var x = delim(s); x.type = s; x.nud = f; return x; } function reserve(s, f) { var x = type(s, f); x.identifier = x.reserved = true; return x; } function reservevar(s, v) { return reserve(s, function () { if (typeof v === 'function') { v(this); } return this; }); } function infix(s, f, p, w) { var x = symbol(s, p); reserveName(x); x.led = function (left) { if (!w) { nobreaknonadjacent(prevtoken, token); nonadjacent(token, nexttoken); } if (s === "in" && left.id === "!") { warning("Confusing use of '{a}'.", left, '!'); } if (typeof f === 'function') { return f(left, this); } else { this.left = left; this.right = expression(p); return this; } }; return x; } function relation(s, f) { var x = symbol(s, 100); x.led = function (left) { nobreaknonadjacent(prevtoken, token); nonadjacent(token, nexttoken); var right = expression(100); if ((left && left.id === 'NaN') || (right && right.id === 'NaN')) { warning("Use the isNaN function to compare with NaN.", this); } else if (f) { f.apply(this, [left, right]); } if (left.id === '!') { warning("Confusing use of '{a}'.", left, '!'); } if (right.id === '!') { warning("Confusing use of '{a}'.", right, '!'); } this.left = left; this.right = right; return this; }; return x; } function isPoorRelation(node) { return node && ((node.type === '(number)' && +node.value === 0) || (node.type === '(string)' && node.value === '') || (node.type === 'null' && !option.eqnull) || node.type === 'true' || node.type === 'false' || node.type === 'undefined'); } function assignop(s, f) { symbol(s, 20).exps = true; return infix(s, function (left, that) { var l; that.left = left; if (predefined[left.value] === false && scope[left.value]['(global)'] === true) { warning("Read only.", left); } else if (left['function']) { warning("'{a}' is a function.", left, left.value); } if (left) { if (option.esnext && funct[left.value] === 'const') { warning("Attempting to override '{a}' which is a constant", left, left.value); } if (left.id === '.' || left.id === '[') { if (!left.left || left.left.value === 'arguments') { warning('Bad assignment.', that); } that.right = expression(19); return that; } else if (left.identifier && !left.reserved) { if (funct[left.value] === 'exception') { warning("Do not assign to the exception parameter.", left); } that.right = expression(19); return that; } if (left === syntax['function']) { warning( "Expected an identifier in an assignment and instead saw a function invocation.", token); } } error("Bad assignment.", that); }, 20); } function bitwise(s, f, p) { var x = symbol(s, p); reserveName(x); x.led = (typeof f === 'function') ? f : function (left) { if (option.bitwise) { warning("Unexpected use of '{a}'.", this, this.id); } this.left = left; this.right = expression(p); return this; }; return x; } function bitwiseassignop(s) { symbol(s, 20).exps = true; return infix(s, function (left, that) { if (option.bitwise) { warning("Unexpected use of '{a}'.", that, that.id); } nonadjacent(prevtoken, token); nonadjacent(token, nexttoken); if (left) { if (left.id === '.' || left.id === '[' || (left.identifier && !left.reserved)) { expression(19); return that; } if (left === syntax['function']) { warning( "Expected an identifier in an assignment, and instead saw a function invocation.", token); } return that; } error("Bad assignment.", that); }, 20); } function suffix(s, f) { var x = symbol(s, 150); x.led = function (left) { if (option.plusplus) { warning("Unexpected use of '{a}'.", this, this.id); } else if ((!left.identifier || left.reserved) && left.id !== '.' && left.id !== '[') { warning("Bad operand.", this); } this.left = left; return this; }; return x; } // fnparam means that this identifier is being defined as a function // argument (see identifier()) function optionalidentifier(fnparam) { if (nexttoken.identifier) { advance(); if (token.reserved && !option.es5) { // `undefined` as a function param is a common pattern to protect // against the case when somebody does `undefined = true` and // help with minification. More info: https://gist.github.com/315916 if (!fnparam || token.value !== 'undefined') { warning("Expected an identifier and instead saw '{a}' (a reserved word).", token, token.id); } } return token.value; } } // fnparam means that this identifier is being defined as a function // argument function identifier(fnparam) { var i = optionalidentifier(fnparam); if (i) { return i; } if (token.id === 'function' && nexttoken.id === '(') { warning("Missing name in function declaration."); } else { error("Expected an identifier and instead saw '{a}'.", nexttoken, nexttoken.value); } } function reachable(s) { var i = 0, t; if (nexttoken.id !== ';' || noreach) { return; } for (;;) { t = peek(i); if (t.reach) { return; } if (t.id !== '(endline)') { if (t.id === 'function') { if (!option.latedef) { break; } warning( "Inner functions should be listed at the top of the outer function.", t); break; } warning("Unreachable '{a}' after '{b}'.", t, t.value, s); break; } i += 1; } } function statement(noindent) { var i = indent, r, s = scope, t = nexttoken; if (t.id === ";") { advance(";"); return; } // Is this a labelled statement? if (t.identifier && !t.reserved && peek().id === ':') { advance(); advance(':'); scope = Object.create(s); addlabel(t.value, 'label'); if (!nexttoken.labelled) { warning("Label '{a}' on {b} statement.", nexttoken, t.value, nexttoken.value); } if (jx.test(t.value + ':')) { warning("Label '{a}' looks like a javascript url.", t, t.value); } nexttoken.label = t.value; t = nexttoken; } // Parse the statement. if (!noindent) { indentation(); } r = expression(0, true); // Look for the final semicolon. if (!t.block) { if (!option.expr && (!r || !r.exps)) { warning("Expected an assignment or function call and instead saw an expression.", token); } else if (option.nonew && r.id === '(' && r.left.id === 'new') { warning("Do not use 'new' for side effects."); } if (nexttoken.id !== ';') { if (!option.asi) { // If this is the last statement in a block that ends on // the same line *and* option lastsemic is on, ignore the warning. // Otherwise, complain about missing semicolon. if (!option.lastsemic || nexttoken.id !== '}' || nexttoken.line !== token.line) { warningAt("Missing semicolon.", token.line, token.character); } } } else { adjacent(token, nexttoken); advance(';'); nonadjacent(token, nexttoken); } } // Restore the indentation. indent = i; scope = s; return r; } function statements(startLine) { var a = [], f, p; while (!nexttoken.reach && nexttoken.id !== '(end)') { if (nexttoken.id === ';') { p = peek(); if (!p || p.id !== "(") { warning("Unnecessary semicolon."); } advance(';'); } else { a.push(statement(startLine === nexttoken.line)); } } return a; } /* * read all directives * recognizes a simple form of asi, but always * warns, if it is used */ function directives() { var i, p, pn; for (;;) { if (nexttoken.id === "(string)") { p = peek(0); if (p.id === "(endline)") { i = 1; do { pn = peek(i); i = i + 1; } while (pn.id === "(endline)"); if (pn.id !== ";") { if (pn.id !== "(string)" && pn.id !== "(number)" && pn.id !== "(regexp)" && pn.identifier !== true && pn.id !== "}") { break; } warning("Missing semicolon.", nexttoken); } else { p = pn; } } else if (p.id === "}") { // directive with no other statements, warn about missing semicolon warning("Missing semicolon.", p); } else if (p.id !== ";") { break; } indentation(); advance(); if (directive[token.value]) { warning("Unnecessary directive \"{a}\".", token, token.value); } if (token.value === "use strict") { option.newcap = true; option.undef = true; } // there's no directive negation, so always set to true directive[token.value] = true; if (p.id === ";") { advance(";"); } continue; } break; } } /* * Parses a single block. A block is a sequence of statements wrapped in * braces. * * ordinary - true for everything but function bodies and try blocks. * stmt - true if block can be a single statement (e.g. in if/for/while). * isfunc - true if block is a function body */ function block(ordinary, stmt, isfunc) { var a, b = inblock, old_indent = indent, m, s = scope, t, line, d; inblock = ordinary; if (!ordinary || !option.funcscope) scope = Object.create(scope); nonadjacent(token, nexttoken); t = nexttoken; if (nexttoken.id === '{') { advance('{'); line = token.line; if (nexttoken.id !== '}') { indent += option.indent; while (!ordinary && nexttoken.from > indent) { indent += option.indent; } if (isfunc) { m = {}; for (d in directive) { if (is_own(directive, d)) { m[d] = directive[d]; } } directives(); if (option.strict && funct['(context)']['(global)']) { if (!m["use strict"] && !directive["use strict"]) { warning("Missing \"use strict\" statement."); } } } a = statements(line); if (isfunc) { directive = m; } indent -= option.indent; if (line !== nexttoken.line) { indentation(); } } else if (line !== nexttoken.line) { indentation(); } advance('}', t); indent = old_indent; } else if (!ordinary) { error("Expected '{a}' and instead saw '{b}'.", nexttoken, '{', nexttoken.value); } else { if (!stmt || option.curly) warning("Expected '{a}' and instead saw '{b}'.", nexttoken, '{', nexttoken.value); noreach = true; indent += option.indent; // test indentation only if statement is in new line a = [statement(nexttoken.line === token.line)]; indent -= option.indent; noreach = false; } funct['(verb)'] = null; if (!ordinary || !option.funcscope) scope = s; inblock = b; if (ordinary && option.noempty && (!a || a.length === 0)) { warning("Empty block."); } return a; } function countMember(m) { if (membersOnly && typeof membersOnly[m] !== 'boolean') { warning("Unexpected /*member '{a}'.", token, m); } if (typeof member[m] === 'number') { member[m] += 1; } else { member[m] = 1; } } function note_implied(token) { var name = token.value, line = token.line, a = implied[name]; if (typeof a === 'function') { a = false; } if (!a) { a = [line]; implied[name] = a; } else if (a[a.length - 1] !== line) { a.push(line); } } // Build the syntax table by declaring the syntactic elements of the language. type('(number)', function () { return this; }); type('(string)', function () { return this; }); syntax['(identifier)'] = { type: '(identifier)', lbp: 0, identifier: true, nud: function () { var v = this.value, s = scope[v], f; if (typeof s === 'function') { // Protection against accidental inheritance. s = undefined; } else if (typeof s === 'boolean') { f = funct; funct = functions[0]; addlabel(v, 'var'); s = funct; funct = f; } // The name is in scope and defined in the current function. if (funct === s) { // Change 'unused' to 'var', and reject labels. switch (funct[v]) { case 'unused': funct[v] = 'var'; break; case 'unction': funct[v] = 'function'; this['function'] = true; break; case 'function': this['function'] = true; break; case 'label': warning("'{a}' is a statement label.", token, v); break; } } else if (funct['(global)']) { // The name is not defined in the function. If we are in the global // scope, then we have an undefined variable. // // Operators typeof and delete do not raise runtime errors even if // the base object of a reference is null so no need to display warning // if we're inside of typeof or delete. if (option.undef && typeof predefined[v] !== 'boolean') { // Attempting to subscript a null reference will throw an // error, even within the typeof and delete operators if (!(anonname === 'typeof' || anonname === 'delete') || (nexttoken && (nexttoken.value === '.' || nexttoken.value === '['))) { isundef(funct, "'{a}' is not defined.", token, v); } } note_implied(token); } else { // If the name is already defined in the current // function, but not as outer, then there is a scope error. switch (funct[v]) { case 'closure': case 'function': case 'var': case 'unused': warning("'{a}' used out of scope.", token, v); break; case 'label': warning("'{a}' is a statement label.", token, v); break; case 'outer': case 'global': break; default: // If the name is defined in an outer function, make an outer entry, // and if it was unused, make it var. if (s === true) { funct[v] = true; } else if (s === null) { warning("'{a}' is not allowed.", token, v); note_implied(token); } else if (typeof s !== 'object') { // Operators typeof and delete do not raise runtime errors even // if the base object of a reference is null so no need to // display warning if we're inside of typeof or delete. if (option.undef) { // Attempting to subscript a null reference will throw an // error, even within the typeof and delete operators if (!(anonname === 'typeof' || anonname === 'delete') || (nexttoken && (nexttoken.value === '.' || nexttoken.value === '['))) { isundef(funct, "'{a}' is not defined.", token, v); } } funct[v] = true; note_implied(token); } else { switch (s[v]) { case 'function': case 'unction': this['function'] = true; s[v] = 'closure'; funct[v] = s['(global)'] ? 'global' : 'outer'; break; case 'var': case 'unused': s[v] = 'closure'; funct[v] = s['(global)'] ? 'global' : 'outer'; break; case 'closure': case 'parameter': funct[v] = s['(global)'] ? 'global' : 'outer'; break; case 'label': warning("'{a}' is a statement label.", token, v); } } } } return this; }, led: function () { error("Expected an operator and instead saw '{a}'.", nexttoken, nexttoken.value); } }; type('(regexp)', function () { return this; }); // ECMAScript parser delim('(endline)'); delim('(begin)'); delim('(end)').reach = true; delim('</').reach = true; delim('<!'); delim('<!--'); delim('-->'); delim('(error)').reach = true; delim('}').reach = true; delim(')'); delim(']'); delim('"').reach = true; delim("'").reach = true; delim(';'); delim(':').reach = true; delim(','); delim('#'); delim('@'); reserve('else'); reserve('case').reach = true; reserve('catch'); reserve('default').reach = true; reserve('finally'); reservevar('arguments', function (x) { if (directive['use strict'] && funct['(global)']) { warning("Strict violation.", x); } }); reservevar('eval'); reservevar('false'); reservevar('Infinity'); reservevar('NaN'); reservevar('null'); reservevar('this', function (x) { if (directive['use strict'] && !option.validthis && ((funct['(statement)'] && funct['(name)'].charAt(0) > 'Z') || funct['(global)'])) { warning("Possible strict violation.", x); } }); reservevar('true'); reservevar('undefined'); assignop('=', 'assign', 20); assignop('+=', 'assignadd', 20); assignop('-=', 'assignsub', 20); assignop('*=', 'assignmult', 20); assignop('/=', 'assigndiv', 20).nud = function () { error("A regular expression literal can be confused with '/='."); }; assignop('%=', 'assignmod', 20); bitwiseassignop('&=', 'assignbitand', 20); bitwiseassignop('|=', 'assignbitor', 20); bitwiseassignop('^=', 'assignbitxor', 20); bitwiseassignop('<<=', 'assignshiftleft', 20); bitwiseassignop('>>=', 'assignshiftright', 20); bitwiseassignop('>>>=', 'assignshiftrightunsigned', 20); infix('?', function (left, that) { that.left = left; that.right = expression(10); advance(':'); that['else'] = expression(10); return that; }, 30); infix('||', 'or', 40); infix('&&', 'and', 50); bitwise('|', 'bitor', 70); bitwise('^', 'bitxor', 80); bitwise('&', 'bitand', 90); relation('==', function (left, right) { var eqnull = option.eqnull && (left.value === 'null' || right.value === 'null'); if (!eqnull && option.eqeqeq) warning("Expected '{a}' and instead saw '{b}'.", this, '===', '=='); else if (isPoorRelation(left)) warning("Use '{a}' to compare with '{b}'.", this, '===', left.value); else if (isPoorRelation(right)) warning("Use '{a}' to compare with '{b}'.", this, '===', right.value); return this; }); relation('==='); relation('!=', function (left, right) { var eqnull = option.eqnull && (left.value === 'null' || right.value === 'null'); if (!eqnull && option.eqeqeq) { warning("Expected '{a}' and instead saw '{b}'.", this, '!==', '!='); } else if (isPoorRelation(left)) { warning("Use '{a}' to compare with '{b}'.", this, '!==', left.value); } else if (isPoorRelation(right)) { warning("Use '{a}' to compare with '{b}'.", this, '!==', right.value); } return this; }); relation('!=='); relation('<'); relation('>'); relation('<='); relation('>='); bitwise('<<', 'shiftleft', 120); bitwise('>>', 'shiftright', 120); bitwise('>>>', 'shiftrightunsigned', 120); infix('in', 'in', 120); infix('instanceof', 'instanceof', 120); infix('+', function (left, that) { var right = expression(130); if (left && right && left.id === '(string)' && right.id === '(string)') { left.value += right.value; left.character = right.character; if (!option.scripturl && jx.test(left.value)) { warning("JavaScript URL.", left); } return left; } that.left = left; that.right = right; return that; }, 130); prefix('+', 'num'); prefix('+++', function () { warning("Confusing pluses."); this.right = expression(150); this.arity = 'unary'; return this; }); infix('+++', function (left) { warning("Confusing pluses."); this.left = left; this.right = expression(130); return this; }, 130); infix('-', 'sub', 130); prefix('-', 'neg'); prefix('---', function () { warning("Confusing minuses."); this.right = expression(150); this.arity = 'unary'; return this; }); infix('---', function (left) { warning("Confusing minuses."); this.left = left; this.right = expression(130); return this; }, 130); infix('*', 'mult', 140); infix('/', 'div', 140); infix('%', 'mod', 140); suffix('++', 'postinc'); prefix('++', 'preinc'); syntax['++'].exps = true; suffix('--', 'postdec'); prefix('--', 'predec'); syntax['--'].exps = true; prefix('delete', function () { var p = expression(0); if (!p || (p.id !== '.' && p.id !== '[')) { warning("Variables should not be deleted."); } this.first = p; return this; }).exps = true; prefix('~', function () { if (option.bitwise) { warning("Unexpected '{a}'.", this, '~'); } expression(150); return this; }); prefix('!', function () { this.right = expression(150); this.arity = 'unary'; if (bang[this.right.id] === true) { warning("Confusing use of '{a}'.", this, '!'); } return this; }); prefix('typeof', 'typeof'); prefix('new', function () { var c = expression(155), i; if (c && c.id !== 'function') { if (c.identifier) { c['new'] = true; switch (c.value) { case 'Object': warning("Use the object literal notation {}.", token); break; case 'Number': case 'String': case 'Boolean': case 'Math': case 'JSON': warning("Do not use {a} as a constructor.", token, c.value); break; case 'Function': if (!option.evil) { warning("The Function constructor is eval."); } break; case 'Date': case 'RegExp': break; default: if (c.id !== 'function') { i = c.value.substr(0, 1); if (option.newcap && (i < 'A' || i > 'Z')) { warning("A constructor name should start with an uppercase letter.", token); } } } } else { if (c.id !== '.' && c.id !== '[' && c.id !== '(') { warning("Bad constructor.", token); } } } else { if (!option.supernew) warning("Weird construction. Delete 'new'.", this); } adjacent(token, nexttoken); if (nexttoken.id !== '(' && !option.supernew) { warning("Missing '()' invoking a constructor."); } this.first = c; return this; }); syntax['new'].exps = true; prefix('void').exps = true; infix('.', function (left, that) { adjacent(prevtoken, token); nobreak(); var m = identifier(); if (typeof m === 'string') { countMember(m); } that.left = left; that.right = m; if (left && left.value === 'arguments' && (m === 'callee' || m === 'caller')) { if (option.noarg) warning("Avoid arguments.{a}.", left, m); else if (directive['use strict']) error('Strict violation.'); } else if (!option.evil && left && left.value === 'document' && (m === 'write' || m === 'writeln')) { warning("document.write can be a form of eval.", left); } if (!option.evil && (m === 'eval' || m === 'execScript')) { warning('eval is evil.'); } return that; }, 160, true); infix('(', function (left, that) { if (prevtoken.id !== '}' && prevtoken.id !== ')') { nobreak(prevtoken, token); } nospace(); if (option.immed && !left.immed && left.id === 'function') { warning("Wrap an immediate function invocation in parentheses " + "to assist the reader in understanding that the expression " + "is the result of a function, and not the function itself."); } var n = 0, p = []; if (left) { if (left.type === '(identifier)') { if (left.value.match(/^[A-Z]([A-Z0-9_$]*[a-z][A-Za-z0-9_$]*)?$/)) { if (left.value !== 'Number' && left.value !== 'String' && left.value !== 'Boolean' && left.value !== 'Date') { if (left.value === 'Math') { warning("Math is not a function.", left); } else if (option.newcap) { warning( "Missing 'new' prefix when invoking a constructor.", left); } } } } } if (nexttoken.id !== ')') { for (;;) { p[p.length] = expression(10); n += 1; if (nexttoken.id !== ',') { break; } comma(); } } advance(')'); nospace(prevtoken, token); if (typeof left === 'object') { if (left.value === 'parseInt' && n === 1) { warning("Missing radix parameter.", left); } if (!option.evil) { if (left.value === 'eval' || left.value === 'Function' || left.value === 'execScript') { warning("eval is evil.", left); } else if (p[0] && p[0].id === '(string)' && (left.value === 'setTimeout' || left.value === 'setInterval')) { warning( "Implied eval is evil. Pass a function instead of a string.", left); } } if (!left.identifier && left.id !== '.' && left.id !== '[' && left.id !== '(' && left.id !== '&&' && left.id !== '||' && left.id !== '?') { warning("Bad invocation.", left); } } that.left = left; return that; }, 155, true).exps = true; prefix('(', function () { nospace(); if (nexttoken.id === 'function') { nexttoken.immed = true; } var v = expression(0); advance(')', this); nospace(prevtoken, token); if (option.immed && v.id === 'function') { if (nexttoken.id === '(' || (nexttoken.id === '.' && (peek().value === 'call' || peek().value === 'apply'))) { warning( "Move the invocation into the parens that contain the function.", nexttoken); } else { warning( "Do not wrap function literals in parens unless they are to be immediately invoked.", this); } } return v; }); infix('[', function (left, that) { nobreak(prevtoken, token); nospace(); var e = expression(0), s; if (e && e.type === '(string)') { if (!option.evil && (e.value === 'eval' || e.value === 'execScript')) { warning("eval is evil.", that); } countMember(e.value); if (!option.sub && ix.test(e.value)) { s = syntax[e.value]; if (!s || !s.reserved) { warning("['{a}'] is better written in dot notation.", e, e.value); } } } advance(']', that); nospace(prevtoken, token); that.left = left; that.right = e; return that; }, 160, true); prefix('[', function () { var b = token.line !== nexttoken.line; this.first = []; if (b) { indent += option.indent; if (nexttoken.from === indent + option.indent) { indent += option.indent; } } while (nexttoken.id !== '(end)') { while (nexttoken.id === ',') { warning("Extra comma."); advance(','); } if (nexttoken.id === ']') { break; } if (b && token.line !== nexttoken.line) { indentation(); } this.first.push(expression(10)); if (nexttoken.id === ',') { comma(); if (nexttoken.id === ']' && !option.es5) { warning("Extra comma.", token); break; } } else { break; } } if (b) { indent -= option.indent; indentation(); } advance(']', this); return this; }, 160); function property_name() { var id = optionalidentifier(true); if (!id) { if (nexttoken.id === '(string)') { id = nexttoken.value; advance(); } else if (nexttoken.id === '(number)') { id = nexttoken.value.toString(); advance(); } } return id; } function functionparams() { var i, t = nexttoken, p = []; advance('('); nospace(); if (nexttoken.id === ')') { advance(')'); return; } for (;;) { i = identifier(true); p.push(i); addlabel(i, 'parameter'); if (nexttoken.id === ',') { comma(); } else { advance(')', t); nospace(prevtoken, token); return p; } } } function doFunction(i, statement) { var f, oldOption = option, oldScope = scope; option = Object.create(option); scope = Object.create(scope); funct = { '(name)' : i || '"' + anonname + '"', '(line)' : nexttoken.line, '(context)' : funct, '(breakage)' : 0, '(loopage)' : 0, '(scope)' : scope, '(statement)': statement }; f = funct; token.funct = funct; functions.push(funct); if (i) { addlabel(i, 'function'); } funct['(params)'] = functionparams(); block(false, false, true); scope = oldScope; option = oldOption; funct['(last)'] = token.line; funct = funct['(context)']; return f; } (function (x) { x.nud = function () { var b, f, i, j, p, t; var props = {}; // All properties, including accessors function saveProperty(name, token) { if (props[name] && is_own(props, name)) warning("Duplicate member '{a}'.", nexttoken, i); else props[name] = {}; props[name].basic = true; props[name].basicToken = token; } function saveSetter(name, token) { if (props[name] && is_own(props, name)) { if (props[name].basic || props[name].setter) warning("Duplicate member '{a}'.", nexttoken, i); } else { props[name] = {}; } props[name].setter = true; props[name].setterToken = token; } function saveGetter(name) { if (props[name] && is_own(props, name)) { if (props[name].basic || props[name].getter) warning("Duplicate member '{a}'.", nexttoken, i); } else { props[name] = {}; } props[name].getter = true; props[name].getterToken = token; } b = token.line !== nexttoken.line; if (b) { indent += option.indent; if (nexttoken.from === indent + option.indent) { indent += option.indent; } } for (;;) { if (nexttoken.id === '}') { break; } if (b) { indentation(); } if (nexttoken.value === 'get' && peek().id !== ':') { advance('get'); if (!option.es5) { error("get/set are ES5 features."); } i = property_name(); if (!i) { error("Missing property name."); } saveGetter(i); t = nexttoken; adjacent(token, nexttoken); f = doFunction(); p = f['(params)']; if (p) { warning("Unexpected parameter '{a}' in get {b} function.", t, p[0], i); } adjacent(token, nexttoken); } else if (nexttoken.value === 'set' && peek().id !== ':') { advance('set'); if (!option.es5) { error("get/set are ES5 features."); } i = property_name(); if (!i) { error("Missing property name."); } saveSetter(i, nexttoken); t = nexttoken; adjacent(token, nexttoken); f = doFunction(); p = f['(params)']; if (!p || p.length !== 1) { warning("Expected a single parameter in set {a} function.", t, i); } } else { i = property_name(); saveProperty(i, nexttoken); if (typeof i !== 'string') { break; } advance(':'); nonadjacent(token, nexttoken); expression(10); } countMember(i); if (nexttoken.id === ',') { comma(); if (nexttoken.id === ',') { warning("Extra comma.", token); } else if (nexttoken.id === '}' && !option.es5) { warning("Extra comma.", token); } } else { break; } } if (b) { indent -= option.indent; indentation(); } advance('}', this); // Check for lonely setters if in the ES5 mode. if (option.es5) { for (var name in props) { if (is_own(props, name) && props[name].setter && !props[name].getter) { warning("Setter is defined without getter.", props[name].setterToken); } } } return this; }; x.fud = function () { error("Expected to see a statement and instead saw a block.", token); }; }(delim('{'))); // This Function is called when esnext option is set to true // it adds the `const` statement to JSHINT useESNextSyntax = function () { var conststatement = stmt('const', function (prefix) { var id, name, value; this.first = []; for (;;) { nonadjacent(token, nexttoken); id = identifier(); if (funct[id] === "const") { warning("const '" + id + "' has already been declared"); } if (funct['(global)'] && predefined[id] === false) { warning("Redefinition of '{a}'.", token, id); } addlabel(id, 'const'); if (prefix) { break; } name = token; this.first.push(token); if (nexttoken.id !== "=") { warning("const " + "'{a}' is initialized to 'undefined'.", token, id); } if (nexttoken.id === '=') { nonadjacent(token, nexttoken); advance('='); nonadjacent(token, nexttoken); if (nexttoken.id === 'undefined') { warning("It is not necessary to initialize " + "'{a}' to 'undefined'.", token, id); } if (peek(0).id === '=' && nexttoken.identifier) { error("Constant {a} was not declared correctly.", nexttoken, nexttoken.value); } value = expression(0); name.first = value; } if (nexttoken.id !== ',') { break; } comma(); } return this; }); conststatement.exps = true; }; var varstatement = stmt('var', function (prefix) { // JavaScript does not have block scope. It only has function scope. So, // declaring a variable in a block can have unexpected consequences. var id, name, value; if (funct['(onevar)'] && option.onevar) { warning("Too many var statements."); } else if (!funct['(global)']) { funct['(onevar)'] = true; } this.first = []; for (;;) { nonadjacent(token, nexttoken); id = identifier(); if (option.esnext && funct[id] === "const") { warning("const '" + id + "' has already been declared"); } if (funct['(global)'] && predefined[id] === false) { warning("Redefinition of '{a}'.", token, id); } addlabel(id, 'unused'); if (prefix) { break; } name = token; this.first.push(token); if (nexttoken.id === '=') { nonadjacent(token, nexttoken); advance('='); nonadjacent(token, nexttoken); if (nexttoken.id === 'undefined') { warning("It is not necessary to initialize '{a}' to 'undefined'.", token, id); } if (peek(0).id === '=' && nexttoken.identifier) { error("Variable {a} was not declared correctly.", nexttoken, nexttoken.value); } value = expression(0); name.first = value; } if (nexttoken.id !== ',') { break; } comma(); } return this; }); varstatement.exps = true; blockstmt('function', function () { if (inblock) { warning("Function declarations should not be placed in blocks. " + "Use a function expression or move the statement to the top of " + "the outer function.", token); } var i = identifier(); if (option.esnext && funct[i] === "const") { warning("const '" + i + "' has already been declared"); } adjacent(token, nexttoken); addlabel(i, 'unction'); doFunction(i, true); if (nexttoken.id === '(' && nexttoken.line === token.line) { error( "Function declarations are not invocable. Wrap the whole function invocation in parens."); } return this; }); prefix('function', function () { var i = optionalidentifier(); if (i) { adjacent(token, nexttoken); } else { nonadjacent(token, nexttoken); } doFunction(i); if (!option.loopfunc && funct['(loopage)']) { warning("Don't make functions within a loop."); } return this; }); blockstmt('if', function () { var t = nexttoken; advance('('); nonadjacent(this, t); nospace(); expression(20); if (nexttoken.id === '=') { if (!option.boss) warning("Expected a conditional expression and instead saw an assignment."); advance('='); expression(20); } advance(')', t); nospace(prevtoken, token); block(true, true); if (nexttoken.id === 'else') { nonadjacent(token, nexttoken); advance('else'); if (nexttoken.id === 'if' || nexttoken.id === 'switch') { statement(true); } else { block(true, true); } } return this; }); blockstmt('try', function () { var b, e, s; block(false); if (nexttoken.id === 'catch') { advance('catch'); nonadjacent(token, nexttoken); advance('('); s = scope; scope = Object.create(s); e = nexttoken.value; if (nexttoken.type !== '(identifier)') { warning("Expected an identifier and instead saw '{a}'.", nexttoken, e); } else { addlabel(e, 'exception'); } advance(); advance(')'); block(false); b = true; scope = s; } if (nexttoken.id === 'finally') { advance('finally'); block(false); return; } else if (!b) { error("Expected '{a}' and instead saw '{b}'.", nexttoken, 'catch', nexttoken.value); } return this; }); blockstmt('while', function () { var t = nexttoken; funct['(breakage)'] += 1; funct['(loopage)'] += 1; advance('('); nonadjacent(this, t); nospace(); expression(20); if (nexttoken.id === '=') { if (!option.boss) warning("Expected a conditional expression and instead saw an assignment."); advance('='); expression(20); } advance(')', t); nospace(prevtoken, token); block(true, true); funct['(breakage)'] -= 1; funct['(loopage)'] -= 1; return this; }).labelled = true; reserve('with'); blockstmt('switch', function () { var t = nexttoken, g = false; funct['(breakage)'] += 1; advance('('); nonadjacent(this, t); nospace(); this.condition = expression(20); advance(')', t); nospace(prevtoken, token); nonadjacent(token, nexttoken); t = nexttoken; advance('{'); nonadjacent(token, nexttoken); indent += option.indent; this.cases = []; for (;;) { switch (nexttoken.id) { case 'case': switch (funct['(verb)']) { case 'break': case 'case': case 'continue': case 'return': case 'switch': case 'throw': break; default: // You can tell JSHint that you don't use break intentionally by // adding a comment /* falls through */ on a line just before // the next `case`. if (!ft.test(lines[nexttoken.line - 2])) { warning( "Expected a 'break' statement before 'case'.", token); } } indentation(-option.indent); advance('case'); this.cases.push(expression(20)); g = true; advance(':'); funct['(verb)'] = 'case'; break; case 'default': switch (funct['(verb)']) { case 'break': case 'continue': case 'return': case 'throw': break; default: if (!ft.test(lines[nexttoken.line - 2])) { warning( "Expected a 'break' statement before 'default'.", token); } } indentation(-option.indent); advance('default'); g = true; advance(':'); break; case '}': indent -= option.indent; indentation(); advance('}', t); if (this.cases.length === 1 || this.condition.id === 'true' || this.condition.id === 'false') { if (!option.onecase) warning("This 'switch' should be an 'if'.", this); } funct['(breakage)'] -= 1; funct['(verb)'] = undefined; return; case '(end)': error("Missing '{a}'.", nexttoken, '}'); return; default: if (g) { switch (token.id) { case ',': error("Each value should have its own case label."); return; case ':': g = false; statements(); break; default: error("Missing ':' on a case clause.", token); return; } } else { if (token.id === ':') { advance(':'); error("Unexpected '{a}'.", token, ':'); statements(); } else { error("Expected '{a}' and instead saw '{b}'.", nexttoken, 'case', nexttoken.value); return; } } } } }).labelled = true; stmt('debugger', function () { if (!option.debug) { warning("All 'debugger' statements should be removed."); } return this; }).exps = true; (function () { var x = stmt('do', function () { funct['(breakage)'] += 1; funct['(loopage)'] += 1; this.first = block(true); advance('while'); var t = nexttoken; nonadjacent(token, t); advance('('); nospace(); expression(20); if (nexttoken.id === '=') { if (!option.boss) warning("Expected a conditional expression and instead saw an assignment."); advance('='); expression(20); } advance(')', t); nospace(prevtoken, token); funct['(breakage)'] -= 1; funct['(loopage)'] -= 1; return this; }); x.labelled = true; x.exps = true; }()); blockstmt('for', function () { var s, t = nexttoken; funct['(breakage)'] += 1; funct['(loopage)'] += 1; advance('('); nonadjacent(this, t); nospace(); if (peek(nexttoken.id === 'var' ? 1 : 0).id === 'in') { if (nexttoken.id === 'var') { advance('var'); varstatement.fud.call(varstatement, true); } else { switch (funct[nexttoken.value]) { case 'unused': funct[nexttoken.value] = 'var'; break; case 'var': break; default: warning("Bad for in variable '{a}'.", nexttoken, nexttoken.value); } advance(); } advance('in'); expression(20); advance(')', t); s = block(true, true); if (option.forin && s && (s.length > 1 || typeof s[0] !== 'object' || s[0].value !== 'if')) { warning("The body of a for in should be wrapped in an if statement to filter " + "unwanted properties from the prototype.", this); } funct['(breakage)'] -= 1; funct['(loopage)'] -= 1; return this; } else { if (nexttoken.id !== ';') { if (nexttoken.id === 'var') { advance('var'); varstatement.fud.call(varstatement); } else { for (;;) { expression(0, 'for'); if (nexttoken.id !== ',') { break; } comma(); } } } nolinebreak(token); advance(';'); if (nexttoken.id !== ';') { expression(20); if (nexttoken.id === '=') { if (!option.boss) warning("Expected a conditional expression and instead saw an assignment."); advance('='); expression(20); } } nolinebreak(token); advance(';'); if (nexttoken.id === ';') { error("Expected '{a}' and instead saw '{b}'.", nexttoken, ')', ';'); } if (nexttoken.id !== ')') { for (;;) { expression(0, 'for'); if (nexttoken.id !== ',') { break; } comma(); } } advance(')', t); nospace(prevtoken, token); block(true, true); funct['(breakage)'] -= 1; funct['(loopage)'] -= 1; return this; } }).labelled = true; stmt('break', function () { var v = nexttoken.value; if (funct['(breakage)'] === 0) warning("Unexpected '{a}'.", nexttoken, this.value); if (!option.asi) nolinebreak(this); if (nexttoken.id !== ';') { if (token.line === nexttoken.line) { if (funct[v] !== 'label') { warning("'{a}' is not a statement label.", nexttoken, v); } else if (scope[v] !== funct) { warning("'{a}' is out of scope.", nexttoken, v); } this.first = nexttoken; advance(); } } reachable('break'); return this; }).exps = true; stmt('continue', function () { var v = nexttoken.value; if (funct['(breakage)'] === 0) warning("Unexpected '{a}'.", nexttoken, this.value); if (!option.asi) nolinebreak(this); if (nexttoken.id !== ';') { if (token.line === nexttoken.line) { if (funct[v] !== 'label') { warning("'{a}' is not a statement label.", nexttoken, v); } else if (scope[v] !== funct) { warning("'{a}' is out of scope.", nexttoken, v); } this.first = nexttoken; advance(); } } else if (!funct['(loopage)']) { warning("Unexpected '{a}'.", nexttoken, this.value); } reachable('continue'); return this; }).exps = true; stmt('return', function () { if (this.line === nexttoken.line) { if (nexttoken.id === '(regexp)') warning("Wrap the /regexp/ literal in parens to disambiguate the slash operator."); if (nexttoken.id !== ';' && !nexttoken.reach) { nonadjacent(token, nexttoken); if (peek().value === "=" && !option.boss) { warningAt("Did you mean to return a conditional instead of an assignment?", token.line, token.character + 1); } this.first = expression(0); } } else if (!option.asi) { nolinebreak(this); // always warn (Line breaking error) } reachable('return'); return this; }).exps = true; stmt('throw', function () { nolinebreak(this); nonadjacent(token, nexttoken); this.first = expression(20); reachable('throw'); return this; }).exps = true; // Superfluous reserved words reserve('class'); reserve('const'); reserve('enum'); reserve('export'); reserve('extends'); reserve('import'); reserve('super'); reserve('let'); reserve('yield'); reserve('implements'); reserve('interface'); reserve('package'); reserve('private'); reserve('protected'); reserve('public'); reserve('static'); // Parse JSON function jsonValue() { function jsonObject() { var o = {}, t = nexttoken; advance('{'); if (nexttoken.id !== '}') { for (;;) { if (nexttoken.id === '(end)') { error("Missing '}' to match '{' from line {a}.", nexttoken, t.line); } else if (nexttoken.id === '}') { warning("Unexpected comma.", token); break; } else if (nexttoken.id === ',') { error("Unexpected comma.", nexttoken); } else if (nexttoken.id !== '(string)') { warning("Expected a string and instead saw {a}.", nexttoken, nexttoken.value); } if (o[nexttoken.value] === true) { warning("Duplicate key '{a}'.", nexttoken, nexttoken.value); } else if ((nexttoken.value === '__proto__' && !option.proto) || (nexttoken.value === '__iterator__' && !option.iterator)) { warning("The '{a}' key may produce unexpected results.", nexttoken, nexttoken.value); } else { o[nexttoken.value] = true; } advance(); advance(':'); jsonValue(); if (nexttoken.id !== ',') { break; } advance(','); } } advance('}'); } function jsonArray() { var t = nexttoken; advance('['); if (nexttoken.id !== ']') { for (;;) { if (nexttoken.id === '(end)') { error("Missing ']' to match '[' from line {a}.", nexttoken, t.line); } else if (nexttoken.id === ']') { warning("Unexpected comma.", token); break; } else if (nexttoken.id === ',') { error("Unexpected comma.", nexttoken); } jsonValue(); if (nexttoken.id !== ',') { break; } advance(','); } } advance(']'); } switch (nexttoken.id) { case '{': jsonObject(); break; case '[': jsonArray(); break; case 'true': case 'false': case 'null': case '(number)': case '(string)': advance(); break; case '-': advance('-'); if (token.character !== nexttoken.from) { warning("Unexpected space after '-'.", token); } adjacent(token, nexttoken); advance('(number)'); break; default: error("Expected a JSON value.", nexttoken); } } // The actual JSHINT function itself. var itself = function (s, o, g) { var a, i, k; JSHINT.errors = []; JSHINT.undefs = []; predefined = Object.create(standard); combine(predefined, g || {}); if (o) { a = o.predef; if (a) { if (Array.isArray(a)) { for (i = 0; i < a.length; i += 1) { predefined[a[i]] = true; } } else if (typeof a === 'object') { k = Object.keys(a); for (i = 0; i < k.length; i += 1) { predefined[k[i]] = !!a[k[i]]; } } } option = o; } else { option = {}; } option.indent = option.indent || 4; option.maxerr = option.maxerr || 50; tab = ''; for (i = 0; i < option.indent; i += 1) { tab += ' '; } indent = 1; global = Object.create(predefined); scope = global; funct = { '(global)': true, '(name)': '(global)', '(scope)': scope, '(breakage)': 0, '(loopage)': 0 }; functions = [funct]; urls = []; stack = null; member = {}; membersOnly = null; implied = {}; inblock = false; lookahead = []; jsonmode = false; warnings = 0; lex.init(s); prereg = true; directive = {}; prevtoken = token = nexttoken = syntax['(begin)']; assume(); // combine the passed globals after we've assumed all our options combine(predefined, g || {}); try { advance(); switch (nexttoken.id) { case '{': case '[': option.laxbreak = true; jsonmode = true; jsonValue(); break; default: directives(); if (directive["use strict"] && !option.globalstrict) { warning("Use the function form of \"use strict\".", prevtoken); } statements(); } advance('(end)'); var markDefined = function (name, context) { do { if (typeof context[name] === 'string') { // JSHINT marks unused variables as 'unused' and // unused function declaration as 'unction'. This // code changes such instances back 'var' and // 'closure' so that the code in JSHINT.data() // doesn't think they're unused. if (context[name] === 'unused') context[name] = 'var'; else if (context[name] === 'unction') context[name] = 'closure'; return true; } context = context['(context)']; } while (context); return false; }; var clearImplied = function (name, line) { if (!implied[name]) return; var newImplied = []; for (var i = 0; i < implied[name].length; i += 1) { if (implied[name][i] !== line) newImplied.push(implied[name][i]); } if (newImplied.length === 0) delete implied[name]; else implied[name] = newImplied; }; // Check queued 'x is not defined' instances to see if they're still undefined. for (i = 0; i < JSHINT.undefs.length; i += 1) { k = JSHINT.undefs[i].slice(0); if (markDefined(k[2].value, k[0])) { clearImplied(k[2].value, k[2].line); } else { warning.apply(warning, k.slice(1)); } } } catch (e) { if (e) { var nt = nexttoken || {}; JSHINT.errors.push({ raw : e.raw, reason : e.message, line : e.line || nt.line, character : e.character || nt.from }, null); } } return JSHINT.errors.length === 0; }; // Data summary. itself.data = function () { var data = { functions: [], options: option }, fu, globals, implieds = [], f, i, j, members = [], n, unused = [], v; if (itself.errors.length) { data.errors = itself.errors; } if (jsonmode) { data.json = true; } for (n in implied) { if (is_own(implied, n)) { implieds.push({ name: n, line: implied[n] }); } } if (implieds.length > 0) { data.implieds = implieds; } if (urls.length > 0) { data.urls = urls; } globals = Object.keys(scope); if (globals.length > 0) { data.globals = globals; } for (i = 1; i < functions.length; i += 1) { f = functions[i]; fu = {}; for (j = 0; j < functionicity.length; j += 1) { fu[functionicity[j]] = []; } for (n in f) { if (is_own(f, n) && n.charAt(0) !== '(') { v = f[n]; if (v === 'unction') { v = 'unused'; } if (Array.isArray(fu[v])) { fu[v].push(n); if (v === 'unused') { unused.push({ name: n, line: f['(line)'], 'function': f['(name)'] }); } } } } for (j = 0; j < functionicity.length; j += 1) { if (fu[functionicity[j]].length === 0) { delete fu[functionicity[j]]; } } fu.name = f['(name)']; fu.param = f['(params)']; fu.line = f['(line)']; fu.last = f['(last)']; data.functions.push(fu); } if (unused.length > 0) { data.unused = unused; } members = []; for (n in member) { if (typeof member[n] === 'number') { data.member = member; break; } } return data; }; itself.report = function (option) { var data = itself.data(); var a = [], c, e, err, f, i, k, l, m = '', n, o = [], s; function detail(h, array) { var b, i, singularity; if (array) { o.push('<div><i>' + h + '</i> '); array = array.sort(); for (i = 0; i < array.length; i += 1) { if (array[i] !== singularity) { singularity = array[i]; o.push((b ? ', ' : '') + singularity); b = true; } } o.push('</div>'); } } if (data.errors || data.implieds || data.unused) { err = true; o.push('<div id=errors><i>Error:</i>'); if (data.errors) { for (i = 0; i < data.errors.length; i += 1) { c = data.errors[i]; if (c) { e = c.evidence || ''; o.push('<p>Problem' + (isFinite(c.line) ? ' at line ' + c.line + ' character ' + c.character : '') + ': ' + c.reason.entityify() + '</p><p class=evidence>' + (e && (e.length > 80 ? e.slice(0, 77) + '...' : e).entityify()) + '</p>'); } } } if (data.implieds) { s = []; for (i = 0; i < data.implieds.length; i += 1) { s[i] = '<code>' + data.implieds[i].name + '</code>&nbsp;<i>' + data.implieds[i].line + '</i>'; } o.push('<p><i>Implied global:</i> ' + s.join(', ') + '</p>'); } if (data.unused) { s = []; for (i = 0; i < data.unused.length; i += 1) { s[i] = '<code><u>' + data.unused[i].name + '</u></code>&nbsp;<i>' + data.unused[i].line + '</i> <code>' + data.unused[i]['function'] + '</code>'; } o.push('<p><i>Unused variable:</i> ' + s.join(', ') + '</p>'); } if (data.json) { o.push('<p>JSON: bad.</p>'); } o.push('</div>'); } if (!option) { o.push('<br><div id=functions>'); if (data.urls) { detail("URLs<br>", data.urls, '<br>'); } if (data.json && !err) { o.push('<p>JSON: good.</p>'); } else if (data.globals) { o.push('<div><i>Global</i> ' + data.globals.sort().join(', ') + '</div>'); } else { o.push('<div><i>No new global variables introduced.</i></div>'); } for (i = 0; i < data.functions.length; i += 1) { f = data.functions[i]; o.push('<br><div class=function><i>' + f.line + '-' + f.last + '</i> ' + (f.name || '') + '(' + (f.param ? f.param.join(', ') : '') + ')</div>'); detail('<big><b>Unused</b></big>', f.unused); detail('Closure', f.closure); detail('Variable', f['var']); detail('Exception', f.exception); detail('Outer', f.outer); detail('Global', f.global); detail('Label', f.label); } if (data.member) { a = Object.keys(data.member); if (a.length) { a = a.sort(); m = '<br><pre id=members>/*members '; l = 10; for (i = 0; i < a.length; i += 1) { k = a[i]; n = k.name(); if (l + n.length > 72) { o.push(m + '<br>'); m = ' '; l = 1; } l += n.length + 2; if (data.member[k] === 1) { n = '<i>' + n + '</i>'; } if (i < a.length - 1) { n += ', '; } m += n; } o.push(m + '<br>*/</pre>'); } o.push('</div>'); } } return o.join(''); }; itself.jshint = itself; return itself; }()); // Make JSHINT a Node module, if possible. if (typeof exports === 'object' && exports) exports.JSHINT = JSHINT;