[javascriptlint-commit] SF.net SVN: javascriptlint:[355] trunk/javascriptlint
Status: Beta
Brought to you by:
matthiasmiller
|
From: <mat...@us...> - 2016-12-23 20:45:43
|
Revision: 355
http://sourceforge.net/p/javascriptlint/code/355
Author: matthiasmiller
Date: 2016-12-23 20:45:40 +0000 (Fri, 23 Dec 2016)
Log Message:
-----------
Fix name conflict with built-in module.
Modified Paths:
--------------
trunk/javascriptlint/conf.py
trunk/javascriptlint/lint.py
Added Paths:
-----------
trunk/javascriptlint/lintwarnings.py
Removed Paths:
-------------
trunk/javascriptlint/warnings.py
Modified: trunk/javascriptlint/conf.py
===================================================================
--- trunk/javascriptlint/conf.py 2014-10-27 22:25:27 UTC (rev 354)
+++ trunk/javascriptlint/conf.py 2016-12-23 20:45:40 UTC (rev 355)
@@ -5,7 +5,7 @@
import fs
import util
import version
-import warnings
+import lintwarnings
_DISABLED_WARNINGS = (
'block_without_braces',
@@ -16,8 +16,8 @@
def _getwarningsconf():
lines = []
- for name in sorted(warnings.warnings.keys()):
- message = warnings.warnings[name]
+ for name in sorted(lintwarnings.warnings.keys()):
+ message = lintwarnings.warnings[name]
sign = '+'
if name in _DISABLED_WARNINGS:
sign = '-'
@@ -207,7 +207,7 @@
'anon_no_return_value': BooleanSetting(True),
'decorate_function_name_warning': BooleanSetting(False),
}
- for name in warnings.warnings:
+ for name in lintwarnings.warnings:
self._settings[name] = BooleanSetting(True)
for warning in _DISABLED_WARNINGS:
self.loadline('-%s' % warning)
Modified: trunk/javascriptlint/lint.py
===================================================================
--- trunk/javascriptlint/lint.py 2014-10-27 22:25:27 UTC (rev 354)
+++ trunk/javascriptlint/lint.py 2016-12-23 20:45:40 UTC (rev 355)
@@ -7,8 +7,8 @@
import fs
import htmlparse
import jsparse
+import lintwarnings
import visitation
-import warnings
import unittest
import util
@@ -482,7 +482,7 @@
# Find all visitors and convert them into "onpush" callbacks that call "report"
visitors = {
- 'push': warnings.make_visitors(conf)
+ 'push': lintwarnings.make_visitors(conf)
}
for event in visitors:
for kind, callbacks in visitors[event].items():
@@ -516,11 +516,11 @@
def _lint_script_parts(script_parts, script_cache, lint_error, conf, import_callback):
def report_lint(node, errname, offset=0, **errargs):
- errdesc = warnings.format_error(errname, **errargs)
+ errdesc = lintwarnings.format_error(errname, **errargs)
_report(offset or node.start_offset, errname, errdesc, True)
def report_native(offset, errname, errargs):
- errdesc = warnings.format_error(errname, **errargs)
+ errdesc = lintwarnings.format_error(errname, **errargs)
_report(offset, errname, errdesc, False)
def _report(offset, errname, errdesc, require_key):
@@ -571,7 +571,7 @@
try:
ret = visitor(node)
assert ret is None, 'visitor should raise an exception, not return a value'
- except warnings.LintWarning, warning:
+ except lintwarnings.LintWarning, warning:
# TODO: This is ugly hardcoding to improve the error positioning of
# "missing_semicolon" errors.
if visitor.warning in ('missing_semicolon', 'missing_semicolon_for_lambda'):
Copied: trunk/javascriptlint/lintwarnings.py (from rev 354, trunk/javascriptlint/warnings.py)
===================================================================
--- trunk/javascriptlint/lintwarnings.py (rev 0)
+++ trunk/javascriptlint/lintwarnings.py 2016-12-23 20:45:40 UTC (rev 355)
@@ -0,0 +1,814 @@
+# vim: ts=4 sw=4 expandtab
+""" This module contains all the warnings. To add a new warning, define a
+function. Its name should be in lowercase and words should be separated by
+underscores.
+
+The function should be decorated with a @lookfor call specifying the nodes it
+wants to examine. The node names may be in the tok.KIND or (tok.KIND, op.OPCODE)
+format. To report a warning, the function should raise a LintWarning exception.
+
+For example:
+
+ @lookfor(tok.NODEKIND, (tok.NODEKIND, op.OPCODE))
+ def warning_name(node):
+ if questionable:
+ raise LintWarning(node)
+"""
+import itertools
+import re
+import sys
+import types
+
+import util
+import visitation
+
+from jsengine.parser import kind as tok
+from jsengine.parser import op
+
+_ALL_TOKENS = tok.__dict__.values()
+
+def _get_assigned_lambda(node):
+ """ Given a node "x = function() {}", returns "function() {}".
+ """
+ value = None
+ if node.kind == tok.SEMI:
+ assign_node, = node.kids
+ if assign_node and assign_node.kind == tok.ASSIGN:
+ ignored, value = assign_node.kids
+ elif node.kind == tok.VAR:
+ variables = node.kids
+ if variables:
+ value, = variables[-1].kids
+
+ if value and value.kind == tok.FUNCTION and value.opcode == op.ANONFUNOBJ:
+ return value
+
+# TODO: document inspect, node:opcode, etc
+
+warnings = {
+ 'comparison_type_conv': 'comparisons against null, 0, true, false, or an empty string allowing implicit type conversion (use === or !==)',
+ 'default_not_at_end': 'the default case is not at the end of the switch statement',
+ 'duplicate_case_in_switch': 'duplicate case in switch statement',
+ 'missing_default_case': 'missing default case in switch statement',
+ 'with_statement': 'with statement hides undeclared variables; use temporary variable instead',
+ 'useless_comparison': 'useless comparison; comparing identical expressions',
+ 'use_of_label': 'use of label',
+ 'misplaced_regex': 'regular expressions should be preceded by a left parenthesis, assignment, colon, or comma',
+ 'assign_to_function_call': 'assignment to a function call',
+ 'equal_as_assign': 'test for equality (==) mistyped as assignment (=)?',
+ 'ambiguous_else_stmt': 'the else statement could be matched with one of multiple if statements (use curly braces to indicate intent',
+ 'block_without_braces': 'block statement without curly braces',
+ 'ambiguous_nested_stmt': 'block statements containing block statements should use curly braces to resolve ambiguity',
+ 'inc_dec_within_stmt': 'increment (++) and decrement (--) operators used as part of greater statement',
+ 'comma_separated_stmts': 'multiple statements separated by commas (use semicolons?)',
+ 'empty_statement': 'empty statement or extra semicolon',
+ 'missing_break': 'missing break statement',
+ 'missing_break_for_last_case': 'missing break statement for last case in switch',
+ 'multiple_plus_minus': 'unknown order of operations for successive plus (e.g. x+++y) or minus (e.g. x---y) signs',
+ 'useless_assign': 'useless assignment',
+ 'unreachable_code': 'unreachable code',
+ 'meaningless_block': 'meaningless block; curly braces have no impact',
+ 'useless_void': 'use of the void type may be unnecessary (void is always undefined)',
+ 'parseint_missing_radix': 'parseInt missing radix parameter',
+ 'leading_decimal_point': 'leading decimal point may indicate a number or an object member',
+ 'trailing_decimal_point': 'trailing decimal point may indicate a number or an object member',
+ 'octal_number': 'leading zeros make an octal number',
+ 'trailing_comma': 'extra comma is not recommended in object initializers',
+ 'trailing_comma_in_array': 'extra comma is not recommended in array initializers',
+ 'useless_quotes': 'the quotation marks are unnecessary',
+ 'mismatch_ctrl_comments': 'mismatched control comment; "ignore" and "end" control comments must have a one-to-one correspondence',
+ 'redeclared_var': 'redeclaration of {name}',
+ 'undeclared_identifier': 'undeclared identifier: {name}',
+ 'unreferenced_argument': 'argument declared but never referenced: {name}',
+ 'unreferenced_function': 'function is declared but never referenced: {name}',
+ 'unreferenced_variable': 'variable is declared but never referenced: {name}',
+ 'jsl_cc_not_understood': 'couldn\'t understand control comment using /*jsl:keyword*/ syntax',
+ 'nested_comment': 'nested comment',
+ 'legacy_cc_not_understood': 'couldn\'t understand control comment using /*@keyword@*/ syntax',
+ 'var_hides_arg': 'variable {name} hides argument',
+ 'identifier_hides_another': 'identifer {name} hides an identifier in a parent scope',
+ 'duplicate_formal': 'duplicate formal argument {name}',
+ 'missing_semicolon': 'missing semicolon',
+ 'missing_semicolon_for_lambda': 'missing semicolon for lambda assignment',
+ 'ambiguous_newline': 'unexpected end of line; it is ambiguous whether these lines are part of the same statement',
+ 'missing_option_explicit': 'the "option explicit" control comment is missing',
+ 'partial_option_explicit': 'the "option explicit" control comment, if used, must be in the first script tag',
+ 'dup_option_explicit': 'duplicate "option explicit" control comment',
+ 'invalid_fallthru': 'unexpected "fallthru" control comment',
+ 'invalid_pass': 'unexpected "pass" control comment',
+ 'want_assign_or_call': 'expected an assignment or function call',
+ 'no_return_value': 'function {name} does not always return a value',
+ 'anon_no_return_value': 'anonymous function does not always return value',
+ 'unsupported_version': 'JavaScript {version} is not supported',
+ 'incorrect_version': 'Expected /*jsl:content-type*/ control comment. The script was parsed with the wrong version.',
+ 'for_in_missing_identifier': 'for..in should have identifier on left side',
+ 'misplaced_function': 'unconventional use of function expression',
+ 'function_name_missing': 'anonymous function should be named to match property name {name}',
+ 'function_name_mismatch': 'function name {fn_name} does not match property name {prop_name}',
+ 'trailing_whitespace': 'trailing whitespace',
+}
+
+errors = {
+ 'e4x_deprecated': 'e4x is deprecated',
+ 'semi_before_stmnt': 'missing semicolon before statement',
+ 'syntax_error': 'syntax error',
+ 'expected_tok': 'expected token: {token}',
+ 'unexpected_char': 'unexpected character: {char}',
+ 'unexpected_eof': 'unexpected end of file',
+ 'io_error': '{error}',
+}
+
+def format_error(errname, **errargs):
+ if errname in errors:
+ errdesc = errors[errname]
+ else:
+ errdesc = warnings[errname]
+
+ try:
+ keyword = re.compile(r"{(\w+)}")
+ errdesc = keyword.sub(lambda match: errargs[match.group(1)], errdesc)
+ except (TypeError, KeyError):
+ raise KeyError('Invalid keyword in error %s: %s' % (errname, errdesc))
+ return errdesc
+
+_visitors = []
+def lookfor(*args):
+ def decorate(fn):
+ fn.warning = fn.func_name.rstrip('_')
+ assert fn.warning in warnings, 'Missing warning description: %s' % fn.warning
+
+ for arg in args:
+ _visitors.append((arg, fn))
+ return decorate
+
+_visitor_classes = []
+def lookfor_class(*args):
+ def decorate(cls):
+ # Convert the class name to camel case
+ camelcase = re.sub('([A-Z])', r'_\1', cls.__name__).lower().lstrip('_')
+
+ cls.warning = camelcase.rstrip('_')
+ assert cls.warning in warnings, \
+ 'Missing warning description: %s' % cls.warning
+
+ for arg in args:
+ _visitor_classes.append((arg, cls))
+ return decorate
+
+class LintWarning(Exception):
+ def __init__(self, node, **errargs):
+ self.node = node
+ self.errargs = errargs
+
+def _get_branch_in_for(node):
+ " Returns None if this is not one of the branches in a 'for' "
+ if node.parent and node.parent.kind == tok.RESERVED and \
+ node.parent.parent.kind == tok.FOR:
+ return node.node_index
+ return None
+
+def _get_exit_points(node):
+ """ Returns a set of exit points, which may be:
+ * None, indicating a code path with no specific exit point.
+ * a node of type tok.BREAK, tok.RETURN, tok.THROW.
+ """
+ if node.kind == tok.LC:
+ exit_points = set([None])
+ for kid in node.kids:
+ if kid:
+ # Merge in the kid's exit points.
+ kid_exit_points = _get_exit_points(kid)
+ exit_points |= kid_exit_points
+
+ # Stop if the kid always exits.
+ if not None in kid_exit_points:
+ exit_points.discard(None)
+ break
+ elif node.kind == tok.IF:
+ # Only if both branches have an exit point
+ cond_, if_, else_ = node.kids
+ exit_points = _get_exit_points(if_)
+ if else_:
+ exit_points |= _get_exit_points(else_)
+ else:
+ exit_points.add(None)
+ elif node.kind == tok.SWITCH:
+ exit_points = set([None])
+
+ switch_has_default = False
+ switch_has_final_fallthru = True
+
+ switch_var, switch_stmts = node.kids
+ for node in switch_stmts.kids:
+ case_val, case_stmt = node.kids
+ case_exit_points = _get_exit_points(case_stmt)
+ switch_has_default = switch_has_default or node.kind == tok.DEFAULT
+ switch_has_final_fallthru = None in case_exit_points
+ exit_points |= case_exit_points
+
+ # Correct the "None" exit point.
+ exit_points.remove(None)
+
+ # Convert "break" into None
+ def break_to_none(node):
+ if node and node.kind != tok.BREAK:
+ return node
+ exit_points = set(map(break_to_none, exit_points))
+
+ # Check if the switch had a default case
+ if not switch_has_default:
+ exit_points.add(None)
+
+ # Check if the final case statement had a fallthru
+ if switch_has_final_fallthru:
+ exit_points.add(None)
+ elif node.kind == tok.BREAK:
+ exit_points = set([node])
+ elif node.kind == tok.CONTINUE:
+ exit_points = set([node])
+ elif node.kind == tok.WITH:
+ exit_points = _get_exit_points(node.kids[-1])
+ elif node.kind == tok.RETURN:
+ exit_points = set([node])
+ elif node.kind == tok.THROW:
+ exit_points = set([node])
+ elif node.kind == tok.TRY:
+ try_, catch_, finally_ = node.kids
+
+ exit_points = _get_exit_points(try_)
+
+ if catch_:
+ assert catch_.kind == tok.RESERVED
+ catch_, = catch_.kids
+ assert catch_.kind == tok.LEXICALSCOPE
+ catch_, = catch_.kids
+ assert catch_.kind == tok.CATCH
+ ignored, ignored, catch_ = catch_.kids
+ assert catch_.kind == tok.LC
+
+ exit_points |= _get_exit_points(catch_)
+
+ if finally_:
+ finally_exit_points = _get_exit_points(finally_)
+ if None in finally_exit_points:
+ # The finally statement does not add a missing exit point.
+ finally_exit_points.remove(None)
+ else:
+ # If the finally statement always returns, the other
+ # exit points are irrelevant.
+ if None in exit_points:
+ exit_points.remove(None)
+
+ exit_points |= finally_exit_points
+
+ else:
+ exit_points = set([None])
+
+ return exit_points
+
+def _loop_has_unreachable_condition(node):
+ for exit_point in _get_exit_points(node):
+ if exit_point is None:
+ return False
+ if exit_point.kind == tok.CONTINUE:
+ return False
+ return True
+
+@lookfor((tok.EQOP, op.EQ))
+def comparison_type_conv(node):
+ for kid in node.kids:
+ if kid.kind == tok.PRIMARY and kid.opcode in (op.NULL, op.TRUE, op.FALSE):
+ raise LintWarning(kid)
+ if kid.kind == tok.NUMBER and not kid.dval:
+ raise LintWarning(kid)
+ if kid.kind == tok.STRING and not kid.atom:
+ raise LintWarning(kid)
+
+@lookfor(tok.DEFAULT)
+def default_not_at_end(node):
+ siblings = node.parent.kids
+ if node.node_index != len(siblings)-1:
+ raise LintWarning(siblings[node.node_index+1])
+
+@lookfor(tok.CASE)
+def duplicate_case_in_switch(node):
+ # Only look at previous siblings
+ siblings = node.parent.kids
+ siblings = siblings[:node.node_index]
+ # Compare values (first kid)
+ node_value = node.kids[0]
+ for sibling in siblings:
+ if sibling.kind == tok.CASE:
+ sibling_value = sibling.kids[0]
+ if node_value.is_equivalent(sibling_value, True):
+ raise LintWarning(node)
+
+@lookfor(tok.SWITCH)
+def missing_default_case(node):
+ value, cases = node.kids
+ for case in cases.kids:
+ if case.kind == tok.DEFAULT:
+ return
+ raise LintWarning(node)
+
+@lookfor(tok.WITH)
+def with_statement(node):
+ raise LintWarning(node)
+
+@lookfor(tok.EQOP, tok.RELOP)
+def useless_comparison(node):
+ for lvalue, rvalue in itertools.combinations(node.kids, 2):
+ if lvalue.is_equivalent(rvalue):
+ raise LintWarning(node)
+
+@lookfor((tok.COLON, op.NAME))
+def use_of_label(node):
+ raise LintWarning(node)
+
+@lookfor((tok.OBJECT, op.REGEXP))
+def misplaced_regex(node):
+ if node.parent.kind == tok.NAME and node.parent.opcode == op.SETNAME:
+ return # Allow in var statements
+ if node.parent.kind == tok.ASSIGN and node.parent.opcode == op.NOP:
+ return # Allow in assigns
+ if node.parent.kind == tok.COLON and node.parent.parent.kind == tok.RC:
+ return # Allow in object literals
+ if node.parent.kind == tok.LP and node.parent.opcode == op.CALL:
+ return # Allow in parameters
+ if node.parent.kind == tok.DOT and node.parent.opcode == op.GETPROP:
+ return # Allow in /re/.property
+ if node.parent.kind == tok.RETURN:
+ return # Allow for return values
+ raise LintWarning(node)
+
+@lookfor(tok.ASSIGN)
+def assign_to_function_call(node):
+ kid = node.kids[0]
+ # Unpack parens.
+ while kid.kind == tok.RP:
+ kid, = kid.kids
+ if kid.kind == tok.LP:
+ raise LintWarning(node)
+
+@lookfor((tok.ASSIGN, None))
+def equal_as_assign(node):
+ # Allow in VAR statements.
+ if node.parent.parent and node.parent.parent.kind == tok.VAR:
+ return
+
+ if not node.parent.kind in (tok.SEMI, tok.RESERVED, tok.RP, tok.COMMA,
+ tok.ASSIGN):
+ raise LintWarning(node)
+
+@lookfor(tok.IF)
+def ambiguous_else_stmt(node):
+ # Only examine this node if it has an else statement.
+ condition, if_, else_ = node.kids
+ if not else_:
+ return
+
+ tmp = node
+ while tmp:
+ # Curly braces always clarify if statements.
+ if tmp.kind == tok.LC:
+ return
+ # Else is only ambiguous in the first branch of an if statement.
+ if tmp.parent.kind == tok.IF and tmp.node_index == 1:
+ raise LintWarning(else_)
+ tmp = tmp.parent
+
+@lookfor(tok.IF, tok.WHILE, tok.DO, tok.FOR, tok.WITH)
+def block_without_braces(node):
+ if node.kids[1].kind != tok.LC:
+ raise LintWarning(node.kids[1])
+
+_block_nodes = (tok.IF, tok.WHILE, tok.DO, tok.FOR, tok.WITH)
+@lookfor(*_block_nodes)
+def ambiguous_nested_stmt(node):
+ # Ignore "else if"
+ if node.kind == tok.IF and node.node_index == 2 and node.parent.kind == tok.IF:
+ return
+
+ # If the parent is a block, it means a block statement
+ # was inside a block statement without clarifying curlies.
+ # (Otherwise, the node type would be tok.LC.)
+ if node.parent.kind in _block_nodes:
+ raise LintWarning(node)
+
+@lookfor(tok.INC, tok.DEC)
+def inc_dec_within_stmt(node):
+ if node.parent.kind == tok.SEMI:
+ return
+
+ # Allow within the third part of the "for"
+ tmp = node
+ while tmp and tmp.parent and tmp.parent.kind == tok.COMMA:
+ tmp = tmp.parent
+ if tmp and tmp.node_index == 2 and \
+ tmp.parent.kind == tok.RESERVED and \
+ tmp.parent.parent.kind == tok.FOR:
+ return
+
+ raise LintWarning(node)
+
+@lookfor(tok.COMMA)
+def comma_separated_stmts(node):
+ # Allow within the first and third part of "for(;;)"
+ if _get_branch_in_for(node) in (0, 2):
+ return
+ # This is an array
+ if node.parent.kind == tok.RB:
+ return
+ raise LintWarning(node)
+
+@lookfor(tok.SEMI)
+def empty_statement(node):
+ if not node.kids[0]:
+ raise LintWarning(node)
+@lookfor(tok.LC)
+def empty_statement_(node):
+ if node.kids:
+ return
+ # Ignore the outermost block.
+ if not node.parent:
+ return
+ # Some empty blocks are meaningful.
+ if node.parent.kind in (tok.CATCH, tok.CASE, tok.DEFAULT, tok.SWITCH, tok.FUNCTION):
+ return
+ raise LintWarning(node)
+
+@lookfor(tok.CASE, tok.DEFAULT)
+def missing_break(node):
+ # The last item is handled separately
+ if node.node_index == len(node.parent.kids)-1:
+ return
+ case_contents = node.kids[1]
+ assert case_contents.kind == tok.LC
+ # Ignore empty case statements
+ if not case_contents.kids:
+ return
+ if None in _get_exit_points(case_contents):
+ # Show the warning on the *next* node.
+ raise LintWarning(node.parent.kids[node.node_index+1])
+
+@lookfor(tok.CASE, tok.DEFAULT)
+def missing_break_for_last_case(node):
+ if node.node_index < len(node.parent.kids)-1:
+ return
+ case_contents = node.kids[1]
+ assert case_contents.kind == tok.LC
+ if None in _get_exit_points(case_contents):
+ raise LintWarning(node)
+
+@lookfor(tok.INC)
+def multiple_plus_minus(node):
+ if node.node_index == 0 and node.parent.kind == tok.PLUS:
+ raise LintWarning(node)
+@lookfor(tok.DEC)
+def multiple_plus_minus_(node):
+ if node.node_index == 0 and node.parent.kind == tok.MINUS:
+ raise LintWarning(node)
+
+@lookfor((tok.NAME, op.SETNAME))
+def useless_assign(node):
+ if node.parent.kind == tok.ASSIGN:
+ assert node.node_index == 0
+ value = node.parent.kids[1]
+ elif node.parent.kind == tok.VAR:
+ value = node.kids[0]
+ if value and value.kind == tok.NAME and node.atom == value.atom:
+ raise LintWarning(node)
+
+@lookfor(tok.BREAK, tok.CONTINUE, tok.RETURN, tok.THROW)
+def unreachable_code(node):
+ if node.parent.kind == tok.LC:
+ for sibling in node.parent.kids[node.node_index+1:]:
+ if sibling.kind == tok.VAR:
+ # Look for a variable assignment
+ for variable in sibling.kids:
+ value, = variable.kids
+ if value:
+ raise LintWarning(value)
+ elif sibling.kind == tok.FUNCTION:
+ # Functions are always declared.
+ pass
+ else:
+ raise LintWarning(sibling)
+
+@lookfor(tok.FOR)
+def unreachable_code_(node):
+ # Warn if the for loop always exits.
+ preamble, code = node.kids
+ if preamble.kind == tok.RESERVED:
+ pre, condition, post = preamble.kids
+ if post:
+ if _loop_has_unreachable_condition(code):
+ raise LintWarning(post)
+
+@lookfor(tok.DO)
+def unreachable_code__(node):
+ # Warn if the do..while loop always exits.
+ code, condition = node.kids
+ if _loop_has_unreachable_condition(code):
+ raise LintWarning(condition)
+
+#TODO: @lookfor(tok.IF)
+def meaningless_block(node):
+ condition, if_, else_ = node.kids
+ if condition.kind == tok.PRIMARY and condition.opcode in (op.TRUE, op.FALSE, op.NULL):
+ raise LintWarning(condition)
+#TODO: @lookfor(tok.WHILE)
+def meaningless_blocK_(node):
+ condition = node.kids[0]
+ if condition.kind == tok.PRIMARY and condition.opcode in (op.FALSE, op.NULL):
+ raise LintWarning(condition)
+@lookfor(tok.LC)
+def meaningless_block__(node):
+ if node.parent and node.parent.kind == tok.LC:
+ raise LintWarning(node)
+
+@lookfor((tok.UNARYOP, op.VOID))
+def useless_void(node):
+ raise LintWarning(node)
+
+@lookfor((tok.LP, op.CALL))
+def parseint_missing_radix(node):
+ if node.kids[0].kind == tok.NAME and node.kids[0].atom == 'parseInt' and len(node.kids) <= 2:
+ raise LintWarning(node)
+
+@lookfor(tok.NUMBER)
+def leading_decimal_point(node):
+ if node.atom.startswith('.'):
+ raise LintWarning(node)
+
+@lookfor(tok.NUMBER)
+def trailing_decimal_point(node):
+ if node.parent.kind == tok.DOT:
+ raise LintWarning(node)
+ if node.atom.endswith('.'):
+ raise LintWarning(node)
+
+_octal_regexp = re.compile('^0[0-9]')
+@lookfor(tok.NUMBER)
+def octal_number(node):
+ if _octal_regexp.match(node.atom):
+ raise LintWarning(node)
+
+@lookfor(tok.RC)
+def trailing_comma(node):
+ if node.end_comma:
+ # Warn on the last value in the dictionary.
+ last_item = node.kids[-1]
+ assert last_item.kind == tok.COLON
+ key, value = last_item.kids
+ raise LintWarning(value)
+
+@lookfor(tok.RB)
+def trailing_comma_in_array(node):
+ if node.end_comma:
+ # Warn on the last value in the array.
+ raise LintWarning(node.kids[-1])
+
+@lookfor(tok.STRING)
+def useless_quotes(node):
+ if node.node_index == 0 and node.parent.kind == tok.COLON:
+ # Only warn if the quotes could safely be removed.
+ if util.isidentifier(node.atom):
+ raise LintWarning(node)
+
+@lookfor(tok.SEMI)
+def want_assign_or_call(node):
+ child, = node.kids
+ # Ignore empty statements.
+ if not child:
+ return
+ # NOTE: Don't handle comma-separated statements.
+ if child.kind in (tok.ASSIGN, tok.INC, tok.DEC, tok.DELETE, tok.COMMA):
+ return
+ if child.kind == tok.YIELD:
+ return
+ # Ignore function calls.
+ if child.kind == tok.LP and child.opcode == op.CALL:
+ return
+ # Allow new function() { } as statements.
+ if child.kind == tok.NEW:
+ # The first kid is the constructor, followed by its arguments.
+ grandchild = child.kids[0]
+ if grandchild.kind == tok.FUNCTION:
+ return
+ raise LintWarning(child)
+
+def _check_return_value(node):
+ name = node.fn_name or '(anonymous function)'
+
+ def is_return_with_val(node):
+ return node and node.kind == tok.RETURN and node.kids[0]
+ def is_return_without_val(node):
+ return node and node.kind == tok.RETURN and not node.kids[0]
+
+ node, = node.kids
+ assert node.kind == tok.LC
+
+ exit_points = _get_exit_points(node)
+ if filter(is_return_with_val, exit_points):
+ # If the function returns a value, find all returns without a value.
+ returns = filter(is_return_without_val, exit_points)
+ returns.sort(key=lambda node: node.start_offset)
+ if returns:
+ raise LintWarning(returns[0], name=name)
+ # Warn if the function sometimes exits naturally.
+ if None in exit_points:
+ raise LintWarning(node, name=name)
+
+@lookfor(tok.FUNCTION)
+def no_return_value(node):
+ if node.fn_name:
+ _check_return_value(node)
+
+@lookfor(tok.FUNCTION)
+def anon_no_return_value(node):
+ if not node.fn_name:
+ _check_return_value(node)
+
+@lookfor((tok.FOR, op.FORIN))
+def for_in_missing_identifier(node):
+ assert node.kids[0].kind == tok.IN
+ left, right = node.kids[0].kids
+ if not left.kind in (tok.VAR, tok.NAME):
+ raise LintWarning(left)
+
+@lookfor(tok.FUNCTION)
+def misplaced_function(node):
+ # Ignore function statements.
+ if node.opcode in (None, op.CLOSURE):
+ return
+
+ # Ignore parens.
+ parent = node.parent
+ while parent.kind == tok.RP:
+ parent = parent.parent
+
+ # Allow x = x || ...
+ if parent.kind == tok.OR and len(parent.kids) == 2 and \
+ node is parent.kids[-1]:
+ parent = parent.parent
+
+ if parent.kind == tok.NAME and parent.opcode == op.SETNAME:
+ return # Allow in var statements
+ if parent.kind == tok.ASSIGN and parent.opcode == op.NOP:
+ return # Allow in assigns
+ if parent.kind == tok.COLON and parent.parent.kind == tok.RC:
+ return # Allow in object literals
+ if parent.kind == tok.LP and parent.opcode in (op.CALL, op.SETCALL):
+ return # Allow in parameters
+ if parent.kind == tok.RETURN:
+ return # Allow for return values
+ if parent.kind == tok.NEW:
+ return # Allow as constructors
+ raise LintWarning(node)
+
+def _get_function_property_name(node):
+ # Ignore function statements.
+ if node.opcode in (None, op.CLOSURE):
+ return
+
+ # Ignore parens.
+ parent = node.parent
+ while parent.kind == tok.RP:
+ parent = parent.parent
+
+ # Allow x = x || ...
+ if parent.kind == tok.OR and len(parent.kids) == 2 and \
+ node is parent.kids[-1]:
+ parent = parent.parent
+
+ # Var assignment.
+ if parent.kind == tok.NAME and parent.opcode == op.SETNAME:
+ return parent.atom
+
+ # Assignment.
+ if parent.kind == tok.ASSIGN and parent.opcode == op.NOP:
+ if parent.kids[0].kind == tok.NAME and \
+ parent.kids[0].opcode == op.SETNAME:
+ return parent.kids[0].atom
+ if parent.kids[0].kind == tok.DOT and \
+ parent.kids[0].opcode == op.SETPROP:
+ return parent.kids[0].atom
+ return '<error>'
+
+ # Object literal.
+ if parent.kind == tok.COLON and parent.parent.kind == tok.RC:
+ return parent.kids[0].atom
+
+def _get_expected_function_name(node, decorate):
+ name = _get_function_property_name(node)
+ if name and decorate:
+ return '__%s' % name
+ return name
+
+@lookfor_class(tok.FUNCTION)
+class FunctionNameMissing(object):
+ def __init__(self, conf):
+ self._decorate = conf['decorate_function_name_warning']
+
+ def __call__(self, node):
+ if node.fn_name:
+ return
+
+ expected_name = _get_expected_function_name(node, self._decorate)
+ if not expected_name is None:
+ raise LintWarning(node, name=expected_name)
+
+@lookfor_class(tok.FUNCTION)
+class FunctionNameMismatch(object):
+ def __init__(self, conf):
+ self._decorate = conf['decorate_function_name_warning']
+
+ def __call__(self, node):
+ if not node.fn_name:
+ return
+
+ expected_name = _get_expected_function_name(node, self._decorate)
+ if expected_name is None:
+ return
+
+ if expected_name != node.fn_name:
+ raise LintWarning(node, fn_name=node.fn_name,
+ prop_name=expected_name)
+
+@lookfor()
+def mismatch_ctrl_comments(node):
+ pass
+
+@lookfor()
+def redeclared_var(node):
+ pass
+
+@lookfor()
+def undeclared_identifier(node):
+ pass
+
+@lookfor()
+def jsl_cc_not_understood(node):
+ pass
+
+@lookfor()
+def nested_comment(node):
+ pass
+
+@lookfor()
+def legacy_cc_not_understood(node):
+ pass
+
+@lookfor()
+def var_hides_arg(node):
+ pass
+
+@lookfor()
+def duplicate_formal(node):
+ pass
+
+@lookfor(*_ALL_TOKENS)
+def missing_semicolon(node):
+ if node.no_semi:
+ if not _get_assigned_lambda(node):
+ raise LintWarning(node)
+
+@lookfor(*_ALL_TOKENS)
+def missing_semicolon_for_lambda(node):
+ if node.no_semi:
+ # spidermonkey sometimes returns incorrect positions for var
+ # statements, so use the position of the lambda instead.
+ lambda_ = _get_assigned_lambda(node)
+ if lambda_:
+ raise LintWarning(lambda_)
+
+@lookfor()
+def ambiguous_newline(node):
+ pass
+
+@lookfor()
+def missing_option_explicit(node):
+ pass
+
+@lookfor()
+def partial_option_explicit(node):
+ pass
+
+@lookfor()
+def dup_option_explicit(node):
+ pass
+
+def make_visitors(conf):
+ all_visitors = list(_visitors)
+ for kind, klass in _visitor_classes:
+ all_visitors.append((kind, klass(conf=conf)))
+
+ visitors = {}
+ for kind, func in all_visitors:
+ try:
+ visitors[kind].append(func)
+ except KeyError:
+ visitors[kind] = [func]
+ return visitors
+
Deleted: trunk/javascriptlint/warnings.py
===================================================================
--- trunk/javascriptlint/warnings.py 2014-10-27 22:25:27 UTC (rev 354)
+++ trunk/javascriptlint/warnings.py 2016-12-23 20:45:40 UTC (rev 355)
@@ -1,814 +0,0 @@
-# vim: ts=4 sw=4 expandtab
-""" This module contains all the warnings. To add a new warning, define a
-function. Its name should be in lowercase and words should be separated by
-underscores.
-
-The function should be decorated with a @lookfor call specifying the nodes it
-wants to examine. The node names may be in the tok.KIND or (tok.KIND, op.OPCODE)
-format. To report a warning, the function should raise a LintWarning exception.
-
-For example:
-
- @lookfor(tok.NODEKIND, (tok.NODEKIND, op.OPCODE))
- def warning_name(node):
- if questionable:
- raise LintWarning(node)
-"""
-import itertools
-import re
-import sys
-import types
-
-import util
-import visitation
-
-from jsengine.parser import kind as tok
-from jsengine.parser import op
-
-_ALL_TOKENS = tok.__dict__.values()
-
-def _get_assigned_lambda(node):
- """ Given a node "x = function() {}", returns "function() {}".
- """
- value = None
- if node.kind == tok.SEMI:
- assign_node, = node.kids
- if assign_node and assign_node.kind == tok.ASSIGN:
- ignored, value = assign_node.kids
- elif node.kind == tok.VAR:
- variables = node.kids
- if variables:
- value, = variables[-1].kids
-
- if value and value.kind == tok.FUNCTION and value.opcode == op.ANONFUNOBJ:
- return value
-
-# TODO: document inspect, node:opcode, etc
-
-warnings = {
- 'comparison_type_conv': 'comparisons against null, 0, true, false, or an empty string allowing implicit type conversion (use === or !==)',
- 'default_not_at_end': 'the default case is not at the end of the switch statement',
- 'duplicate_case_in_switch': 'duplicate case in switch statement',
- 'missing_default_case': 'missing default case in switch statement',
- 'with_statement': 'with statement hides undeclared variables; use temporary variable instead',
- 'useless_comparison': 'useless comparison; comparing identical expressions',
- 'use_of_label': 'use of label',
- 'misplaced_regex': 'regular expressions should be preceded by a left parenthesis, assignment, colon, or comma',
- 'assign_to_function_call': 'assignment to a function call',
- 'equal_as_assign': 'test for equality (==) mistyped as assignment (=)?',
- 'ambiguous_else_stmt': 'the else statement could be matched with one of multiple if statements (use curly braces to indicate intent',
- 'block_without_braces': 'block statement without curly braces',
- 'ambiguous_nested_stmt': 'block statements containing block statements should use curly braces to resolve ambiguity',
- 'inc_dec_within_stmt': 'increment (++) and decrement (--) operators used as part of greater statement',
- 'comma_separated_stmts': 'multiple statements separated by commas (use semicolons?)',
- 'empty_statement': 'empty statement or extra semicolon',
- 'missing_break': 'missing break statement',
- 'missing_break_for_last_case': 'missing break statement for last case in switch',
- 'multiple_plus_minus': 'unknown order of operations for successive plus (e.g. x+++y) or minus (e.g. x---y) signs',
- 'useless_assign': 'useless assignment',
- 'unreachable_code': 'unreachable code',
- 'meaningless_block': 'meaningless block; curly braces have no impact',
- 'useless_void': 'use of the void type may be unnecessary (void is always undefined)',
- 'parseint_missing_radix': 'parseInt missing radix parameter',
- 'leading_decimal_point': 'leading decimal point may indicate a number or an object member',
- 'trailing_decimal_point': 'trailing decimal point may indicate a number or an object member',
- 'octal_number': 'leading zeros make an octal number',
- 'trailing_comma': 'extra comma is not recommended in object initializers',
- 'trailing_comma_in_array': 'extra comma is not recommended in array initializers',
- 'useless_quotes': 'the quotation marks are unnecessary',
- 'mismatch_ctrl_comments': 'mismatched control comment; "ignore" and "end" control comments must have a one-to-one correspondence',
- 'redeclared_var': 'redeclaration of {name}',
- 'undeclared_identifier': 'undeclared identifier: {name}',
- 'unreferenced_argument': 'argument declared but never referenced: {name}',
- 'unreferenced_function': 'function is declared but never referenced: {name}',
- 'unreferenced_variable': 'variable is declared but never referenced: {name}',
- 'jsl_cc_not_understood': 'couldn\'t understand control comment using /*jsl:keyword*/ syntax',
- 'nested_comment': 'nested comment',
- 'legacy_cc_not_understood': 'couldn\'t understand control comment using /*@keyword@*/ syntax',
- 'var_hides_arg': 'variable {name} hides argument',
- 'identifier_hides_another': 'identifer {name} hides an identifier in a parent scope',
- 'duplicate_formal': 'duplicate formal argument {name}',
- 'missing_semicolon': 'missing semicolon',
- 'missing_semicolon_for_lambda': 'missing semicolon for lambda assignment',
- 'ambiguous_newline': 'unexpected end of line; it is ambiguous whether these lines are part of the same statement',
- 'missing_option_explicit': 'the "option explicit" control comment is missing',
- 'partial_option_explicit': 'the "option explicit" control comment, if used, must be in the first script tag',
- 'dup_option_explicit': 'duplicate "option explicit" control comment',
- 'invalid_fallthru': 'unexpected "fallthru" control comment',
- 'invalid_pass': 'unexpected "pass" control comment',
- 'want_assign_or_call': 'expected an assignment or function call',
- 'no_return_value': 'function {name} does not always return a value',
- 'anon_no_return_value': 'anonymous function does not always return value',
- 'unsupported_version': 'JavaScript {version} is not supported',
- 'incorrect_version': 'Expected /*jsl:content-type*/ control comment. The script was parsed with the wrong version.',
- 'for_in_missing_identifier': 'for..in should have identifier on left side',
- 'misplaced_function': 'unconventional use of function expression',
- 'function_name_missing': 'anonymous function should be named to match property name {name}',
- 'function_name_mismatch': 'function name {fn_name} does not match property name {prop_name}',
- 'trailing_whitespace': 'trailing whitespace',
-}
-
-errors = {
- 'e4x_deprecated': 'e4x is deprecated',
- 'semi_before_stmnt': 'missing semicolon before statement',
- 'syntax_error': 'syntax error',
- 'expected_tok': 'expected token: {token}',
- 'unexpected_char': 'unexpected character: {char}',
- 'unexpected_eof': 'unexpected end of file',
- 'io_error': '{error}',
-}
-
-def format_error(errname, **errargs):
- if errname in errors:
- errdesc = errors[errname]
- else:
- errdesc = warnings[errname]
-
- try:
- keyword = re.compile(r"{(\w+)}")
- errdesc = keyword.sub(lambda match: errargs[match.group(1)], errdesc)
- except (TypeError, KeyError):
- raise KeyError('Invalid keyword in error %s: %s' % (errname, errdesc))
- return errdesc
-
-_visitors = []
-def lookfor(*args):
- def decorate(fn):
- fn.warning = fn.func_name.rstrip('_')
- assert fn.warning in warnings, 'Missing warning description: %s' % fn.warning
-
- for arg in args:
- _visitors.append((arg, fn))
- return decorate
-
-_visitor_classes = []
-def lookfor_class(*args):
- def decorate(cls):
- # Convert the class name to camel case
- camelcase = re.sub('([A-Z])', r'_\1', cls.__name__).lower().lstrip('_')
-
- cls.warning = camelcase.rstrip('_')
- assert cls.warning in warnings, \
- 'Missing warning description: %s' % cls.warning
-
- for arg in args:
- _visitor_classes.append((arg, cls))
- return decorate
-
-class LintWarning(Exception):
- def __init__(self, node, **errargs):
- self.node = node
- self.errargs = errargs
-
-def _get_branch_in_for(node):
- " Returns None if this is not one of the branches in a 'for' "
- if node.parent and node.parent.kind == tok.RESERVED and \
- node.parent.parent.kind == tok.FOR:
- return node.node_index
- return None
-
-def _get_exit_points(node):
- """ Returns a set of exit points, which may be:
- * None, indicating a code path with no specific exit point.
- * a node of type tok.BREAK, tok.RETURN, tok.THROW.
- """
- if node.kind == tok.LC:
- exit_points = set([None])
- for kid in node.kids:
- if kid:
- # Merge in the kid's exit points.
- kid_exit_points = _get_exit_points(kid)
- exit_points |= kid_exit_points
-
- # Stop if the kid always exits.
- if not None in kid_exit_points:
- exit_points.discard(None)
- break
- elif node.kind == tok.IF:
- # Only if both branches have an exit point
- cond_, if_, else_ = node.kids
- exit_points = _get_exit_points(if_)
- if else_:
- exit_points |= _get_exit_points(else_)
- else:
- exit_points.add(None)
- elif node.kind == tok.SWITCH:
- exit_points = set([None])
-
- switch_has_default = False
- switch_has_final_fallthru = True
-
- switch_var, switch_stmts = node.kids
- for node in switch_stmts.kids:
- case_val, case_stmt = node.kids
- case_exit_points = _get_exit_points(case_stmt)
- switch_has_default = switch_has_default or node.kind == tok.DEFAULT
- switch_has_final_fallthru = None in case_exit_points
- exit_points |= case_exit_points
-
- # Correct the "None" exit point.
- exit_points.remove(None)
-
- # Convert "break" into None
- def break_to_none(node):
- if node and node.kind != tok.BREAK:
- return node
- exit_points = set(map(break_to_none, exit_points))
-
- # Check if the switch had a default case
- if not switch_has_default:
- exit_points.add(None)
-
- # Check if the final case statement had a fallthru
- if switch_has_final_fallthru:
- exit_points.add(None)
- elif node.kind == tok.BREAK:
- exit_points = set([node])
- elif node.kind == tok.CONTINUE:
- exit_points = set([node])
- elif node.kind == tok.WITH:
- exit_points = _get_exit_points(node.kids[-1])
- elif node.kind == tok.RETURN:
- exit_points = set([node])
- elif node.kind == tok.THROW:
- exit_points = set([node])
- elif node.kind == tok.TRY:
- try_, catch_, finally_ = node.kids
-
- exit_points = _get_exit_points(try_)
-
- if catch_:
- assert catch_.kind == tok.RESERVED
- catch_, = catch_.kids
- assert catch_.kind == tok.LEXICALSCOPE
- catch_, = catch_.kids
- assert catch_.kind == tok.CATCH
- ignored, ignored, catch_ = catch_.kids
- assert catch_.kind == tok.LC
-
- exit_points |= _get_exit_points(catch_)
-
- if finally_:
- finally_exit_points = _get_exit_points(finally_)
- if None in finally_exit_points:
- # The finally statement does not add a missing exit point.
- finally_exit_points.remove(None)
- else:
- # If the finally statement always returns, the other
- # exit points are irrelevant.
- if None in exit_points:
- exit_points.remove(None)
-
- exit_points |= finally_exit_points
-
- else:
- exit_points = set([None])
-
- return exit_points
-
-def _loop_has_unreachable_condition(node):
- for exit_point in _get_exit_points(node):
- if exit_point is None:
- return False
- if exit_point.kind == tok.CONTINUE:
- return False
- return True
-
-@lookfor((tok.EQOP, op.EQ))
-def comparison_type_conv(node):
- for kid in node.kids:
- if kid.kind == tok.PRIMARY and kid.opcode in (op.NULL, op.TRUE, op.FALSE):
- raise LintWarning(kid)
- if kid.kind == tok.NUMBER and not kid.dval:
- raise LintWarning(kid)
- if kid.kind == tok.STRING and not kid.atom:
- raise LintWarning(kid)
-
-@lookfor(tok.DEFAULT)
-def default_not_at_end(node):
- siblings = node.parent.kids
- if node.node_index != len(siblings)-1:
- raise LintWarning(siblings[node.node_index+1])
-
-@lookfor(tok.CASE)
-def duplicate_case_in_switch(node):
- # Only look at previous siblings
- siblings = node.parent.kids
- siblings = siblings[:node.node_index]
- # Compare values (first kid)
- node_value = node.kids[0]
- for sibling in siblings:
- if sibling.kind == tok.CASE:
- sibling_value = sibling.kids[0]
- if node_value.is_equivalent(sibling_value, True):
- raise LintWarning(node)
-
-@lookfor(tok.SWITCH)
-def missing_default_case(node):
- value, cases = node.kids
- for case in cases.kids:
- if case.kind == tok.DEFAULT:
- return
- raise LintWarning(node)
-
-@lookfor(tok.WITH)
-def with_statement(node):
- raise LintWarning(node)
-
-@lookfor(tok.EQOP, tok.RELOP)
-def useless_comparison(node):
- for lvalue, rvalue in itertools.combinations(node.kids, 2):
- if lvalue.is_equivalent(rvalue):
- raise LintWarning(node)
-
-@lookfor((tok.COLON, op.NAME))
-def use_of_label(node):
- raise LintWarning(node)
-
-@lookfor((tok.OBJECT, op.REGEXP))
-def misplaced_regex(node):
- if node.parent.kind == tok.NAME and node.parent.opcode == op.SETNAME:
- return # Allow in var statements
- if node.parent.kind == tok.ASSIGN and node.parent.opcode == op.NOP:
- return # Allow in assigns
- if node.parent.kind == tok.COLON and node.parent.parent.kind == tok.RC:
- return # Allow in object literals
- if node.parent.kind == tok.LP and node.parent.opcode == op.CALL:
- return # Allow in parameters
- if node.parent.kind == tok.DOT and node.parent.opcode == op.GETPROP:
- return # Allow in /re/.property
- if node.parent.kind == tok.RETURN:
- return # Allow for return values
- raise LintWarning(node)
-
-@lookfor(tok.ASSIGN)
-def assign_to_function_call(node):
- kid = node.kids[0]
- # Unpack parens.
- while kid.kind == tok.RP:
- kid, = kid.kids
- if kid.kind == tok.LP:
- raise LintWarning(node)
-
-@lookfor((tok.ASSIGN, None))
-def equal_as_assign(node):
- # Allow in VAR statements.
- if node.parent.parent and node.parent.parent.kind == tok.VAR:
- return
-
- if not node.parent.kind in (tok.SEMI, tok.RESERVED, tok.RP, tok.COMMA,
- tok.ASSIGN):
- raise LintWarning(node)
-
-@lookfor(tok.IF)
-def ambiguous_else_stmt(node):
- # Only examine this node if it has an else statement.
- condition, if_, else_ = node.kids
- if not else_:
- return
-
- tmp = node
- while tmp:
- # Curly braces always clarify if statements.
- if tmp.kind == tok.LC:
- return
- # Else is only ambiguous in the first branch of an if statement.
- if tmp.parent.kind == tok.IF and tmp.node_index == 1:
- raise LintWarning(else_)
- tmp = tmp.parent
-
-@lookfor(tok.IF, tok.WHILE, tok.DO, tok.FOR, tok.WITH)
-def block_without_braces(node):
- if node.kids[1].kind != tok.LC:
- raise LintWarning(node.kids[1])
-
-_block_nodes = (tok.IF, tok.WHILE, tok.DO, tok.FOR, tok.WITH)
-@lookfor(*_block_nodes)
-def ambiguous_nested_stmt(node):
- # Ignore "else if"
- if node.kind == tok.IF and node.node_index == 2 and node.parent.kind == tok.IF:
- return
-
- # If the parent is a block, it means a block statement
- # was inside a block statement without clarifying curlies.
- # (Otherwise, the node type would be tok.LC.)
- if node.parent.kind in _block_nodes:
- raise LintWarning(node)
-
-@lookfor(tok.INC, tok.DEC)
-def inc_dec_within_stmt(node):
- if node.parent.kind == tok.SEMI:
- return
-
- # Allow within the third part of the "for"
- tmp = node
- while tmp and tmp.parent and tmp.parent.kind == tok.COMMA:
- tmp = tmp.parent
- if tmp and tmp.node_index == 2 and \
- tmp.parent.kind == tok.RESERVED and \
- tmp.parent.parent.kind == tok.FOR:
- return
-
- raise LintWarning(node)
-
-@lookfor(tok.COMMA)
-def comma_separated_stmts(node):
- # Allow within the first and third part of "for(;;)"
- if _get_branch_in_for(node) in (0, 2):
- return
- # This is an array
- if node.parent.kind == tok.RB:
- return
- raise LintWarning(node)
-
-@lookfor(tok.SEMI)
-def empty_statement(node):
- if not node.kids[0]:
- raise LintWarning(node)
-@lookfor(tok.LC)
-def empty_statement_(node):
- if node.kids:
- return
- # Ignore the outermost block.
- if not node.parent:
- return
- # Some empty blocks are meaningful.
- if node.parent.kind in (tok.CATCH, tok.CASE, tok.DEFAULT, tok.SWITCH, tok.FUNCTION):
- return
- raise LintWarning(node)
-
-@lookfor(tok.CASE, tok.DEFAULT)
-def missing_break(node):
- # The last item is handled separately
- if node.node_index == len(node.parent.kids)-1:
- return
- case_contents = node.kids[1]
- assert case_contents.kind == tok.LC
- # Ignore empty case statements
- if not case_contents.kids:
- return
- if None in _get_exit_points(case_contents):
- # Show the warning on the *next* node.
- raise LintWarning(node.parent.kids[node.node_index+1])
-
-@lookfor(tok.CASE, tok.DEFAULT)
-def missing_break_for_last_case(node):
- if node.node_index < len(node.parent.kids)-1:
- return
- case_contents = node.kids[1]
- assert case_contents.kind == tok.LC
- if None in _get_exit_points(case_contents):
- raise LintWarning(node)
-
-@lookfor(tok.INC)
-def multiple_plus_minus(node):
- if node.node_index == 0 and node.parent.kind == tok.PLUS:
- raise LintWarning(node)
-@lookfor(tok.DEC)
-def multiple_plus_minus_(node):
- if node.node_index == 0 and node.parent.kind == tok.MINUS:
- raise LintWarning(node)
-
-@lookfor((tok.NAME, op.SETNAME))
-def useless_assign(node):
- if node.parent.kind == tok.ASSIGN:
- assert node.node_index == 0
- value = node.parent.kids[1]
- elif node.parent.kind == tok.VAR:
- value = node.kids[0]
- if value and value.kind == tok.NAME and node.atom == value.atom:
- raise LintWarning(node)
-
-@lookfor(tok.BREAK, tok.CONTINUE, tok.RETURN, tok.THROW)
-def unreachable_code(node):
- if node.parent.kind == tok.LC:
- for sibling in node.parent.kids[node.node_index+1:]:
- if sibling.kind == tok.VAR:
- # Look for a variable assignment
- for variable in sibling.kids:
- value, = variable.kids
- if value:
- raise LintWarning(value)
- elif sibling.kind == tok.FUNCTION:
- # Functions are always declared.
- pass
- else:
- raise LintWarning(sibling)
-
-@lookfor(tok.FOR)
-def unreachable_code_(node):
- # Warn if the for loop always exits.
- preamble, code = node.kids
- if preamble.kind == tok.RESERVED:
- pre, condition, post = preamble.kids
- if post:
- if _loop_has_unreachable_condition(code):
- raise LintWarning(post)
-
-@lookfor(tok.DO)
-def unreachable_code__(node):
- # Warn if the do..while loop always exits.
- code, condition = node.kids
- if _loop_has_unreachable_condition(code):
- raise LintWarning(condition)
-
-#TODO: @lookfor(tok.IF)
-def meaningless_block(node):
- condition, if_, else_ = node.kids
- if condition.kind == tok.PRIMARY and condition.opcode in (op.TRUE, op.FALSE, op.NULL):
- raise LintWarning(condition)
-#TODO: @lookfor(tok.WHILE)
-def meaningless_blocK_(node):
- condition = node.kids[0]
- if condition.kind == tok.PRIMARY and condition.opcode in (op.FALSE, op.NULL):
- raise LintWarning(condition)
-@lookfor(tok.LC)
-def meaningless_block__(node):
- if node.parent and node.parent.kind == tok.LC:
- raise LintWarning(node)
-
-@lookfor((tok.UNARYOP, op.VOID))
-def useless_void(node):
- raise LintWarning(node)
-
-@lookfor((tok.LP, op.CALL))
-def parseint_missing_radix(node):
- if node.kids[0].kind == tok.NAME and node.kids[0].atom == 'parseInt' and len(node.kids) <= 2:
- raise LintWarning(node)
-
-@lookfor(tok.NUMBER)
-def leading_decimal_point(node):
- if node.atom.startswith('.'):
- raise LintWarning(node)
-
-@lookfor(tok.NUMBER)
-def trailing_decimal_point(node):
- if node.parent.kind == tok.DOT:
- raise LintWarning(node)
- if node.atom.endswith('.'):
- raise LintWarning(node)
-
-_octal_regexp = re.compile('^0[0-9]')
-@lookfor(tok.NUMBER)
-def octal_number(node):
- if _octal_regexp.match(node.atom):
- raise LintWarning(node)
-
-@lookfor(tok.RC)
-def trailing_comma(node):
- if node.end_comma:
- # Warn on the last value in the dictionary.
- last_item = node.kids[-1]
- assert last_item.kind == tok.COLON
- key, value = last_item.kids
- raise LintWarning(value)
-
-@lookfor(tok.RB)
-def trailing_comma_in_array(node):
- if node.end_comma:
- # Warn on the last value in the array.
- raise LintWarning(node.kids[-1])
-
-@lookfor(tok.STRING)
-def useless_quotes(node):
- if node.node_index == 0 and node.parent.kind == tok.COLON:
- # Only warn if the quotes could safely be removed.
- if util.isidentifier(node.atom):
- raise LintWarning(node)
-
-@lookfor(tok.SEMI)
-def want_assign_or_call(node):
- child, = node.kids
- # Ignore empty statements.
- if not child:
- return
- # NOTE: Don't handle comma-separated statements.
- if child.kind in (tok.ASSIGN, tok.INC, tok.DEC, tok.DELETE, tok.COMMA):
- return
- if child.kind == tok.YIELD:
- return
- # Ignore function calls.
- if child.kind == tok.LP and child.opcode == op.CALL:
- return
- # Allow new function() { } as statements.
- if child.kind == tok.NEW:
- # The first kid is the constructor, followed by its arguments.
- grandchild = child.kids[0]
- if grandchild.kind == tok.FUNCTION:
- return
- raise LintWarning(child)
-
-def _check_return_value(node):
- name = node.fn_name or '(anonymous function)'
-
- def is_return_with_val(node):
- return node and node.kind == tok.RETURN and node.kids[0]
- def is_return_without_val(node):
- return node and node.kind == tok.RETURN and not node.kids[0]
-
- node, = node.kids
- assert node.kind == tok.LC
-
- exit_points = _get_exit_points(node)
- if filter(is_return_with_val, exit_points):
- # If the function returns a value, find all returns without a value.
- returns = filter(is_return_without_val, exit_points)
- returns.sort(key=lambda node: node.start_offset)
- if returns:
- raise LintWarning(returns[0], name=name)
- # Warn if the function sometimes exits naturally.
- if None in exit_points:
- raise LintWarning(node, name=name)
-
-@lookfor(tok.FUNCTION)
-def no_return_value(node):
- if node.fn_name:
- _check_return_value(node)
-
-@lookfor(tok.FUNCTION)
-def anon_no_return_value(node):
- if not node.fn_name:
- _check_return_value(node)
-
-@lookfor((tok.FOR, op.FORIN))
-def for_in_missing_identifier(node):
- assert node.kids[0].kind == tok.IN
- left, right = node.kids[0].kids
- if not left.kind in (tok.VAR, tok.NAME):
- raise LintWarning(left)
-
-@lookfor(tok.FUNCTION)
-def misplaced_function(node):
- # Ignore function statements.
- if node.opcode in (None, op.CLOSURE):
- return
-
- # Ignore parens.
- parent = node.parent
- while parent.kind == tok.RP:
- parent = parent.parent
-
- # Allow x = x || ...
- if parent.kind == tok.OR and len(parent.kids) == 2 and \
- node is parent.kids[-1]:
- parent = parent.parent
-
- if parent.kind == tok.NAME and parent.opcode == op.SETNAME:
- return # Allow in var statements
- if parent.kind == tok.ASSIGN and parent.opcode == op.NOP:
- return # Allow in assigns
- if parent.kind == tok.COLON and parent.parent.kind == tok.RC:
- return # Allow in object literals
- if parent.kind == tok.LP and parent.opcode in (op.CALL, op.SETCALL):
- return # Allow in parameters
- if parent.kind == tok.RETURN:
- return # Allow for return values
- if parent.kind == tok.NEW:
- return # Allow as constructors
- raise LintWarning(node)
-
-def _get_function_property_name(node):
- # Ignore function statements.
- if node.opcode in (None, op.CLOSURE):
- return
-
- # Ignore parens.
- parent = node.parent
- while parent.kind == tok.RP:
- parent = parent.parent
-
- # Allow x = x || ...
- if parent.kind == tok.OR and len(parent.kids) == 2 and \
- node is parent.kids[-1]:
- parent = parent.parent
-
- # Var assignment.
- if parent.kind == tok.NAME and parent.opcode == op.SETNAME:
- return parent.atom
-
- # Assignment.
- if parent.kind == tok.ASSIGN and parent.opcode == op.NOP:
- if parent.kids[0].kind == tok.NAME and \
- parent.kids[0].opcode == op.SETNAME:
- return parent.kids[0].atom
- if parent.kids[0].kind == tok.DOT and \
- parent.kids[0].opcode == op.SETPROP:
- return parent.kids[0].atom
- return '<error>'
-
- # Object literal.
- if parent.kind == tok.COLON and parent.parent.kind == tok.RC:
- return parent.kids[0].atom
-
-def _get_expected_function_name(node, decorate):
- name = _get_function_property_name(node)
- if name and decorate:
- return '__%s' % name
- return name
-
-@lookfor_class(tok.FUNCTION)
-class FunctionNameMissing(object):
- def __init__(self, conf):
- self._decorate = conf['decorate_function_name_warning']
-
- def __call__(self, node):
- if node.fn_name:
- return
-
- expected_name = _get_expected_function_name(node, self._decorate)
- if not expected_name is None:
- raise LintWarning(node, name=expected_name)
-
-@lookfor_class(tok.FUNCTION)
-class FunctionNameMismatch(object):
- def __init__(self, conf):
- self._decorate = conf['decorate_function_name_warning']
-
- def __call__(self, node):
- if not node.fn_name:
- return
-
- expected_name = _get_expected_function_name(node, self._decorate)
- if expected_name is None:
- return
-
- if expected_name != node.fn_name:
- raise LintWarning(node, fn_name=node.fn_name,
- prop_name=expected_name)
-
-@lookfor()
-def mismatch_ctrl_comments(node):
- pass
-
-@lookfor()
-def redeclared_var(node):
- pass
-
-@lookfor()
-def undeclared_identifier(node):
- pass
-
-@lookfor()
-def jsl_cc_not_understood(node):
- pass
-
-@lookfor()
-def nested_comment(node):
- pass
-
-@lookfor()
-def legacy_cc_not_understood(node):
- pass
-
-@lookfor()
-def var_hides_arg(node):
- pass
-
-@lookfor()
-def duplicate_formal(node):
- pass
-
-@lookfor(*_ALL_TOKENS)
-def missing_semicolon(node):
- if node.no_semi:
- if not _get_assigned_lambda(node):
- raise LintWarning(node)
-
-@lookfor(*_ALL_TOKENS)
-def missing_semicolon_for_lambda(node):
- if node.no_semi:
- # spidermonkey sometimes returns incorrect positions for var
- # statements, so use the position of the lambda instead.
- lambd...
[truncated message content] |