Thread: [javascriptlint-commit] SF.net SVN: javascriptlint:[258] trunk/javascriptlint
Status: Beta
Brought to you by:
matthiasmiller
|
From: <mat...@us...> - 2009-10-03 18:10:14
|
Revision: 258
http://javascriptlint.svn.sourceforge.net/javascriptlint/?rev=258&view=rev
Author: matthiasmiller
Date: 2009-10-03 18:10:05 +0000 (Sat, 03 Oct 2009)
Log Message:
-----------
Implement --help:conf.
Modified Paths:
--------------
trunk/javascriptlint/conf.py
trunk/javascriptlint/jsl.py
trunk/javascriptlint/warnings.py
Modified: trunk/javascriptlint/conf.py
===================================================================
--- trunk/javascriptlint/conf.py 2009-10-03 17:39:40 UTC (rev 257)
+++ trunk/javascriptlint/conf.py 2009-10-03 18:10:05 UTC (rev 258)
@@ -1,8 +1,90 @@
# vim: ts=4 sw=4 expandtab
import os
+import unittest
import warnings
+def _getwarningsconf():
+ lines = []
+ for name in sorted(warnings.warnings.keys()):
+ message = warnings.warnings[name]
+ sign = '+'
+ if name == 'block_without_braces':
+ sign = '-'
+ assert len(name) < 29
+ lines.append(sign + name.ljust(29) + '# ' + message)
+ return '\n'.join(lines)
+
+DEFAULT_CONF = """\
+#
+# Configuration File for JavaScript Lint %(version)s
+# Developed by Matthias Miller (http://www.JavaScriptLint.com)
+#
+# This configuration file can be used to lint a collection of scripts, or to enable
+# or disable warnings for scripts that are linted via the command line.
+#
+
+### Warnings
+# Enable or disable warnings based on requirements.
+# Use "+WarningName" to display or "-WarningName" to suppress.
+#
+%(warnings)s
+
+
+### Output format
+# Customize the format of the error message.
+# __FILE__ indicates current file path
+# __FILENAME__ indicates current file name
+# __LINE__ indicates current line
+# __ERROR__ indicates error message
+#
+# Visual Studio syntax (default):
++output-format __FILE__(__LINE__): __ERROR__
+# Alternative syntax:
+#+output-format __FILE__:__LINE__: __ERROR__
+
+
+### Context
+# Show the in-line position of the error.
+# Use "+context" to display or "-context" to suppress.
+#
++context
+
+
+### Control Comments
+# Both JavaScript Lint and the JScript interpreter confuse each other with the syntax for
+# the /*@keyword@*/ control comments and JScript conditional comments. (The latter is
+# enabled in JScript with @cc_on@). The /*jsl:keyword*/ syntax is preferred for this reason,
+# although legacy control comments are enabled by default for backward compatibility.
+#
+-legacy_control_comments
+
+
+### Defining identifiers
+# By default, "option explicit" is enabled on a per-file basis.
+# To enable this for all files, use "+always_use_option_explicit"
++always_use_option_explicit
+
+# Define certain identifiers of which the lint is not aware.
+# (Use this in conjunction with the "undeclared identifier" warning.)
+#
+# Common uses for webpages might be:
+#+define window
+#+define document
+
+
+### Files
+# Specify which files to lint
+# Use "+recurse" to enable recursion (disabled by default).
+# To add a set of files, use "+process FileName", "+process Folder\Path\*.js",
+# or "+process Folder\Path\*.htm".
+#
+""" % {
+ 'version': '', # TODO
+ 'warnings': _getwarningsconf(),
+}
+
+
class ConfError(Exception):
def __init__(self, error):
Exception.__init__(self, error)
@@ -13,6 +95,12 @@
wants_parm = False
wants_dir = False
+class DeprecatedSetting(Setting):
+ wants_parm = False
+ value = None
+ def load(self, enabled):
+ raise ConfError, 'This setting is deprecated.'
+
class BooleanSetting(Setting):
wants_parm = False
def __init__(self, default):
@@ -54,14 +142,13 @@
recurse = BooleanSetting(False)
self._settings = {
'recurse': recurse,
- 'show_context': BooleanSetting(False),
'output-format': StringSetting('__FILE__(__LINE__): __ERROR__'),
- 'lambda_assign_requires_semicolon': BooleanSetting(False),
- 'legacy_control_comments': BooleanSetting(True),
- 'jscript_function_extensions': BooleanSetting(False),
- 'always_use_option_explicit': BooleanSetting(False),
+ 'lambda_assign_requires_semicolon': DeprecatedSetting(),
+ 'legacy_control_comments': BooleanSetting(False),
+ 'jscript_function_extensions': DeprecatedSetting(),
+ 'always_use_option_explicit': BooleanSetting(True),
'define': DeclareSetting(),
- 'context': BooleanSetting(False),
+ 'context': BooleanSetting(True),
'process': ProcessSetting(recurse),
# SpiderMonkey warnings
'no_return_value': BooleanSetting(True),
@@ -134,3 +221,14 @@
name = 'define'
return self._settings[name].value
+class TestConf(unittest.TestCase):
+ def testDefaultConf(self):
+ # Make sure the string version corresponds with the code.
+ fromstr = Conf()
+ fromstr.loadtext(DEFAULT_CONF)
+ fromcode = Conf()
+ settings = set(fromcode._settings.keys() + fromstr._settings.keys())
+ for setting in settings:
+ self.assertEquals(fromcode[setting], fromstr[setting],
+ 'Mismatched defaults for %s' % setting)
+
Modified: trunk/javascriptlint/jsl.py
===================================================================
--- trunk/javascriptlint/jsl.py 2009-10-03 17:39:40 UTC (rev 257)
+++ trunk/javascriptlint/jsl.py 2009-10-03 18:10:05 UTC (rev 258)
@@ -81,6 +81,8 @@
help="minimal output")
add("--verbose", dest="verbosity", action="store_const", const=2,
help="verbose output")
+ add("--help:conf", dest="showdefaultconf", action="store_true", default=False,
+ help="display the default configuration file")
parser.set_defaults(verbosity=1)
options, args = parser.parse_args()
@@ -88,6 +90,10 @@
parser.print_help()
sys.exit()
+ if options.showdefaultconf:
+ print conf.DEFAULT_CONF
+ sys.exit()
+
conf_ = conf.Conf()
if options.conf:
conf_.loadfile(options.conf)
@@ -98,7 +104,7 @@
if options.unittest:
suite = unittest.TestSuite();
- for module in [htmlparse, jsparse, util]:
+ for module in [conf, htmlparse, jsparse, util]:
suite.addTest(unittest.findTestCases(module))
runner = unittest.TextTestRunner(verbosity=options.verbosity)
Modified: trunk/javascriptlint/warnings.py
===================================================================
--- trunk/javascriptlint/warnings.py 2009-10-03 17:39:40 UTC (rev 257)
+++ trunk/javascriptlint/warnings.py 2009-10-03 18:10:05 UTC (rev 258)
@@ -82,7 +82,7 @@
'nested_comment': 'nested comment',
'legacy_cc_not_understood': 'couldn\'t understand control comment using /*@keyword@*/ syntax',
'var_hides_arg': 'variable {name} hides argument',
- 'duplicate_formal': 'TODO',
+ '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',
This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.
|
|
From: <mat...@us...> - 2009-10-22 05:09:32
|
Revision: 279
http://javascriptlint.svn.sourceforge.net/javascriptlint/?rev=279&view=rev
Author: matthiasmiller
Date: 2009-10-22 05:09:12 +0000 (Thu, 22 Oct 2009)
Log Message:
-----------
htmlparse should not have JavaScript-specific logic.
Modified Paths:
--------------
trunk/javascriptlint/htmlparse.py
trunk/javascriptlint/jsl.py
trunk/javascriptlint/lint.py
Modified: trunk/javascriptlint/htmlparse.py
===================================================================
--- trunk/javascriptlint/htmlparse.py 2009-10-13 01:29:52 UTC (rev 278)
+++ trunk/javascriptlint/htmlparse.py 2009-10-22 05:09:12 UTC (rev 279)
@@ -2,88 +2,50 @@
import HTMLParser
import unittest
-import jsparse
-
class _Parser(HTMLParser.HTMLParser):
def __init__(self):
HTMLParser.HTMLParser.__init__(self)
- self._node_positions = jsparse.NodePositions('')
- self._script = None
- self._scripts = []
+ self._tags = []
def handle_starttag(self, tag, attributes):
- if tag.lower() == 'script' and not self._script:
- offset = self._getoffset() + len(self.get_starttag_text())
- self._script = self._script or {
- 'start': offset,
- 'attributes': attributes
- }
+ if tag.lower() == 'script':
+ attr = dict(attributes)
+ self._tags.append({
+ 'type': 'start',
+ 'lineno': self.lineno,
+ 'offset': self.offset,
+ 'len': len(self.get_starttag_text()),
+ 'attr': attr
+ })
def handle_endtag(self, tag):
- if tag.lower() == 'script' and self._script:
- start = self._script['start']
- end = self._getoffset()
- script = self.rawdata[start:end]
- if jsparse.is_compilable_unit(script):
- attr = dict(self._script['attributes'])
- self._scripts.append({
- 'script': script,
- 'startoffset': start,
- 'endoffset': end,
- 'startpos': self._node_positions.from_offset(start),
- 'endpos': self._node_positions.from_offset(end),
- 'src': attr.get('src'),
- 'type': attr.get('type')
- })
- self._script = None
+ if tag.lower() == 'script':
+ self._tags.append({
+ 'type': 'end',
+ 'lineno': self.lineno,
+ 'offset': self.offset,
+ })
- def feed(self, data):
- self._node_positions = jsparse.NodePositions(self.rawdata + data)
- HTMLParser.HTMLParser.feed(self, data)
-
def unknown_decl(self, data):
# Ignore unknown declarations instead of raising an exception.
pass
- def getscripts(self):
- return self._scripts
+ def gettags(self):
+ return self._tags
- def _getnodepos(self):
- line, col = self.getpos()
- return jsparse.NodePos(line - 1, col)
-
- def _getoffset(self):
- return self._node_positions.to_offset(self._getnodepos())
-
-def findscripts(s):
+def findscripttags(s):
+ """ Note that the lineno is 1-based.
+ """
parser = _Parser()
parser.feed(s)
parser.close()
- return parser.getscripts()
+ return parser.gettags()
class TestHTMLParse(unittest.TestCase):
- def testFindScript(self):
- html = """
-<html><body>
-<script src=test.js></script>
-hi&b
-a<script><!--
-var s = '<script></script>';
---></script>
-ok&
-..</script>
-ok&
-</body>
-</html>
-"""
- scripts = [x['script'] for x in findscripts(html)]
- self.assertEquals(scripts, [
- "",
- "<!--\nvar s = '<script></script>';\n-->"
- ])
def testConditionalComments(self):
html = """
<!--[if IE]>This is Internet Explorer.<![endif]-->
<![if !IE]>This is not Internet Explorer<![endif]>
"""
- findscripts(html)
+ findscripttags(html)
+
Modified: trunk/javascriptlint/jsl.py
===================================================================
--- trunk/javascriptlint/jsl.py 2009-10-13 01:29:52 UTC (rev 278)
+++ trunk/javascriptlint/jsl.py 2009-10-22 05:09:12 UTC (rev 279)
@@ -104,7 +104,7 @@
if options.unittest:
suite = unittest.TestSuite();
- for module in [conf, htmlparse, jsparse, util]:
+ for module in [conf, htmlparse, jsparse, lint, util]:
suite.addTest(unittest.findTestCases(module))
runner = unittest.TextTestRunner(verbosity=options.verbosity)
Modified: trunk/javascriptlint/lint.py
===================================================================
--- trunk/javascriptlint/lint.py 2009-10-13 01:29:52 UTC (rev 278)
+++ trunk/javascriptlint/lint.py 2009-10-22 05:09:12 UTC (rev 279)
@@ -8,6 +8,7 @@
import jsparse
import visitation
import warnings
+import unittest
import util
from spidermonkey import tok, op
@@ -213,6 +214,45 @@
if global_:
return global_
+def _findhtmlscripts(contents):
+ starttag = None
+ nodepos = jsparse.NodePositions(contents)
+ for tag in htmlparse.findscripttags(contents):
+ if tag['type'] == 'start':
+ # Ignore nested start tags.
+ if not starttag:
+ starttag = tag
+ src = tag['attr'].get('src')
+ if src:
+ yield {
+ 'type': 'external',
+ 'src': src
+ }
+ elif tag['type'] == 'end':
+ if not starttag:
+ continue
+
+ # htmlparse returns 1-based line numbers. Calculate the
+ # position of the script's contents.
+ tagpos = jsparse.NodePos(starttag['lineno']-1, starttag['offset'])
+ tagoffset = nodepos.to_offset(tagpos)
+ startoffset = tagoffset + starttag['len']
+ startpos = nodepos.from_offset(startoffset)
+ endpos = jsparse.NodePos(tag['lineno']-1, tag['offset'])
+ endoffset = nodepos.to_offset(endpos)
+ script = contents[startoffset:endoffset]
+
+ if jsparse.is_compilable_unit(script):
+ starttag = None
+ if script.strip():
+ yield {
+ 'type': 'inline',
+ 'pos': startpos,
+ 'contents': script,
+ }
+ else:
+ assert False, 'Invalid internal tag type %s' % tag['type']
+
def lint_files(paths, lint_error, conf=conf.Conf()):
def lint_file(path, kind):
def import_script(import_path):
@@ -235,12 +275,15 @@
if kind == 'js':
script_parts.append((None, contents))
elif kind == 'html':
- for script in htmlparse.findscripts(contents):
- if script['src']:
+ for script in _findhtmlscripts(contents):
+ if script['type'] == 'external':
other = import_script(script['src'])
lint_cache[normpath].importscript(other)
- if script['script'].strip():
- script_parts.append((script['startpos'], script['script']))
+ elif script['type'] == 'inline':
+ script_parts.append((script['pos'], script['contents']))
+ else:
+ assert False, 'Invalid internal script type %s' % \
+ script['type']
else:
assert False, 'Unsupported file kind: %s' % kind
@@ -521,3 +564,24 @@
visitor(node)
+class TestLint(unittest.TestCase):
+ def testFindScript(self):
+ html = """
+<html><body>
+<script src=test.js></script>
+hi&b
+a<script><!--
+var s = '<script></script>';
+--></script>
+ok&
+..</script>
+ok&
+</body>
+</html>
+"""
+ scripts = [(x.get('src'), x.get('contents'))
+ for x in _findhtmlscripts(html)]
+ self.assertEquals(scripts, [
+ ('test.js', None),
+ (None, "<!--\nvar s = '<script></script>';\n-->")
+ ])
This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.
|
|
From: <mat...@us...> - 2010-04-23 20:38:12
|
Revision: 298
http://javascriptlint.svn.sourceforge.net/javascriptlint/?rev=298&view=rev
Author: matthiasmiller
Date: 2010-04-23 20:38:04 +0000 (Fri, 23 Apr 2010)
Log Message:
-----------
Add options to control whether to show header, file listings, and lint summary.
Modified Paths:
--------------
trunk/javascriptlint/jsl.py
trunk/javascriptlint/lint.py
Modified: trunk/javascriptlint/jsl.py
===================================================================
--- trunk/javascriptlint/jsl.py 2010-04-23 20:09:47 UTC (rev 297)
+++ trunk/javascriptlint/jsl.py 2010-04-23 20:38:04 UTC (rev 298)
@@ -24,12 +24,12 @@
script = util.readfile(path)
jsparse.dump_tree(script)
-def _lint(paths, conf_):
+def _lint(paths, conf_, printpaths):
def lint_error(path, line, col, errname, errdesc):
_lint_results['warnings'] = _lint_results['warnings'] + 1
print util.format_error(conf_['output-format'], path, line, col,
errname, errdesc)
- lint.lint_files(paths, lint_error, conf=conf_)
+ lint.lint_files(paths, lint_error, conf=conf_, printpaths=printpaths)
def _resolve_paths(path, recurse):
# Build a list of directories
@@ -46,6 +46,11 @@
# force an error to be thrown if no matching files were found.
return paths or [path]
+def printlogo():
+ # TODO: Print version number.
+ print "JavaScript Lint"
+ print "Developed by Matthias Miller (http://www.JavaScriptLint.com)"
+
def _profile_enabled(func, *args, **kwargs):
import tempfile
import hotshot
@@ -83,6 +88,12 @@
help="minimal output")
add("--verbose", dest="verbosity", action="store_const", const=2,
help="verbose output")
+ add("--nologo", dest="printlogo", action="store_false", default=True,
+ help="suppress version information")
+ add("--nofilelisting", dest="printlisting", action="store_false",
+ default=True, help="suppress file names")
+ add("--nosummary", dest="printsummary", action="store_false", default=True,
+ help="suppress lint summary")
add("--help:conf", dest="showdefaultconf", action="store_true", default=False,
help="display the default configuration file")
parser.set_defaults(verbosity=1)
@@ -96,6 +107,9 @@
print conf.DEFAULT_CONF
sys.exit()
+ if options.printlogo:
+ printlogo()
+
conf_ = conf.Conf()
if options.conf:
conf_.loadfile(options.conf)
@@ -125,8 +139,12 @@
if options.dump:
profile_func(_dump, paths)
else:
- profile_func(_lint, paths, conf_)
+ profile_func(_lint, paths, conf_, options.printlisting)
+ if options.printsummary:
+ print '\n%i error(s), %i warnings(s)' % (_lint_results['errors'],
+ _lint_results['warnings'])
+
if _lint_results['errors']:
sys.exit(3)
if _lint_results['warnings']:
Modified: trunk/javascriptlint/lint.py
===================================================================
--- trunk/javascriptlint/lint.py 2010-04-23 20:09:47 UTC (rev 297)
+++ trunk/javascriptlint/lint.py 2010-04-23 20:38:04 UTC (rev 298)
@@ -267,7 +267,7 @@
else:
assert False, 'Invalid internal tag type %s' % tag['type']
-def lint_files(paths, lint_error, conf=conf.Conf()):
+def lint_files(paths, lint_error, conf=conf.Conf(), printpaths=True):
def lint_file(path, kind):
def import_script(import_path):
# The user can specify paths using backslashes (such as when
@@ -281,7 +281,8 @@
normpath = util.normpath(path)
if normpath in lint_cache:
return lint_cache[normpath]
- print normpath
+ if printpaths:
+ print normpath
contents = util.readfile(path)
lint_cache[normpath] = _Script()
This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.
|
|
From: <mat...@us...> - 2011-04-07 00:07:12
|
Revision: 302
http://javascriptlint.svn.sourceforge.net/javascriptlint/?rev=302&view=rev
Author: matthiasmiller
Date: 2011-04-07 00:07:05 +0000 (Thu, 07 Apr 2011)
Log Message:
-----------
Move file system functions into separate module in preparation for online lint.
Modified Paths:
--------------
trunk/javascriptlint/conf.py
trunk/javascriptlint/lint.py
trunk/javascriptlint/util.py
Added Paths:
-----------
trunk/javascriptlint/fs.py
Modified: trunk/javascriptlint/conf.py
===================================================================
--- trunk/javascriptlint/conf.py 2010-10-03 20:18:10 UTC (rev 301)
+++ trunk/javascriptlint/conf.py 2011-04-07 00:07:05 UTC (rev 302)
@@ -2,6 +2,7 @@
import os
import unittest
+import fs
import util
import warnings
@@ -186,7 +187,7 @@
def loadfile(self, path):
path = os.path.abspath(path)
- conf = open(path, 'r').read()
+ conf = fs.readfile(path)
try:
self.loadtext(conf, dir=os.path.dirname(path))
except ConfError, error:
Added: trunk/javascriptlint/fs.py
===================================================================
--- trunk/javascriptlint/fs.py (rev 0)
+++ trunk/javascriptlint/fs.py 2011-04-07 00:07:05 UTC (rev 302)
@@ -0,0 +1,17 @@
+# vim: ts=4 sw=4 expandtab
+import codecs
+import os
+
+def readfile(path):
+ file = codecs.open(path, 'r', 'utf-8')
+ contents = file.read()
+ if contents and contents[0] == unicode(codecs.BOM_UTF8, 'utf8'):
+ contents = contents[1:]
+ return contents
+
+def normpath(path):
+ path = os.path.abspath(path)
+ path = os.path.normcase(path)
+ path = os.path.normpath(path)
+ return path
+
Property changes on: trunk/javascriptlint/fs.py
___________________________________________________________________
Added: svn:eol-style
+ native
Modified: trunk/javascriptlint/lint.py
===================================================================
--- trunk/javascriptlint/lint.py 2010-10-03 20:18:10 UTC (rev 301)
+++ trunk/javascriptlint/lint.py 2011-04-07 00:07:05 UTC (rev 302)
@@ -4,6 +4,7 @@
import re
import conf
+import fs
import htmlparse
import jsparse
import visitation
@@ -295,12 +296,12 @@
def _lint_error(*args):
return lint_error(normpath, *args)
- normpath = util.normpath(path)
+ normpath = fs.normpath(path)
if normpath in lint_cache:
return lint_cache[normpath]
if printpaths:
print normpath
- contents = util.readfile(path)
+ contents = fs.readfile(path)
lint_cache[normpath] = _Script()
script_parts = []
Modified: trunk/javascriptlint/util.py
===================================================================
--- trunk/javascriptlint/util.py 2010-10-03 20:18:10 UTC (rev 301)
+++ trunk/javascriptlint/util.py 2011-04-07 00:07:05 UTC (rev 302)
@@ -1,6 +1,5 @@
# vim: ts=4 sw=4 expandtab
import cgi
-import codecs
import os.path
import re
import unittest
@@ -101,19 +100,6 @@
return re.sub(regexp, lambda match: replacements[match.group(0)],
formatted_error)
-def readfile(path):
- file = codecs.open(path, 'r', 'utf-8')
- contents = file.read()
- if contents and contents[0] == unicode(codecs.BOM_UTF8, 'utf8'):
- contents = contents[1:]
- return contents
-
-def normpath(path):
- path = os.path.abspath(path)
- path = os.path.normcase(path)
- path = os.path.normpath(path)
- return path
-
class TestUtil(unittest.TestCase):
def testIdentifier(self):
assert not isidentifier('')
This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.
|
|
From: <mat...@us...> - 2013-10-03 19:48:28
|
Revision: 332
http://sourceforge.net/p/javascriptlint/code/332
Author: matthiasmiller
Date: 2013-10-03 19:48:25 +0000 (Thu, 03 Oct 2013)
Log Message:
-----------
Gracefully handle IO errors.
Modified Paths:
--------------
trunk/javascriptlint/lint.py
trunk/javascriptlint/warnings.py
Modified: trunk/javascriptlint/lint.py
===================================================================
--- trunk/javascriptlint/lint.py 2013-10-03 19:39:07 UTC (rev 331)
+++ trunk/javascriptlint/lint.py 2013-10-03 19:48:25 UTC (rev 332)
@@ -302,8 +302,13 @@
return lint_cache[normpath]
if printpaths:
print normpath
- contents = fs.readfile(path, encoding)
+
lint_cache[normpath] = _Script()
+ try:
+ contents = fs.readfile(path, encoding)
+ except IOError, error:
+ _lint_error(0, 0, 'io_error', unicode(error))
+ return lint_cache[normpath]
script_parts = []
if kind == 'js':
Modified: trunk/javascriptlint/warnings.py
===================================================================
--- trunk/javascriptlint/warnings.py 2013-10-03 19:39:07 UTC (rev 331)
+++ trunk/javascriptlint/warnings.py 2013-10-03 19:48:25 UTC (rev 332)
@@ -114,6 +114,7 @@
'expected_tok': 'expected token: {token}',
'unexpected_char': 'unexpected character: {char}',
'unexpected_eof': 'unexpected end of file',
+ 'io_error': '{error}',
}
def format_error(errname, **errargs):
This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.
|
|
From: <mat...@us...> - 2014-10-27 22:23:33
|
Revision: 353
http://sourceforge.net/p/javascriptlint/code/353
Author: matthiasmiller
Date: 2014-10-27 22:23:26 +0000 (Mon, 27 Oct 2014)
Log Message:
-----------
Format configuration errors correctly.
Modified Paths:
--------------
trunk/javascriptlint/conf.py
trunk/javascriptlint/jsl.py
Modified: trunk/javascriptlint/conf.py
===================================================================
--- trunk/javascriptlint/conf.py 2014-10-27 19:42:54 UTC (rev 352)
+++ trunk/javascriptlint/conf.py 2014-10-27 22:23:26 UTC (rev 353)
@@ -244,7 +244,10 @@
parm = line[len(name):].lstrip()
# Load the setting
- setting = self._settings[name]
+ try:
+ setting = self._settings[name]
+ except KeyError:
+ raise ConfError('Unrecognized setting: %s' % name)
args = {
'enabled': enabled
}
Modified: trunk/javascriptlint/jsl.py
===================================================================
--- trunk/javascriptlint/jsl.py 2014-10-27 19:42:54 UTC (rev 352)
+++ trunk/javascriptlint/jsl.py 2014-10-27 22:23:26 UTC (rev 353)
@@ -2,6 +2,7 @@
# vim: ts=4 sw=4 expandtab
import codecs
import fnmatch
+import functools
import glob
import os
import sys
@@ -27,12 +28,14 @@
script = fs.readfile(path, encoding)
jsparse.dump_tree(script)
+def _lint_warning(conf_, path, line, col, errname, errdesc):
+ _lint_results['warnings'] = _lint_results['warnings'] + 1
+ print util.format_error(conf_['output-format'], path, line, col,
+ errname, errdesc)
+
def _lint(paths, conf_, printpaths, encoding):
- def lint_error(path, line, col, errname, errdesc):
- _lint_results['warnings'] = _lint_results['warnings'] + 1
- print util.format_error(conf_['output-format'], path, line, col,
- errname, errdesc)
- lint.lint_files(paths, lint_error, encoding, conf=conf_, printpaths=printpaths)
+ lint.lint_files(paths, functools.partial(_lint_warning, conf_), encoding,
+ conf=conf_, printpaths=printpaths)
def _resolve_paths(path, recurse):
# Build a list of directories
@@ -116,7 +119,11 @@
conf_ = conf.Conf()
if options.conf:
- conf_.loadfile(options.conf)
+ try:
+ conf_.loadfile(options.conf)
+ except conf.ConfError, error:
+ _lint_warning(conf_, error.path, error.lineno, 0, 'conf_error',
+ unicode(error))
profile_func = _profile_disabled
if options.profile:
This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.
|
|
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_...
[truncated message content] |
|
From: <mat...@us...> - 2016-12-23 22:27:22
|
Revision: 359
http://sourceforge.net/p/javascriptlint/code/359
Author: matthiasmiller
Date: 2016-12-23 22:27:20 +0000 (Fri, 23 Dec 2016)
Log Message:
-----------
Remove more unused code.
Modified Paths:
--------------
trunk/javascriptlint/lint.py
trunk/javascriptlint/lintwarnings.py
Removed Paths:
-------------
trunk/javascriptlint/visitation.py
Modified: trunk/javascriptlint/lint.py
===================================================================
--- trunk/javascriptlint/lint.py 2016-12-23 21:56:37 UTC (rev 358)
+++ trunk/javascriptlint/lint.py 2016-12-23 22:27:20 UTC (rev 359)
@@ -8,7 +8,6 @@
import htmlparse
import jsparse
import lintwarnings
-import visitation
import unittest
import util
@@ -478,7 +477,7 @@
visitors[event][kind] = [_getreporter(callback, report) for callback in callbacks]
# Push the scope/variable checks.
- visitation.make_visitors(visitors, [_get_scope_checks(script_cache.scope, report)])
+ _get_scope_checks(visitors, script_cache.scope, report)
# kickoff!
_lint_node(root, visitors)
@@ -582,43 +581,44 @@
else:
scope.add_declaration(name, node, type_)
-def _get_scope_checks(scope, report):
+def _get_scope_checks(visitors, scope, report):
scopes = [scope]
- class scope_checks:
- """ This is a non-standard visitation class to track scopes. The
- docstring is unused since this class never throws lint errors.
- """
- @visitation.visit('push', tok.NAME)
- def _name(self, node):
- if node.node_index == 0 and node.parent.kind == tok.COLON and node.parent.parent.kind == tok.RC:
- return # left side of object literal
- if node.parent.kind == tok.VAR:
- _warn_or_declare(scopes[-1], node.atom, 'var', node, report)
- return
- if node.parent.kind == tok.CATCH:
- scopes[-1].add_declaration(node.atom, node, 'var')
- scopes[-1].add_reference(node.atom, node)
+ def _visit(event, *args):
+ def _decorate(fn):
+ for arg in args:
+ visitors.setdefault(event, {}).setdefault(arg, []).append(fn)
+ return fn
+ return _decorate
- @visitation.visit('push', tok.FUNCTION)
- def _push_func(self, node):
- if node.opcode in (None, op.CLOSURE) and node.fn_name:
- _warn_or_declare(scopes[-1], node.fn_name, 'function', node, report)
- self._push_scope(node)
- for var_name in node.fn_args:
- if scopes[-1].has_property(var_name.atom):
- report(var_name, 'duplicate_formal', name=var_name.atom)
- scopes[-1].add_declaration(var_name.atom, var_name, 'arg')
+ @_visit('push', tok.NAME)
+ def _push_name(node):
+ if node.node_index == 0 and node.parent.kind == tok.COLON and node.parent.parent.kind == tok.RC:
+ return # left side of object literal
+ if node.parent.kind == tok.VAR:
+ _warn_or_declare(scopes[-1], node.atom, 'var', node, report)
+ return
+ if node.parent.kind == tok.CATCH:
+ scopes[-1].add_declaration(node.atom, node, 'var')
+ scopes[-1].add_reference(node.atom, node)
- @visitation.visit('push', tok.LEXICALSCOPE, tok.WITH)
- def _push_scope(self, node):
- scopes.append(scopes[-1].add_scope(node))
+ @_visit('push', tok.FUNCTION)
+ def _push_func(node):
+ if node.opcode in (None, op.CLOSURE) and node.fn_name:
+ _warn_or_declare(scopes[-1], node.fn_name, 'function', node, report)
+ _push_scope(node)
+ for var_name in node.fn_args:
+ if scopes[-1].has_property(var_name.atom):
+ report(var_name, 'duplicate_formal', name=var_name.atom)
+ scopes[-1].add_declaration(var_name.atom, var_name, 'arg')
- @visitation.visit('pop', tok.FUNCTION, tok.LEXICALSCOPE, tok.WITH)
- def _pop_scope(self, node):
- scopes.pop()
+ @_visit('push', tok.LEXICALSCOPE, tok.WITH)
+ def _push_scope(node):
+ scopes.append(scopes[-1].add_scope(node))
- return scope_checks
+ @_visit('pop', tok.FUNCTION, tok.LEXICALSCOPE, tok.WITH)
+ def _pop_scope(node):
+ scopes.pop()
def _lint_node(node, visitors):
Modified: trunk/javascriptlint/lintwarnings.py
===================================================================
--- trunk/javascriptlint/lintwarnings.py 2016-12-23 21:56:37 UTC (rev 358)
+++ trunk/javascriptlint/lintwarnings.py 2016-12-23 22:27:20 UTC (rev 359)
@@ -20,7 +20,6 @@
import types
import util
-import visitation
from jsengine.parser import kind as tok
from jsengine.parser import op
Deleted: trunk/javascriptlint/visitation.py
===================================================================
--- trunk/javascriptlint/visitation.py 2016-12-23 21:56:37 UTC (rev 358)
+++ trunk/javascriptlint/visitation.py 2016-12-23 22:27:20 UTC (rev 359)
@@ -1,52 +0,0 @@
-# vim: ts=4 sw=4 expandtab
-""" This is an abstract module for visiting specific nodes. This is useed to
-traverse the tree to generate warnings.
-"""
-
-def visit(event, *args):
- """ This decorator is used to indicate which nodes the function should
- examine. The function should accept (self, node) and return the relevant
- node or None. """
- def _decorate(fn):
- fn._visit_event = event
- fn._visit_nodes = args
- return fn
- return _decorate
-
-def make_visitors(visitors, klasses):
- """ Searches klasses for all member functions decorated with @visit and
- fills a dictionary that looks like:
- visitors = {
- 'event_name': {
- 'node_type' : [func1, func2]
- }
- }
- """
- assert isinstance(visitors, dict)
-
- # Intantiate an instance of each class
- for klass in klasses:
- if klass.__name__.lower() != klass.__name__:
- raise ValueError('class names must be lowercase')
- if not klass.__doc__:
- raise ValueError('missing docstring on class %s' % klass.__name__)
-
- # Look for functions with the "_visit_nodes" property.
- visitor = klass()
- for func in [getattr(visitor, name) for name in dir(visitor)]:
- event_visitors = None
- for node_kind in getattr(func, '_visit_nodes', ()):
- # Group visitors by event (e.g. push vs pop)
- if not event_visitors:
- try:
- event_visitors = visitors[func._visit_event]
- except KeyError:
- event_visitors = visitors[func._visit_event] = {}
-
- # Map from node_kind to the function
- try:
- event_visitors[node_kind].append(func)
- except KeyError:
- event_visitors[node_kind] = [func]
- return visitors
-
This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.
|
|
From: <mat...@us...> - 2016-12-23 23:46:57
|
Revision: 364
http://sourceforge.net/p/javascriptlint/code/364
Author: matthiasmiller
Date: 2016-12-23 23:46:55 +0000 (Fri, 23 Dec 2016)
Log Message:
-----------
e4x_deprecated should be a warning, not an error.
Modified Paths:
--------------
trunk/javascriptlint/jsparse.py
trunk/javascriptlint/lint.py
trunk/javascriptlint/lintwarnings.py
Modified: trunk/javascriptlint/jsparse.py
===================================================================
--- trunk/javascriptlint/jsparse.py 2016-12-23 23:38:49 UTC (rev 363)
+++ trunk/javascriptlint/jsparse.py 2016-12-23 23:46:55 UTC (rev 364)
@@ -58,8 +58,6 @@
assert not start_offset is None
jsversion = jsversion or JSVersion.default()
assert isvalidversion(jsversion), jsversion
- if jsversion.e4x:
- error_callback(start_offset, 'e4x_deprecated', {})
return jsengine.parser.parse(script, jsversion.version,
error_callback, start_offset)
Modified: trunk/javascriptlint/lint.py
===================================================================
--- trunk/javascriptlint/lint.py 2016-12-23 23:38:49 UTC (rev 363)
+++ trunk/javascriptlint/lint.py 2016-12-23 23:46:55 UTC (rev 364)
@@ -417,6 +417,10 @@
version=jsversion.version)
return
+ if jsversion.e4x:
+ report_lint(None, 'e4x_deprecated',
+ jsversionnode.start_offset if jsversionnode else script_offset)
+
root = jsparse.parse(script, jsversion, parse_error, script_offset)
if not root:
# Report errors and quit.
Modified: trunk/javascriptlint/lintwarnings.py
===================================================================
--- trunk/javascriptlint/lintwarnings.py 2016-12-23 23:38:49 UTC (rev 363)
+++ trunk/javascriptlint/lintwarnings.py 2016-12-23 23:46:55 UTC (rev 364)
@@ -103,10 +103,10 @@
'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',
+ 'e4x_deprecated': 'e4x is deprecated',
}
errors = {
- 'e4x_deprecated': 'e4x is deprecated',
'semi_before_stmnt': 'missing semicolon before statement',
'syntax_error': 'syntax error',
'expected_tok': 'expected token: {token}',
This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.
|