Thread: [javascriptlint-commit] SF.net SVN: javascriptlint: [161] trunk/pyjsl
Status: Beta
Brought to you by:
matthiasmiller
|
From: <mat...@us...> - 2008-03-04 01:34:41
|
Revision: 161
http://javascriptlint.svn.sourceforge.net/javascriptlint/?rev=161&view=rev
Author: matthiasmiller
Date: 2008-03-03 17:34:39 -0800 (Mon, 03 Mar 2008)
Log Message:
-----------
rename pyjsl.parse to pyjsl.htmlparse
Modified Paths:
--------------
trunk/pyjsl/lint.py
Added Paths:
-----------
trunk/pyjsl/jsparse.py
Removed Paths:
-------------
trunk/pyjsl/parse.py
Copied: trunk/pyjsl/jsparse.py (from rev 157, trunk/pyjsl/parse.py)
===================================================================
--- trunk/pyjsl/jsparse.py (rev 0)
+++ trunk/pyjsl/jsparse.py 2008-03-04 01:34:39 UTC (rev 161)
@@ -0,0 +1,319 @@
+#!/usr/bin/python
+""" Parses a script into nodes. """
+import bisect
+import re
+import unittest
+
+import pyspidermonkey
+from pyspidermonkey import tok, op
+
+_tok_names = dict(zip(
+ [getattr(tok, prop) for prop in dir(tok)],
+ ['tok.%s' % prop for prop in dir(tok)]
+))
+
+class NodePos():
+ def __init__(self, line, col):
+ self.line = line
+ self.col = col
+ def __cmp__(self, other):
+ if self.line < other.line:
+ return -1
+ if self.line > other.line:
+ return 1
+ if self.col < other.col:
+ return -1
+ if self.col > other.col:
+ return 1
+ return 0
+ def __str__(self):
+ return '(line %i, col %i)' % (self.line+1, self.col+1)
+
+class NodePositions():
+ " Given a string, allows [x] lookups for NodePos line and column numbers."
+ def __init__(self, text):
+ # Find the length of each line and incrementally sum all of the lengths
+ # to determine the ending position of each line.
+ self._lines = text.splitlines(True)
+ lines = [0] + [len(x) for x in self._lines]
+ for x in range(1, len(lines)):
+ lines[x] += lines[x-1]
+ self._line_offsets = lines
+ def from_offset(self, offset):
+ line = bisect.bisect(self._line_offsets, offset)-1
+ col = offset - self._line_offsets[line]
+ return NodePos(line, col)
+ def to_offset(self, pos):
+ offset = self._line_offsets[pos.line] + pos.col
+ assert offset <= self._line_offsets[pos.line+1] # out-of-bounds col num
+ return offset
+ def text(self, start, end):
+ assert start <= end
+ # Trim the ending first in case it's a single line.
+ lines = self._lines[start.line:end.line+1]
+ lines[-1] = lines[-1][:end.col+1]
+ lines[0] = lines[0][start.col:]
+ return ''.join(lines)
+
+class NodeRanges():
+ def __init__(self):
+ self._offsets = []
+ def add(self, start, end):
+ i = bisect.bisect_left(self._offsets, start)
+ if i % 2 == 1:
+ i -= 1
+ start = self._offsets[i]
+
+ end = end + 1
+ j = bisect.bisect_left(self._offsets, end)
+ if j % 2 == 1:
+ end = self._offsets[j]
+ j += 1
+
+ self._offsets[i:j] = [start,end]
+ def has(self, pos):
+ return bisect.bisect_right(self._offsets, pos) % 2 == 1
+
+class _Node():
+ def add_child(self, node):
+ if node:
+ node.node_index = len(self.kids)
+ node.parent = self
+ self.kids.append(node)
+
+ def start_pos(self):
+ try:
+ return self._start_pos
+ except AttributeError:
+ self._start_pos = NodePos(self._start_line, self._start_col)
+ return self._start_pos
+
+ def end_pos(self):
+ try:
+ return self._end_pos
+ except AttributeError:
+ self._end_pos = NodePos(self._end_line, self._end_col)
+ return self._end_pos
+
+ def __str__(self):
+ kind = self.kind
+ if not kind:
+ kind = '(none)'
+ return '%s>%s' % (_tok_names[kind], str(self.kids))
+
+ def is_equivalent(self, other, are_functions_equiv=False):
+ if not other:
+ return False
+
+ # Bail out for functions
+ if not are_functions_equiv:
+ if self.kind == tok.FUNCTION:
+ return False
+ if self.kind == tok.LP and self.opcode == op.CALL:
+ return False
+
+ if self.kind != other.kind:
+ return False
+ if self.opcode != other.opcode:
+ return False
+
+ # Check atoms on names, properties, and string constants
+ if self.kind in (tok.NAME, tok.DOT, tok.STRING) and self.atom != other.atom:
+ return False
+
+ # Check values on numbers
+ if self.kind == tok.NUMBER and self.dval != other.dval:
+ return False
+
+ # Compare child nodes
+ if len(self.kids) != len(other.kids):
+ return False
+ for i in range(0, len(self.kids)):
+ # Watch for dead nodes
+ if not self.kids[i]:
+ if not other.kids[i]: return True
+ else: return False
+ if not self.kids[i].is_equivalent(other.kids[i]):
+ return False
+
+ return True
+
+def _parse_comments(script, root, node_positions, ignore_ranges):
+ pos = 0
+ single_line_re = r"//[^\r\n]*"
+ multi_line_re = r"/\*(.*?)\*/"
+ full_re = "(%s)|(%s)" % (single_line_re, multi_line_re)
+ comment_re = re.compile(full_re, re.DOTALL)
+
+ comments = []
+ while True:
+ match = comment_re.search(script, pos)
+ if not match:
+ return comments
+
+ # Get the comment text
+ comment_text = script[match.start():match.end()]
+ if comment_text.startswith('/*'):
+ comment_text = comment_text[2:-2]
+ opcode = 'JSOP_C_COMMENT'
+ else:
+ comment_text = comment_text[2:]
+ opcode = 'JSOP_CPP_COMMENT'
+ opcode = opcode[5:].lower()
+
+ start_offset = match.start()+1
+ end_offset = match.end()
+
+ # Make sure it doesn't start in a string or regexp
+ if not ignore_ranges.has(start_offset):
+ start_pos = node_positions.from_offset(start_offset)
+ end_pos = node_positions.from_offset(end_offset)
+ kwargs = {
+ 'type': 'COMMENT',
+ 'atom': comment_text,
+ 'opcode': opcode,
+ '_start_line': start_pos.line,
+ '_start_col': start_pos.col,
+ '_end_line': end_pos.line,
+ '_end_col': end_pos.col,
+ 'parent': None,
+ 'kids': [],
+ 'node_index': None
+ }
+ comment_node = _Node()
+ comment_node.__dict__.update(kwargs)
+ comments.append(comment_node)
+ pos = match.end()
+ else:
+ pos = match.start()+1
+
+def parse(script, error_callback):
+ def _wrapped_callback(line, col, msg):
+ assert msg.startswith('JSMSG_')
+ msg = msg[6:].lower()
+ error_callback(line, col, msg)
+
+ positions = NodePositions(script)
+
+ roots = []
+ nodes = []
+ comment_ignore_ranges = NodeRanges()
+ def process(node):
+ if node.kind == tok.NUMBER:
+ node.atom = positions.text(node.start_pos(), node.end_pos())
+ elif node.kind == tok.STRING or \
+ (node.kind == tok.OBJECT and node.opcode == op.REGEXP):
+ start_offset = positions.to_offset(node.start_pos())
+ end_offset = positions.to_offset(node.end_pos())
+ comment_ignore_ranges.add(start_offset, end_offset)
+ for kid in node.kids:
+ if kid:
+ process(kid)
+ def pop():
+ nodes.pop()
+
+ roots = pyspidermonkey.traverse(script, _Node, _wrapped_callback)
+ assert len(roots) == 1
+ root_node = roots[0]
+ process(root_node)
+
+ comments = _parse_comments(script, root_node, positions, comment_ignore_ranges)
+ return root_node, comments
+
+def _dump_node(node, depth=0):
+ print '. '*depth,
+ if node is None:
+ print '(none)'
+ else:
+ print '%s\t%s, %s' % (_tok_names[node.kind], node.start_pos(), node.end_pos())
+ for node in node.kids:
+ _dump_node(node, depth+1)
+
+def dump_tree(script):
+ def error_callback(line, col, msg):
+ print '(%i, %i): %s', (line, col, msg)
+ node, comments = parse(script, error_callback)
+ _dump_node(node)
+
+class TestComments(unittest.TestCase):
+ def _test(self, script, expected_comments):
+ root, comments = parse(script, lambda line, col, msg: None)
+ encountered_comments = [node.atom for node in comments]
+ self.assertEquals(encountered_comments, list(expected_comments))
+ def testSimpleComments(self):
+ self._test('re = /\//g', ())
+ self._test('re = /\///g', ())
+ self._test('re = /\////g', ('g',))
+ def testCComments(self):
+ self._test('/*a*//*b*/', ('a', 'b'))
+ self._test('/*a\r\na*//*b\r\nb*/', ('a\r\na', 'b\r\nb'))
+ self._test('a//*b*/c', ('*b*/c',))
+ self._test('a///*b*/c', ('/*b*/c',))
+ self._test('a/*//*/;', ('//',))
+ self._test('a/*b*/+/*c*/d', ('b', 'c'))
+
+class TestNodePositions(unittest.TestCase):
+ def _test(self, text, expected_lines, expected_cols):
+ # Get a NodePos list
+ positions = NodePositions(text)
+ positions = [positions.from_offset(i) for i in range(0, len(text))]
+ encountered_lines = ''.join([str(x.line) for x in positions])
+ encountered_cols = ''.join([str(x.col) for x in positions])
+ self.assertEquals(encountered_lines, expected_lines.replace(' ', ''))
+ self.assertEquals(encountered_cols, expected_cols.replace(' ', ''))
+ def testSimple(self):
+ self._test(
+ 'abc\r\ndef\nghi\n\nj',
+ '0000 0 1111 2222 3 4',
+ '0123 4 0123 0123 0 0'
+ )
+ self._test(
+ '\rabc',
+ '0 111',
+ '0 012'
+ )
+ def testText(self):
+ pos = NodePositions('abc\r\ndef\n\nghi')
+ self.assertEquals(pos.text(NodePos(0, 0), NodePos(0, 0)), 'a')
+ self.assertEquals(pos.text(NodePos(0, 0), NodePos(0, 2)), 'abc')
+ self.assertEquals(pos.text(NodePos(0, 2), NodePos(1, 2)), 'c\r\ndef')
+ def testOffset(self):
+ pos = NodePositions('abc\r\ndef\n\nghi')
+ self.assertEquals(pos.to_offset(NodePos(0, 2)), 2)
+ self.assertEquals(pos.to_offset(NodePos(1, 0)), 5)
+ self.assertEquals(pos.to_offset(NodePos(3, 1)), 11)
+
+class TestNodeRanges(unittest.TestCase):
+ def testAdd(self):
+ r = NodeRanges()
+ r.add(5, 10)
+ self.assertEquals(r._offsets, [5,11])
+ r.add(15, 20)
+ self.assertEquals(r._offsets, [5,11,15,21])
+ r.add(21,22)
+ self.assertEquals(r._offsets, [5,11,15,23])
+ r.add(4,5)
+ self.assertEquals(r._offsets, [4,11,15,23])
+ r.add(9,11)
+ self.assertEquals(r._offsets, [4,12,15,23])
+ r.add(10,20)
+ self.assertEquals(r._offsets, [4,23])
+ r.add(4,22)
+ self.assertEquals(r._offsets, [4,23])
+ r.add(30,30)
+ self.assertEquals(r._offsets, [4,23,30,31])
+ def testHas(self):
+ r = NodeRanges()
+ r.add(5, 10)
+ r.add(15, 15)
+ assert not r.has(4)
+ assert r.has(5)
+ assert r.has(6)
+ assert r.has(9)
+ assert r.has(10)
+ assert not r.has(14)
+ assert r.has(15)
+ assert not r.has(16)
+if __name__ == '__main__':
+ unittest.main()
+
Modified: trunk/pyjsl/lint.py
===================================================================
--- trunk/pyjsl/lint.py 2008-03-03 22:53:12 UTC (rev 160)
+++ trunk/pyjsl/lint.py 2008-03-04 01:34:39 UTC (rev 161)
@@ -3,7 +3,7 @@
import re
import conf
-import parse
+import jsparse
import visitation
import warnings
import util
@@ -149,7 +149,7 @@
def _lint_script(script, script_cache, lint_error, conf, import_callback):
def parse_error(row, col, msg):
if not msg in ('redeclared_var', 'var_hides_arg'):
- parse_errors.append((parse.NodePos(row, col), msg))
+ parse_errors.append((jsparse.NodePos(row, col), msg))
def report(node, errname):
_report(node.start_pos(), errname, True)
@@ -169,7 +169,7 @@
return lint_error(pos.line, pos.col, errname)
parse_errors = []
- root, comments = parse.parse(script, parse_error)
+ root, comments = jsparse.parse(script, parse_error)
ignores = []
start_ignore = None
declares = []
Deleted: trunk/pyjsl/parse.py
===================================================================
--- trunk/pyjsl/parse.py 2008-03-03 22:53:12 UTC (rev 160)
+++ trunk/pyjsl/parse.py 2008-03-04 01:34:39 UTC (rev 161)
@@ -1,321 +0,0 @@
-#!/usr/bin/python
-""" Parses a script into nodes. """
-import bisect
-import re
-import unittest
-
-import pyspidermonkey
-from pyspidermonkey import tok, op
-
-_tok_names = dict(zip(
- [getattr(tok, prop) for prop in dir(tok)],
- ['tok.%s' % prop for prop in dir(tok)]
-))
-_op_names = dict(zip(
- [getattr(op, prop) for prop in dir(op)],
- ['op.%s' % prop for prop in dir(op)]
-))
-
-class NodePos():
- def __init__(self, line, col):
- self.line = line
- self.col = col
- def __cmp__(self, other):
- if self.line < other.line:
- return -1
- if self.line > other.line:
- return 1
- if self.col < other.col:
- return -1
- if self.col > other.col:
- return 1
- return 0
- def __str__(self):
- return '(line %i, col %i)' % (self.line+1, self.col+1)
-
-class NodePositions():
- " Given a string, allows [x] lookups for NodePos line and column numbers."
- def __init__(self, text):
- # Find the length of each line and incrementally sum all of the lengths
- # to determine the ending position of each line.
- self._lines = text.splitlines(True)
- lines = [0] + [len(x) for x in self._lines]
- for x in range(1, len(lines)):
- lines[x] += lines[x-1]
- self._line_offsets = lines
- def from_offset(self, offset):
- line = bisect.bisect(self._line_offsets, offset)-1
- col = offset - self._line_offsets[line]
- return NodePos(line, col)
- def to_offset(self, pos):
- offset = self._line_offsets[pos.line] + pos.col
- assert offset <= self._line_offsets[pos.line+1] # out-of-bounds col num
- return offset
- def text(self, start, end):
- assert start <= end
- # Trim the ending first in case it's a single line.
- lines = self._lines[start.line:end.line+1]
- lines[-1] = lines[-1][:end.col+1]
- lines[0] = lines[0][start.col:]
- return ''.join(lines)
-
-class NodeRanges():
- def __init__(self):
- self._offsets = []
- def add(self, start, end):
- i = bisect.bisect_left(self._offsets, start)
- if i % 2 == 1:
- i -= 1
- start = self._offsets[i]
-
- end = end + 1
- j = bisect.bisect_left(self._offsets, end)
- if j % 2 == 1:
- end = self._offsets[j]
- j += 1
-
- self._offsets[i:j] = [start,end]
- def has(self, pos):
- return bisect.bisect_right(self._offsets, pos) % 2 == 1
-
-class _Node():
- def add_child(self, node):
- if node:
- node.node_index = len(self.kids)
- node.parent = self
- self.kids.append(node)
-
- def start_pos(self):
- try:
- return self._start_pos
- except AttributeError:
- self._start_pos = NodePos(self._start_line, self._start_col)
- return self._start_pos
-
- def end_pos(self):
- try:
- return self._end_pos
- except AttributeError:
- self._end_pos = NodePos(self._end_line, self._end_col)
- return self._end_pos
-
- def __str__(self):
- kind = self.kind
- if not kind:
- kind = '(none)'
- return '%s>%s' % (_tok_names[kind], str(self.kids))
-
- def is_equivalent(self, other, are_functions_equiv=False):
- if not other:
- return False
-
- # Bail out for functions
- if not are_functions_equiv:
- if self.kind == tok.FUNCTION:
- return False
- if self.kind == tok.LP and self.opcode == op.CALL:
- return False
-
- if self.kind != other.kind:
- return False
- if self.opcode != other.opcode:
- return False
-
- # Check atoms on names, properties, and string constants
- if self.kind in (tok.NAME, tok.DOT, tok.STRING) and self.atom != other.atom:
- return False
-
- # Check values on numbers
- if self.kind == tok.NUMBER and self.dval != other.dval:
- return False
-
- # Compare child nodes
- if len(self.kids) != len(other.kids):
- return False
- for i in range(0, len(self.kids)):
- # Watch for dead nodes
- if not self.kids[i]:
- if not other.kids[i]: return True
- else: return False
- if not self.kids[i].is_equivalent(other.kids[i]):
- return False
-
- return True
-
-def _parse_comments(script, root, node_positions, ignore_ranges):
- pos = 0
- single_line_re = r"//[^\r\n]*"
- multi_line_re = r"/\*(.*?)\*/"
- full_re = "(%s)|(%s)" % (single_line_re, multi_line_re)
- comment_re = re.compile(full_re, re.DOTALL)
-
- comments = []
- while True:
- match = comment_re.search(script, pos)
- if not match:
- return comments
-
- # Get the comment text
- comment_text = script[match.start():match.end()]
- if comment_text.startswith('/*'):
- comment_text = comment_text[2:-2]
- opcode = 'JSOP_C_COMMENT'
- else:
- comment_text = comment_text[2:]
- opcode = 'JSOP_CPP_COMMENT'
- opcode = opcode[5:].lower()
-
- start_offset = match.start()+1
- end_offset = match.end()
-
- # Make sure it doesn't start in a string or regexp
- if not ignore_ranges.has(start_offset):
- start_pos = node_positions.from_offset(start_offset)
- end_pos = node_positions.from_offset(end_offset)
- kwargs = {
- 'type': 'COMMENT',
- 'atom': comment_text,
- 'opcode': opcode,
- '_start_line': start_pos.line,
- '_start_col': start_pos.col,
- '_end_line': end_pos.line,
- '_end_col': end_pos.col,
- 'parent': None,
- 'kids': [],
- 'node_index': None
- }
- comment_node = _Node()
- comment_node.__dict__.update(kwargs)
- comments.append(comment_node)
- pos = match.end()
- else:
- pos = match.start()+1
-
-def parse(script, error_callback):
- def _wrapped_callback(line, col, msg):
- assert msg.startswith('JSMSG_')
- msg = msg[6:].lower()
- error_callback(line, col, msg)
-
- positions = NodePositions(script)
-
- roots = []
- nodes = []
- comment_ignore_ranges = NodeRanges()
- def process(node):
- if node.kind == tok.NUMBER:
- node.atom = positions.text(node.start_pos(), node.end_pos())
- elif node.kind == tok.STRING or \
- (node.kind == tok.OBJECT and node.opcode == op.REGEXP):
- start_offset = positions.to_offset(node.start_pos())
- end_offset = positions.to_offset(node.end_pos())
- comment_ignore_ranges.add(start_offset, end_offset)
- for kid in node.kids:
- if kid:
- process(kid)
-
- roots = pyspidermonkey.traverse(script, _Node, _wrapped_callback)
- assert len(roots) == 1
- root_node = roots[0]
- process(root_node)
-
- comments = _parse_comments(script, root_node, positions, comment_ignore_ranges)
- return root_node, comments
-
-def _dump_node(node, depth=0):
- print '. '*depth,
- if node is None:
- print '(none)'
- else:
- print '%s, %s\tfrom %s to %s' % (_tok_names[node.kind], _op_names[node.opcode], node.start_pos(), node.end_pos())
- for node in node.kids:
- _dump_node(node, depth+1)
-
-def dump_tree(script):
- def error_callback(line, col, msg):
- print '(%i, %i): %s', (line, col, msg)
- node, comments = parse(script, error_callback)
- _dump_node(node)
-
-class TestComments(unittest.TestCase):
- def _test(self, script, expected_comments):
- root, comments = parse(script, lambda line, col, msg: None)
- encountered_comments = [node.atom for node in comments]
- self.assertEquals(encountered_comments, list(expected_comments))
- def testSimpleComments(self):
- self._test('re = /\//g', ())
- self._test('re = /\///g', ())
- self._test('re = /\////g', ('g',))
- def testCComments(self):
- self._test('/*a*//*b*/', ('a', 'b'))
- self._test('/*a\r\na*//*b\r\nb*/', ('a\r\na', 'b\r\nb'))
- self._test('a//*b*/c', ('*b*/c',))
- self._test('a///*b*/c', ('/*b*/c',))
- self._test('a/*//*/;', ('//',))
- self._test('a/*b*/+/*c*/d', ('b', 'c'))
-
-class TestNodePositions(unittest.TestCase):
- def _test(self, text, expected_lines, expected_cols):
- # Get a NodePos list
- positions = NodePositions(text)
- positions = [positions.from_offset(i) for i in range(0, len(text))]
- encountered_lines = ''.join([str(x.line) for x in positions])
- encountered_cols = ''.join([str(x.col) for x in positions])
- self.assertEquals(encountered_lines, expected_lines.replace(' ', ''))
- self.assertEquals(encountered_cols, expected_cols.replace(' ', ''))
- def testSimple(self):
- self._test(
- 'abc\r\ndef\nghi\n\nj',
- '0000 0 1111 2222 3 4',
- '0123 4 0123 0123 0 0'
- )
- self._test(
- '\rabc',
- '0 111',
- '0 012'
- )
- def testText(self):
- pos = NodePositions('abc\r\ndef\n\nghi')
- self.assertEquals(pos.text(NodePos(0, 0), NodePos(0, 0)), 'a')
- self.assertEquals(pos.text(NodePos(0, 0), NodePos(0, 2)), 'abc')
- self.assertEquals(pos.text(NodePos(0, 2), NodePos(1, 2)), 'c\r\ndef')
- def testOffset(self):
- pos = NodePositions('abc\r\ndef\n\nghi')
- self.assertEquals(pos.to_offset(NodePos(0, 2)), 2)
- self.assertEquals(pos.to_offset(NodePos(1, 0)), 5)
- self.assertEquals(pos.to_offset(NodePos(3, 1)), 11)
-
-class TestNodeRanges(unittest.TestCase):
- def testAdd(self):
- r = NodeRanges()
- r.add(5, 10)
- self.assertEquals(r._offsets, [5,11])
- r.add(15, 20)
- self.assertEquals(r._offsets, [5,11,15,21])
- r.add(21,22)
- self.assertEquals(r._offsets, [5,11,15,23])
- r.add(4,5)
- self.assertEquals(r._offsets, [4,11,15,23])
- r.add(9,11)
- self.assertEquals(r._offsets, [4,12,15,23])
- r.add(10,20)
- self.assertEquals(r._offsets, [4,23])
- r.add(4,22)
- self.assertEquals(r._offsets, [4,23])
- r.add(30,30)
- self.assertEquals(r._offsets, [4,23,30,31])
- def testHas(self):
- r = NodeRanges()
- r.add(5, 10)
- r.add(15, 15)
- assert not r.has(4)
- assert r.has(5)
- assert r.has(6)
- assert r.has(9)
- assert r.has(10)
- assert not r.has(14)
- assert r.has(15)
- assert not r.has(16)
-if __name__ == '__main__':
- unittest.main()
-
This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.
|
|
From: <mat...@us...> - 2008-03-20 20:48:41
|
Revision: 168
http://javascriptlint.svn.sourceforge.net/javascriptlint/?rev=168&view=rev
Author: matthiasmiller
Date: 2008-03-20 13:48:39 -0700 (Thu, 20 Mar 2008)
Log Message:
-----------
various fixes for Python 2.4
Modified Paths:
--------------
trunk/pyjsl/conf.py
trunk/pyjsl/jsparse.py
trunk/pyjsl/lint.py
Modified: trunk/pyjsl/conf.py
===================================================================
--- trunk/pyjsl/conf.py 2008-03-04 15:15:14 UTC (rev 167)
+++ trunk/pyjsl/conf.py 2008-03-20 20:48:39 UTC (rev 168)
@@ -8,7 +8,7 @@
self.lineno = None
self.path = None
-class Setting():
+class Setting:
wants_parm = False
wants_dir = False
@@ -48,7 +48,7 @@
parm = os.path.join(dir, parm)
self.value.append((self._recurse.value, parm))
-class Conf():
+class Conf:
def __init__(self):
recurse = BooleanSetting(False)
self._settings = {
@@ -94,7 +94,8 @@
assert not '\n' in line
# Allow comments
- line = line.partition('#')[0]
+ if '#' in line:
+ line = line[:line.find('#')]
line = line.rstrip()
if not line:
return
Modified: trunk/pyjsl/jsparse.py
===================================================================
--- trunk/pyjsl/jsparse.py 2008-03-04 15:15:14 UTC (rev 167)
+++ trunk/pyjsl/jsparse.py 2008-03-20 20:48:39 UTC (rev 168)
@@ -12,7 +12,7 @@
['tok.%s' % prop for prop in dir(tok)]
))
-class NodePos():
+class NodePos:
def __init__(self, line, col):
self.line = line
self.col = col
@@ -29,7 +29,7 @@
def __str__(self):
return '(line %i, col %i)' % (self.line+1, self.col+1)
-class NodePositions():
+class NodePositions:
" Given a string, allows [x] lookups for NodePos line and column numbers."
def __init__(self, text):
# Find the length of each line and incrementally sum all of the lengths
@@ -55,7 +55,7 @@
lines[0] = lines[0][start.col:]
return ''.join(lines)
-class NodeRanges():
+class NodeRanges:
def __init__(self):
self._offsets = []
def add(self, start, end):
@@ -74,7 +74,7 @@
def has(self, pos):
return bisect.bisect_right(self._offsets, pos) % 2 == 1
-class _Node():
+class _Node:
def add_child(self, node):
if node:
node.node_index = len(self.kids)
Modified: trunk/pyjsl/lint.py
===================================================================
--- trunk/pyjsl/lint.py 2008-03-04 15:15:14 UTC (rev 167)
+++ trunk/pyjsl/lint.py 2008-03-20 20:48:39 UTC (rev 168)
@@ -70,7 +70,7 @@
parms = control_comment[len(keyword):].strip()
return (comment, keyword, parms)
-class Scope():
+class Scope:
def __init__(self, node):
self._is_with_scope = node.kind == tok.WITH
self._parent = None
This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.
|
|
From: <mat...@us...> - 2008-04-25 21:39:53
|
Revision: 193
http://javascriptlint.svn.sourceforge.net/javascriptlint/?rev=193&view=rev
Author: matthiasmiller
Date: 2008-04-25 14:39:29 -0700 (Fri, 25 Apr 2008)
Log Message:
-----------
Allow node visitors to specify an event (e.g. "push" vs. "pop").
Modified Paths:
--------------
trunk/pyjsl/lint.py
trunk/pyjsl/visitation.py
trunk/pyjsl/warnings.py
Modified: trunk/pyjsl/lint.py
===================================================================
--- trunk/pyjsl/lint.py 2008-04-25 21:16:20 UTC (rev 192)
+++ trunk/pyjsl/lint.py 2008-04-25 21:39:29 UTC (rev 193)
@@ -217,7 +217,7 @@
for pos, msg in parse_errors:
_report(pos, msg, False)
- visitors = visitation.make_visitors(warnings.klasses)
+ visitors = visitation.make_visitors(warnings.klasses)['push']
assert not script_cache
imports = script_cache['imports'] = set()
Modified: trunk/pyjsl/visitation.py
===================================================================
--- trunk/pyjsl/visitation.py 2008-04-25 21:16:20 UTC (rev 192)
+++ trunk/pyjsl/visitation.py 2008-04-25 21:39:29 UTC (rev 193)
@@ -3,11 +3,12 @@
traverse the tree to generate warnings.
"""
-def visit(*args):
+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
@@ -27,10 +28,17 @@
# 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:
+ if not func._visit_event in visitors:
+ visitors[func._visit_event] = {}
+ event_visitors = visitors[func._visit_event]
+
# Map from node_kind to the function
if not node_kind in visitors:
- visitors[node_kind] = []
- visitors[node_kind].append(func)
+ event_visitors[node_kind] = []
+ event_visitors[node_kind].append(func)
return visitors
Modified: trunk/pyjsl/warnings.py
===================================================================
--- trunk/pyjsl/warnings.py 2008-04-25 21:16:20 UTC (rev 192)
+++ trunk/pyjsl/warnings.py 2008-04-25 21:39:29 UTC (rev 193)
@@ -4,7 +4,7 @@
underscores. Its docstring should be the warning message.
The class can have one more more member functions to inspect nodes. The
-function should be decorated with a @lookat call specifying the nodes it
+function should be decorated with a @onpush 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 return the node causing
the warning.
@@ -13,7 +13,7 @@
class warning_name:
'questionable JavaScript coding style'
- @lookat(tok.NODEKIND, (tok.NODEKIND, op.OPCODE))
+ @onpush(tok.NODEKIND, (tok.NODEKIND, op.OPCODE))
def _lint(self, node):
if questionable:
return node
@@ -22,11 +22,14 @@
import sys
import types
-from visitation import visit as lookat
+import visitation
from pyspidermonkey import tok, op
# TODO: document inspect, node:opcode, etc
+def onpush(*args):
+ return visitation.visit('push', *args)
+
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 \
@@ -119,7 +122,7 @@
class comparison_type_conv:
'comparisons against null, 0, true, false, or an empty string allowing implicit type conversion (use === or !==)'
- @lookat((tok.EQOP, op.EQ))
+ @onpush((tok.EQOP, op.EQ))
def _lint(self, node):
lvalue, rvalue = node.kids
if not self._allow_coercive_compare(lvalue) or \
@@ -136,7 +139,7 @@
class default_not_at_end:
'the default case is not at the end of the switch statement'
- @lookat(tok.DEFAULT)
+ @onpush(tok.DEFAULT)
def _lint(self, node):
siblings = node.parent.kids
if node.node_index != len(siblings)-1:
@@ -144,7 +147,7 @@
class duplicate_case_in_switch:
'duplicate case in switch statement'
- @lookat(tok.CASE)
+ @onpush(tok.CASE)
def _lint(self, node):
# Only look at previous siblings
siblings = node.parent.kids
@@ -159,7 +162,7 @@
class missing_default_case:
'missing default case in switch statement'
- @lookat(tok.SWITCH)
+ @onpush(tok.SWITCH)
def _lint(self, node):
value, cases = node.kids
for case in cases.kids:
@@ -169,13 +172,13 @@
class with_statement:
'with statement hides undeclared variables; use temporary variable instead'
- @lookat(tok.WITH)
+ @onpush(tok.WITH)
def _lint(self, node):
return node
class useless_comparison:
'useless comparison; comparing identical expressions'
- @lookat(tok.EQOP,tok.RELOP)
+ @onpush(tok.EQOP,tok.RELOP)
def _lint(self, node):
lvalue, rvalue = node.kids
if lvalue.is_equivalent(rvalue):
@@ -183,20 +186,20 @@
class use_of_label:
'use of label'
- @lookat((tok.COLON, op.NAME))
+ @onpush((tok.COLON, op.NAME))
def _lint(self, node):
return node
class meaningless_block:
'meaningless block; curly braces have no impact'
- @lookat(tok.LC)
+ @onpush(tok.LC)
def _lint(self, node):
if node.parent and node.parent.kind == tok.LC:
return node
class misplaced_regex:
'regular expressions should be preceded by a left parenthesis, assignment, colon, or comma'
- @lookat((tok.OBJECT, op.REGEXP))
+ @onpush((tok.OBJECT, op.REGEXP))
def _lint(self, node):
if node.parent.kind == tok.NAME and node.parent.opcode == op.SETNAME:
return # Allow in var statements
@@ -214,14 +217,14 @@
class assign_to_function_call:
'assignment to a function call'
- @lookat(tok.ASSIGN)
+ @onpush(tok.ASSIGN)
def _lint(self, node):
if node.kids[0].kind == tok.LP:
return node
class ambiguous_else_stmt:
'the else statement could be matched with one of multiple if statements (use curly braces to indicate intent'
- @lookat(tok.IF)
+ @onpush(tok.IF)
def _lint(self, node):
# Only examine this node if it has an else statement.
condition, if_, else_ = node.kids
@@ -240,7 +243,7 @@
class block_without_braces:
'block statement without curly braces'
- @lookat(tok.IF, tok.WHILE, tok.DO, tok.FOR, tok.WITH)
+ @onpush(tok.IF, tok.WHILE, tok.DO, tok.FOR, tok.WITH)
def _lint(self, node):
if node.kids[1].kind != tok.LC:
return node.kids[1]
@@ -248,7 +251,7 @@
class ambiguous_nested_stmt:
'block statements containing block statements should use curly braces to resolve ambiguity'
_block_nodes = (tok.IF, tok.WHILE, tok.DO, tok.FOR, tok.WITH)
- @lookat(*_block_nodes)
+ @onpush(*_block_nodes)
def _lint(self, node):
# Ignore "else if"
if node.kind == tok.IF and node.node_index == 2 and node.parent.kind == tok.IF:
@@ -262,7 +265,7 @@
class inc_dec_within_stmt:
'increment (++) and decrement (--) operators used as part of greater statement'
- @lookat(tok.INC, tok.DEC)
+ @onpush(tok.INC, tok.DEC)
def _lint(self, node):
if node.parent.kind == tok.SEMI:
return None
@@ -287,7 +290,7 @@
class comma_separated_stmts:
'multiple statements separated by commas (use semicolons?)'
- @lookat(tok.COMMA)
+ @onpush(tok.COMMA)
def _lint(self, node):
# Allow within the first and third part of "for(;;)"
if _get_branch_in_for(node) in (0, 2):
@@ -299,11 +302,11 @@
class empty_statement:
'empty statement or extra semicolon'
- @lookat(tok.SEMI)
+ @onpush(tok.SEMI)
def _semi(self, node):
if not node.kids[0]:
return node
- @lookat(tok.LC)
+ @onpush(tok.LC)
def _lc(self, node):
if node.kids:
return
@@ -317,7 +320,7 @@
class missing_break:
'missing break statement'
- @lookat(tok.CASE, tok.DEFAULT)
+ @onpush(tok.CASE, tok.DEFAULT)
def _lint(self, node):
# The last item is handled separately
if node.node_index == len(node.parent.kids)-1:
@@ -333,7 +336,7 @@
class missing_break_for_last_case:
'missing break statement for last case in switch'
- @lookat(tok.CASE, tok.DEFAULT)
+ @onpush(tok.CASE, tok.DEFAULT)
def _lint(self, node):
if node.node_index < len(node.parent.kids)-1:
return
@@ -344,18 +347,18 @@
class multiple_plus_minus:
'unknown order of operations for successive plus (e.g. x+++y) or minus (e.g. x---y) signs'
- @lookat(tok.INC)
+ @onpush(tok.INC)
def _inc(self, node):
if node.node_index == 0 and node.parent.kind == tok.PLUS:
return node
- @lookat(tok.DEC)
+ @onpush(tok.DEC)
def _dec(self, node):
if node.node_index == 0 and node.parent.kind == tok.MINUS:
return node
class useless_assign:
'useless assignment'
- @lookat((tok.NAME, op.SETNAME))
+ @onpush((tok.NAME, op.SETNAME))
def _lint(self, node):
if node.parent.kind == tok.ASSIGN:
assert node.node_index == 0
@@ -367,7 +370,7 @@
class unreachable_code:
'unreachable code'
- @lookat(tok.BREAK, tok.CONTINUE, tok.RETURN, tok.THROW)
+ @onpush(tok.BREAK, tok.CONTINUE, tok.RETURN, tok.THROW)
def _lint(self, node):
if node.parent.kind == tok.LC and \
node.node_index != len(node.parent.kids)-1:
@@ -375,44 +378,44 @@
class meaningless_block:
'meaningless block; curly braces have no impact'
- #TODO: @lookat(tok.IF)
+ #TODO: @onpush(tok.IF)
def _lint(self, node):
condition, if_, else_ = node.kids
if condition.kind == tok.PRIMARY and condition.opcode in (op.TRUE, op.FALSE, op.NULL):
return condition
- #TODO: @lookat(tok.WHILE)
+ #TODO: @onpush(tok.WHILE)
def _lint(self, node):
condition = node.kids[0]
if condition.kind == tok.PRIMARY and condition.opcode in (op.FALSE, op.NULL):
return condition
- @lookat(tok.LC)
+ @onpush(tok.LC)
def _lint(self, node):
if node.parent and node.parent.kind == tok.LC:
return node
class useless_void:
'use of the void type may be unnecessary (void is always undefined)'
- @lookat((tok.UNARYOP, op.VOID))
+ @onpush((tok.UNARYOP, op.VOID))
def _lint(self, node):
return node
class parseint_missing_radix:
'parseInt missing radix parameter'
- @lookat((tok.LP, op.CALL))
+ @onpush((tok.LP, op.CALL))
def _lint(self, node):
if node.kids[0].kind == tok.NAME and node.kids[0].atom == 'parseInt' and len(node.kids) <= 2:
return node
class leading_decimal_point:
'leading decimal point may indicate a number or an object member'
- @lookat(tok.NUMBER)
+ @onpush(tok.NUMBER)
def _lint(self, node):
if node.atom.startswith('.'):
return node
class trailing_decimal_point:
'trailing decimal point may indicate a number or an object member'
- @lookat(tok.NUMBER)
+ @onpush(tok.NUMBER)
def _lint(self, node):
if node.parent.kind == tok.DOT:
return node
@@ -422,21 +425,21 @@
class octal_number:
'leading zeros make an octal number'
_regexp = re.compile('^0[0-9]')
- @lookat(tok.NUMBER)
+ @onpush(tok.NUMBER)
def _line(self, node):
if self._regexp.match(node.atom):
return node
class trailing_comma_in_array:
'extra comma is not recommended in array initializers'
- @lookat(tok.RB)
+ @onpush(tok.RB)
def _line(self, node):
if node.end_comma:
return node
class useless_quotes:
'the quotation marks are unnecessary'
- @lookat(tok.STRING)
+ @onpush(tok.STRING)
def _lint(self, node):
if node.node_index == 0 and node.parent.kind == tok.COLON:
return node
This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.
|
|
From: <mat...@us...> - 2008-04-25 21:52:36
|
Revision: 194
http://javascriptlint.svn.sourceforge.net/javascriptlint/?rev=194&view=rev
Author: matthiasmiller
Date: 2008-04-25 14:52:35 -0700 (Fri, 25 Apr 2008)
Log Message:
-----------
Change visitors to raise an exception instead of return the invalid node.
Modified Paths:
--------------
trunk/pyjsl/lint.py
trunk/pyjsl/warnings.py
Modified: trunk/pyjsl/lint.py
===================================================================
--- trunk/pyjsl/lint.py 2008-04-25 21:39:29 UTC (rev 193)
+++ trunk/pyjsl/lint.py 2008-04-25 21:52:35 UTC (rev 194)
@@ -260,9 +260,11 @@
for kind in (node.kind, (node.kind, node.opcode)):
if kind in visitors:
for visitor in visitors[kind]:
- warning_node = visitor(node)
- if warning_node:
- report(warning_node, visitor.im_class.__name__)
+ try:
+ ret = visitor(node)
+ assert ret is None, 'visitor should raise an exception, not return a value'
+ except warnings.LintWarning, warning:
+ report(warning.node, visitor.im_class.__name__)
if node.kind == tok.NAME:
if node.node_index == 0 and node.parent.kind == tok.COLON and node.parent.parent.kind == tok.RC:
Modified: trunk/pyjsl/warnings.py
===================================================================
--- trunk/pyjsl/warnings.py 2008-04-25 21:39:29 UTC (rev 193)
+++ trunk/pyjsl/warnings.py 2008-04-25 21:52:35 UTC (rev 194)
@@ -30,6 +30,10 @@
def onpush(*args):
return visitation.visit('push', *args)
+class LintWarning(Exception):
+ def __init__(self, node):
+ self.node = node
+
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 \
@@ -124,18 +128,14 @@
'comparisons against null, 0, true, false, or an empty string allowing implicit type conversion (use === or !==)'
@onpush((tok.EQOP, op.EQ))
def _lint(self, node):
- lvalue, rvalue = node.kids
- if not self._allow_coercive_compare(lvalue) or \
- not self._allow_coercive_compare(rvalue):
- return node
- def _allow_coercive_compare(self, node):
- if node.kind == tok.PRIMARY and node.opcode in (op.NULL, op.TRUE, op.FALSE):
- return False
- if node.kind == tok.NUMBER and not node.dval:
- return False
- if node.kind == tok.STRING and not node.atom:
- return False
- return True
+ for kid in node.kids:
+ if kid.kind == tok.PRIMARY and kid.opcode in (op.NULL, op.TRUE, op.FALSE):
+ continue
+ if kid.kind == tok.NUMBER and not kid.dval:
+ continue
+ if kid.kind == tok.STRING and not kid.atom:
+ continue
+ raise LintWarning, kid
class default_not_at_end:
'the default case is not at the end of the switch statement'
@@ -143,7 +143,7 @@
def _lint(self, node):
siblings = node.parent.kids
if node.node_index != len(siblings)-1:
- return siblings[node.node_index+1]
+ raise LintWarning, siblings[node.node_index+1]
class duplicate_case_in_switch:
'duplicate case in switch statement'
@@ -158,7 +158,7 @@
if sibling.kind == tok.CASE:
sibling_value = sibling.kids[0]
if node_value.is_equivalent(sibling_value, True):
- return node
+ raise LintWarning, node
class missing_default_case:
'missing default case in switch statement'
@@ -168,13 +168,13 @@
for case in cases.kids:
if case.kind == tok.DEFAULT:
return
- return node
+ raise LintWarning, node
class with_statement:
'with statement hides undeclared variables; use temporary variable instead'
@onpush(tok.WITH)
def _lint(self, node):
- return node
+ raise LintWarning, node
class useless_comparison:
'useless comparison; comparing identical expressions'
@@ -182,20 +182,20 @@
def _lint(self, node):
lvalue, rvalue = node.kids
if lvalue.is_equivalent(rvalue):
- return node
+ raise LintWarning, node
class use_of_label:
'use of label'
@onpush((tok.COLON, op.NAME))
def _lint(self, node):
- return node
+ raise LintWarning, node
class meaningless_block:
'meaningless block; curly braces have no impact'
@onpush(tok.LC)
def _lint(self, node):
if node.parent and node.parent.kind == tok.LC:
- return node
+ raise LintWarning, node
class misplaced_regex:
'regular expressions should be preceded by a left parenthesis, assignment, colon, or comma'
@@ -213,14 +213,14 @@
return # Allow in /re/.property
if node.parent.kind == tok.RETURN:
return # Allow for return values
- return node
+ raise LintWarning, node
class assign_to_function_call:
'assignment to a function call'
@onpush(tok.ASSIGN)
def _lint(self, node):
if node.kids[0].kind == tok.LP:
- return node
+ raise LintWarning, node
class ambiguous_else_stmt:
'the else statement could be matched with one of multiple if statements (use curly braces to indicate intent'
@@ -238,7 +238,7 @@
return
# Else is only ambiguous in the first branch of an if statement.
if tmp.parent.kind == tok.IF and tmp.node_index == 1:
- return else_
+ raise LintWarning, else_
tmp = tmp.parent
class block_without_braces:
@@ -246,7 +246,7 @@
@onpush(tok.IF, tok.WHILE, tok.DO, tok.FOR, tok.WITH)
def _lint(self, node):
if node.kids[1].kind != tok.LC:
- return node.kids[1]
+ raise LintWarning, node.kids[1]
class ambiguous_nested_stmt:
'block statements containing block statements should use curly braces to resolve ambiguity'
@@ -261,14 +261,14 @@
# was inside a block statement without clarifying curlies.
# (Otherwise, the node type would be tok.LC.)
if node.parent.kind in self._block_nodes:
- return node
+ raise LintWarning, node
class inc_dec_within_stmt:
'increment (++) and decrement (--) operators used as part of greater statement'
@onpush(tok.INC, tok.DEC)
def _lint(self, node):
if node.parent.kind == tok.SEMI:
- return None
+ return
# Allow within the third part of the "for"
tmp = node
@@ -277,9 +277,9 @@
if tmp and tmp.node_index == 2 and \
tmp.parent.kind == tok.RESERVED and \
tmp.parent.parent.kind == tok.FOR:
- return None
+ return
- return node
+ raise LintWarning, node
def _is_for_ternary_stmt(self, node, branch=None):
if node.parent and node.parent.kind == tok.COMMA:
return _is_for_ternary_stmt(node.parent, branch)
@@ -298,14 +298,14 @@
# This is an array
if node.parent.kind == tok.RB:
return
- return node
+ raise LintWarning, node
class empty_statement:
'empty statement or extra semicolon'
@onpush(tok.SEMI)
def _semi(self, node):
if not node.kids[0]:
- return node
+ raise LintWarning, node
@onpush(tok.LC)
def _lc(self, node):
if node.kids:
@@ -316,7 +316,7 @@
# Some empty blocks are meaningful.
if node.parent.kind in (tok.CATCH, tok.CASE, tok.DEFAULT, tok.SWITCH, tok.FUNCTION):
return
- return node
+ raise LintWarning, node
class missing_break:
'missing break statement'
@@ -332,7 +332,7 @@
return
if None in _get_exit_points(case_contents):
# Show the warning on the *next* node.
- return node.parent.kids[node.node_index+1]
+ raise LintWarning, node.parent.kids[node.node_index+1]
class missing_break_for_last_case:
'missing break statement for last case in switch'
@@ -343,18 +343,18 @@
case_contents = node.kids[1]
assert case_contents.kind == tok.LC
if None in _get_exit_points(case_contents):
- return node
+ raise LintWarning, node
class multiple_plus_minus:
'unknown order of operations for successive plus (e.g. x+++y) or minus (e.g. x---y) signs'
@onpush(tok.INC)
def _inc(self, node):
if node.node_index == 0 and node.parent.kind == tok.PLUS:
- return node
+ raise LintWarning, node
@onpush(tok.DEC)
def _dec(self, node):
if node.node_index == 0 and node.parent.kind == tok.MINUS:
- return node
+ raise LintWarning, node
class useless_assign:
'useless assignment'
@@ -366,7 +366,7 @@
elif node.parent.kind == tok.VAR:
value = node.kids[0]
if value and value.kind == tok.NAME and node.atom == value.atom:
- return node
+ raise LintWarning, node
class unreachable_code:
'unreachable code'
@@ -374,7 +374,7 @@
def _lint(self, node):
if node.parent.kind == tok.LC and \
node.node_index != len(node.parent.kids)-1:
- return node.parent.kids[node.node_index+1]
+ raise LintWarning, node.parent.kids[node.node_index+1]
class meaningless_block:
'meaningless block; curly braces have no impact'
@@ -382,45 +382,45 @@
def _lint(self, node):
condition, if_, else_ = node.kids
if condition.kind == tok.PRIMARY and condition.opcode in (op.TRUE, op.FALSE, op.NULL):
- return condition
+ raise LintWarning, condition
#TODO: @onpush(tok.WHILE)
def _lint(self, node):
condition = node.kids[0]
if condition.kind == tok.PRIMARY and condition.opcode in (op.FALSE, op.NULL):
- return condition
+ raise LintWarning, condition
@onpush(tok.LC)
def _lint(self, node):
if node.parent and node.parent.kind == tok.LC:
- return node
+ raise LintWarning, node
class useless_void:
'use of the void type may be unnecessary (void is always undefined)'
@onpush((tok.UNARYOP, op.VOID))
def _lint(self, node):
- return node
+ raise LintWarning, node
class parseint_missing_radix:
'parseInt missing radix parameter'
@onpush((tok.LP, op.CALL))
def _lint(self, node):
if node.kids[0].kind == tok.NAME and node.kids[0].atom == 'parseInt' and len(node.kids) <= 2:
- return node
+ raise LintWarning, node
class leading_decimal_point:
'leading decimal point may indicate a number or an object member'
@onpush(tok.NUMBER)
def _lint(self, node):
if node.atom.startswith('.'):
- return node
+ raise LintWarning, node
class trailing_decimal_point:
'trailing decimal point may indicate a number or an object member'
@onpush(tok.NUMBER)
def _lint(self, node):
if node.parent.kind == tok.DOT:
- return node
+ raise LintWarning, node
if node.atom.endswith('.'):
- return node
+ raise LintWarning, node
class octal_number:
'leading zeros make an octal number'
@@ -428,21 +428,21 @@
@onpush(tok.NUMBER)
def _line(self, node):
if self._regexp.match(node.atom):
- return node
+ raise LintWarning, node
class trailing_comma_in_array:
'extra comma is not recommended in array initializers'
@onpush(tok.RB)
def _line(self, node):
if node.end_comma:
- return node
+ raise LintWarning, node
class useless_quotes:
'the quotation marks are unnecessary'
@onpush(tok.STRING)
def _lint(self, node):
if node.node_index == 0 and node.parent.kind == tok.COLON:
- return node
+ raise LintWarning, node
class mismatch_ctrl_comments:
'mismatched control comment; "ignore" and "end" control comments must have a one-to-one correspondence'
This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.
|
|
From: <mat...@us...> - 2008-04-25 22:24:52
|
Revision: 195
http://javascriptlint.svn.sourceforge.net/javascriptlint/?rev=195&view=rev
Author: matthiasmiller
Date: 2008-04-25 15:24:51 -0700 (Fri, 25 Apr 2008)
Log Message:
-----------
Change scope checks to use visitors.
Modified Paths:
--------------
trunk/pyjsl/lint.py
trunk/pyjsl/visitation.py
Modified: trunk/pyjsl/lint.py
===================================================================
--- trunk/pyjsl/lint.py 2008-04-25 21:52:35 UTC (rev 194)
+++ trunk/pyjsl/lint.py 2008-04-25 22:24:51 UTC (rev 195)
@@ -217,14 +217,22 @@
for pos, msg in parse_errors:
_report(pos, msg, False)
- visitors = visitation.make_visitors(warnings.klasses)['push']
+ # Find all visitors and convert them into "onpush" callbacks that call "report"
+ visitors = {}
+ visitation.make_visitors(visitors, warnings.klasses)
+ for event in visitors:
+ for kind, callbacks in visitors[event].items():
+ visitors[event][kind] = [_getreporter(callback, report) for callback in callbacks]
assert not script_cache
imports = script_cache['imports'] = set()
scope = script_cache['scope'] = Scope(root)
+ # Push the scope/variable checks.
+ visitation.make_visitors(visitors, [_get_scope_checks(scope, report)])
+
# kickoff!
- _lint_node(root, visitors, report, scope)
+ _lint_node(root, visitors)
# Process imports by copying global declarations into the universal scope.
imports |= set(conf['declarations'])
@@ -245,6 +253,15 @@
if not node.atom in imports:
report(node, 'undeclared_identifier')
+def _getreporter(visitor, report):
+ def onpush(node):
+ try:
+ ret = visitor(node)
+ assert ret is None, 'visitor should raise an exception, not return a value'
+ except warnings.LintWarning, warning:
+ report(warning.node, visitor.im_class.__name__)
+ return onpush
+
def _warn_or_declare(scope, name, node, report):
other = scope.get_identifier(name)
if other and other.kind == tok.FUNCTION and name in other.fn_args:
@@ -254,42 +271,58 @@
else:
scope.add_declaration(name, node)
-def _lint_node(node, visitors, report, scope):
+def _get_scope_checks(scope, report):
+ scopes = [scope]
- # Let the visitors warn.
- for kind in (node.kind, (node.kind, node.opcode)):
- if kind in visitors:
- for visitor in visitors[kind]:
- try:
- ret = visitor(node)
- assert ret is None, 'visitor should raise an exception, not return a value'
- except warnings.LintWarning, warning:
- report(warning.node, visitor.im_class.__name__)
+ class scope_checks:
+ ' '
+ @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:
+ pass # left side of object literal
+ elif node.parent.kind == tok.CATCH:
+ scopes[-1].add_declaration(node.atom, node)
+ else:
+ scopes[-1].add_reference(node.atom, node)
- if node.kind == tok.NAME:
- if node.node_index == 0 and node.parent.kind == tok.COLON and node.parent.parent.kind == tok.RC:
- pass # left side of object literal
- elif node.parent.kind == tok.CATCH:
- scope.add_declaration(node.atom, node)
- else:
- scope.add_reference(node.atom, node)
+ @visitation.visit('push', tok.FUNCTION)
+ def _push_func(self, node):
+ if node.fn_name:
+ _warn_or_declare(scopes[-1], node.fn_name, node, report)
+ self._push_scope(node)
+ for var_name in node.fn_args:
+ scopes[-1].add_declaration(var_name, node)
- # Push function identifiers
- if node.kind == tok.FUNCTION:
- if node.fn_name:
- _warn_or_declare(scope, node.fn_name, node, report)
- scope = scope.add_scope(node)
- for var_name in node.fn_args:
- scope.add_declaration(var_name, node)
- elif node.kind == tok.LEXICALSCOPE:
- scope = scope.add_scope(node)
- elif node.kind == tok.WITH:
- scope = scope.add_scope(node)
+ @visitation.visit('push', tok.LEXICALSCOPE, tok.WITH)
+ def _push_scope(self, node):
+ scopes.append(scopes[-1].add_scope(node))
- if node.parent and node.parent.kind == tok.VAR:
- _warn_or_declare(scope, node.atom, node, report)
+ @visitation.visit('pop', tok.FUNCTION, tok.LEXICALSCOPE, tok.WITH)
+ def _pop_scope(self, node):
+ scopes.pop()
+ @visitation.visit('push', tok.VAR)
+ def _push_var(self, node):
+ for kid in node.kids:
+ _warn_or_declare(scope, kid.atom, node, report)
+
+ return scope_checks
+
+
+def _lint_node(node, visitors):
+
+ for kind in (node.kind, (node.kind, node.opcode)):
+ if kind in visitors['push']:
+ for visitor in visitors['push'][kind]:
+ visitor(node)
+
for child in node.kids:
if child:
- _lint_node(child, visitors, report, scope)
+ _lint_node(child, visitors)
+ for kind in (node.kind, (node.kind, node.opcode)):
+ if kind in visitors['pop']:
+ for visitor in visitors['pop'][kind]:
+ visitor(node)
+
+
Modified: trunk/pyjsl/visitation.py
===================================================================
--- trunk/pyjsl/visitation.py 2008-04-25 21:52:35 UTC (rev 194)
+++ trunk/pyjsl/visitation.py 2008-04-25 22:24:51 UTC (rev 195)
@@ -10,13 +10,21 @@
def _decorate(fn):
fn._visit_event = event
fn._visit_nodes = args
+ print dir(fn), fn.func_name
+ raise ValueError
return fn
return _decorate
-def make_visitors(klasses):
+def make_visitors(visitors, klasses):
""" Searches klasses for all member functions decorated with @visit and
- returns a dictionary that maps from node type to visitor function. """
- visitors = {}
+ 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:
@@ -32,13 +40,15 @@
for node_kind in getattr(func, '_visit_nodes', ()):
# Group visitors by event (e.g. push vs pop)
if not event_visitors:
- if not func._visit_event in visitors:
- visitors[func._visit_event] = {}
- event_visitors = visitors[func._visit_event]
+ try:
+ event_visitors = visitors[func._visit_event]
+ except KeyError:
+ event_visitors = visitors[func._visit_event] = {}
# Map from node_kind to the function
- if not node_kind in visitors:
- event_visitors[node_kind] = []
- event_visitors[node_kind].append(func)
+ 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...> - 2008-04-26 05:09:32
|
Revision: 198
http://javascriptlint.svn.sourceforge.net/javascriptlint/?rev=198&view=rev
Author: matthiasmiller
Date: 2008-04-25 22:09:30 -0700 (Fri, 25 Apr 2008)
Log Message:
-----------
Change the visitors to be functions instead of classes.
Modified Paths:
--------------
trunk/pyjsl/conf.py
trunk/pyjsl/lint.py
trunk/pyjsl/warnings.py
Modified: trunk/pyjsl/conf.py
===================================================================
--- trunk/pyjsl/conf.py 2008-04-26 04:40:07 UTC (rev 197)
+++ trunk/pyjsl/conf.py 2008-04-26 05:09:30 UTC (rev 198)
@@ -68,8 +68,8 @@
'equal_as_assign': BooleanSetting(True),
'anon_no_return_value': BooleanSetting(True)
}
- for klass in warnings.klasses:
- self._settings[klass.__name__] = BooleanSetting(True)
+ for name in warnings.warnings:
+ self._settings[name] = BooleanSetting(True)
self.loadline('-block_without_braces')
def loadfile(self, path):
Modified: trunk/pyjsl/lint.py
===================================================================
--- trunk/pyjsl/lint.py 2008-04-26 04:40:07 UTC (rev 197)
+++ trunk/pyjsl/lint.py 2008-04-26 05:09:30 UTC (rev 198)
@@ -218,8 +218,9 @@
_report(pos, msg, False)
# Find all visitors and convert them into "onpush" callbacks that call "report"
- visitors = {}
- visitation.make_visitors(visitors, warnings.klasses)
+ visitors = {
+ 'push': warnings.make_visitors()
+ }
for event in visitors:
for kind, callbacks in visitors[event].items():
visitors[event][kind] = [_getreporter(callback, report) for callback in callbacks]
@@ -259,7 +260,7 @@
ret = visitor(node)
assert ret is None, 'visitor should raise an exception, not return a value'
except warnings.LintWarning, warning:
- report(warning.node, visitor.im_class.__name__)
+ report(warning.node, visitor.warning)
return onpush
def _warn_or_declare(scope, name, node, report):
Modified: trunk/pyjsl/warnings.py
===================================================================
--- trunk/pyjsl/warnings.py 2008-04-26 04:40:07 UTC (rev 197)
+++ trunk/pyjsl/warnings.py 2008-04-26 05:09:30 UTC (rev 198)
@@ -1,22 +1,18 @@
# vim: ts=4 sw=4 expandtab
""" This module contains all the warnings. To add a new warning, define a
-class. Its name should be in lowercase and words should be separated by
-underscores. Its docstring should be the warning message.
+function. Its name should be in lowercase and words should be separated by
+underscores.
-The class can have one more more member functions to inspect nodes. The
-function should be decorated with a @onpush call specifying the nodes it
+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 return the node causing
-the warning.
+format. To report a warning, the function should raise a LintWarning exception.
For example:
- class warning_name:
- 'questionable JavaScript coding style'
- @onpush(tok.NODEKIND, (tok.NODEKIND, op.OPCODE))
- def _lint(self, node):
- if questionable:
- return node
+ @lookfor(tok.NODEKIND, (tok.NODEKIND, op.OPCODE))
+ def warning_name(node):
+ if questionable:
+ raise LintWarning, node
"""
import re
import sys
@@ -27,9 +23,59 @@
# TODO: document inspect, node:opcode, etc
-def onpush(*args):
- return visitation.visit('push', *args)
+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',
+ 'meaningless_block': 'meaningless block; curly braces have no impact',
+ 'misplaced_regex': 'regular expressions should be preceded by a left parenthesis, assignment, colon, or comma',
+ 'assign_to_function_call': 'assignment to a function call',
+ '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_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 {0} {1}',
+ 'undeclared_identifier': 'undeclared identifier: {0}',
+ '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 {0} hides argument',
+ 'duplicate_formal': 'TODO',
+ 'missing_semicolon': 'missing semicolon',
+ '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',
+}
+def lookfor(*args):
+ def decorate(fn):
+ fn._lint_nodes = args
+ fn.warning = fn.func_name.rstrip('_')
+ assert fn.warning in warnings, 'Missing warning description: %s' % fn.warning
+ return fn
+ return decorate
+
class LintWarning(Exception):
def __init__(self, node):
self.node = node
@@ -124,382 +170,325 @@
return exit_points
-class comparison_type_conv:
- 'comparisons against null, 0, true, false, or an empty string allowing implicit type conversion (use === or !==)'
- @onpush((tok.EQOP, op.EQ))
- def comparison_type_conv(self, node):
- for kid in node.kids:
- if kid.kind == tok.PRIMARY and kid.opcode in (op.NULL, op.TRUE, op.FALSE):
- continue
- if kid.kind == tok.NUMBER and not kid.dval:
- continue
- if kid.kind == tok.STRING and not kid.atom:
- continue
- raise LintWarning, kid
+@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):
+ continue
+ if kid.kind == tok.NUMBER and not kid.dval:
+ continue
+ if kid.kind == tok.STRING and not kid.atom:
+ continue
+ raise LintWarning, kid
-class default_not_at_end:
- 'the default case is not at the end of the switch statement'
- @onpush(tok.DEFAULT)
- def default_not_at_end(self, node):
- siblings = node.parent.kids
- if node.node_index != len(siblings)-1:
- raise LintWarning, siblings[node.node_index+1]
+@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]
-class duplicate_case_in_switch:
- 'duplicate case in switch statement'
- @onpush(tok.CASE)
- def duplicate_case_in_switch(self, 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.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
-class missing_default_case:
- 'missing default case in switch statement'
- @onpush(tok.SWITCH)
- def missing_default_case(self, node):
- value, cases = node.kids
- for case in cases.kids:
- if case.kind == tok.DEFAULT:
- return
- 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
-class with_statement:
- 'with statement hides undeclared variables; use temporary variable instead'
- @onpush(tok.WITH)
- def with_statement(self, node):
+@lookfor(tok.WITH)
+def with_statement(node):
+ raise LintWarning, node
+
+@lookfor(tok.EQOP,tok.RELOP)
+def useless_comparison(node):
+ lvalue, rvalue = node.kids
+ if lvalue.is_equivalent(rvalue):
raise LintWarning, node
-class useless_comparison:
- 'useless comparison; comparing identical expressions'
- @onpush(tok.EQOP,tok.RELOP)
- def useless_comparison(self, node):
- lvalue, rvalue = node.kids
- if lvalue.is_equivalent(rvalue):
- raise LintWarning, node
+@lookfor((tok.COLON, op.NAME))
+def use_of_label(node):
+ raise LintWarning, node
-class use_of_label:
- 'use of label'
- @onpush((tok.COLON, op.NAME))
- def use_of_label(self, node):
+@lookfor(tok.LC)
+def meaningless_block(node):
+ if node.parent and node.parent.kind == tok.LC:
raise LintWarning, node
-class meaningless_block:
- 'meaningless block; curly braces have no impact'
- @onpush(tok.LC)
- def meaningless_block(self, node):
- if node.parent and node.parent.kind == tok.LC:
- 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
-class misplaced_regex:
- 'regular expressions should be preceded by a left parenthesis, assignment, colon, or comma'
- @onpush((tok.OBJECT, op.REGEXP))
- def misplaced_regex(self, 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
+@lookfor(tok.ASSIGN)
+def assign_to_function_call(node):
+ if node.kids[0].kind == tok.LP:
raise LintWarning, node
-class assign_to_function_call:
- 'assignment to a function call'
- @onpush(tok.ASSIGN)
- def assign_to_function_call(self, node):
- if node.kids[0].kind == tok.LP:
- 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
-class ambiguous_else_stmt:
- 'the else statement could be matched with one of multiple if statements (use curly braces to indicate intent'
- @onpush(tok.IF)
- def ambiguous_else_stmt(self, node):
- # Only examine this node if it has an else statement.
- condition, if_, else_ = node.kids
- if not else_:
+ 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
- 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]
-class block_without_braces:
- 'block statement without curly braces'
- @onpush(tok.IF, tok.WHILE, tok.DO, tok.FOR, tok.WITH)
- def block_without_braces(self, 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
-class ambiguous_nested_stmt:
- 'block statements containing block statements should use curly braces to resolve ambiguity'
- _block_nodes = (tok.IF, tok.WHILE, tok.DO, tok.FOR, tok.WITH)
- @onpush(*_block_nodes)
- def ambiguous_nested_stmt(self, 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
- # 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 self._block_nodes:
- raise LintWarning, node
+@lookfor(tok.INC, tok.DEC)
+def inc_dec_within_stmt(node):
+ if node.parent.kind == tok.SEMI:
+ return
-class inc_dec_within_stmt:
- 'increment (++) and decrement (--) operators used as part of greater statement'
- @onpush(tok.INC, tok.DEC)
- def inc_dec_within_stmt(self, 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
- # 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
- def _is_for_ternary_stmt(self, node, branch=None):
- if node.parent and node.parent.kind == tok.COMMA:
- return _is_for_ternary_stmt(node.parent, branch)
- return node.node_index == branch and \
- node.parent and \
- node.parent.kind == tok.RESERVED and \
- node.parent.parent.kind == tok.FOR
+@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
-class comma_separated_stmts:
- 'multiple statements separated by commas (use semicolons?)'
- @onpush(tok.COMMA)
- def comma_separated_stmts(self, 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
+@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
-class empty_statement:
- 'empty statement or extra semicolon'
- @onpush(tok.SEMI)
- def empty_statement(self, node):
- if not node.kids[0]:
- raise LintWarning, node
- @onpush(tok.LC)
- def empty_statement_(self, 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
+@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
-class missing_break:
- 'missing break statement'
- @onpush(tok.CASE, tok.DEFAULT)
- def missing_break(self, 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.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
-class missing_break_for_last_case:
- 'missing break statement for last case in switch'
- @onpush(tok.CASE, tok.DEFAULT)
- def missing_break_for_last_case(self, 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.BREAK, tok.CONTINUE, tok.RETURN, tok.THROW)
+def unreachable_code(node):
+ if node.parent.kind == tok.LC and \
+ node.node_index != len(node.parent.kids)-1:
+ raise LintWarning, node.parent.kids[node.node_index+1]
-class multiple_plus_minus:
- 'unknown order of operations for successive plus (e.g. x+++y) or minus (e.g. x---y) signs'
- @onpush(tok.INC)
- def multiple_plus_minus(self, node):
- if node.node_index == 0 and node.parent.kind == tok.PLUS:
- raise LintWarning, node
- @onpush(tok.DEC)
- def multiple_plus_minus_(self, node):
- if node.node_index == 0 and node.parent.kind == tok.MINUS:
- raise LintWarning, node
+#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
-class useless_assign:
- 'useless assignment'
- @onpush((tok.NAME, op.SETNAME))
- def useless_assign(self, 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.UNARYOP, op.VOID))
+def useless_void(node):
+ raise LintWarning, node
-class unreachable_code:
- 'unreachable code'
- @onpush(tok.BREAK, tok.CONTINUE, tok.RETURN, tok.THROW)
- def unreachable_code(self, node):
- if node.parent.kind == tok.LC and \
- node.node_index != len(node.parent.kids)-1:
- raise LintWarning, node.parent.kids[node.node_index+1]
+@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
-class meaningless_block:
- 'meaningless block; curly braces have no impact'
- #TODO: @onpush(tok.IF)
- def meaningless_block(self, 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: @onpush(tok.WHILE)
- def meaningless_blocK_(self, node):
- condition = node.kids[0]
- if condition.kind == tok.PRIMARY and condition.opcode in (op.FALSE, op.NULL):
- raise LintWarning, condition
- @onpush(tok.LC)
- def meaningless_block__(self, node):
- if node.parent and node.parent.kind == tok.LC:
- raise LintWarning, node
+@lookfor(tok.NUMBER)
+def leading_decimal_point(node):
+ if node.atom.startswith('.'):
+ raise LintWarning, node
-class useless_void:
- 'use of the void type may be unnecessary (void is always undefined)'
- @onpush((tok.UNARYOP, op.VOID))
- def useless_void(self, 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
-class parseint_missing_radix:
- 'parseInt missing radix parameter'
- @onpush((tok.LP, op.CALL))
- def parseint_missing_radix(self, node):
- if node.kids[0].kind == tok.NAME and node.kids[0].atom == 'parseInt' and len(node.kids) <= 2:
- 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
-class leading_decimal_point:
- 'leading decimal point may indicate a number or an object member'
- @onpush(tok.NUMBER)
- def leading_decimal_point(self, node):
- if node.atom.startswith('.'):
- raise LintWarning, node
+@lookfor(tok.RB)
+def trailing_comma_in_array(node):
+ if node.end_comma:
+ raise LintWarning, node
-class trailing_decimal_point:
- 'trailing decimal point may indicate a number or an object member'
- @onpush(tok.NUMBER)
- def trailing_decimal_point(self, node):
- if node.parent.kind == tok.DOT:
- raise LintWarning, node
- if node.atom.endswith('.'):
- raise LintWarning, node
+@lookfor(tok.STRING)
+def useless_quotes(node):
+ if node.node_index == 0 and node.parent.kind == tok.COLON:
+ raise LintWarning, node
-class octal_number:
- 'leading zeros make an octal number'
- _regexp = re.compile('^0[0-9]')
- @onpush(tok.NUMBER)
- def octal_number(self, node):
- if self._regexp.match(node.atom):
- raise LintWarning, node
-
-class trailing_comma_in_array:
- 'extra comma is not recommended in array initializers'
- @onpush(tok.RB)
- def trailing_comma_in_array(self, node):
- if node.end_comma:
- raise LintWarning, node
-
-class useless_quotes:
- 'the quotation marks are unnecessary'
- @onpush(tok.STRING)
- def useless_quotes(self, node):
- if node.node_index == 0 and node.parent.kind == tok.COLON:
- raise LintWarning, node
-
-class mismatch_ctrl_comments:
- 'mismatched control comment; "ignore" and "end" control comments must have a one-to-one correspondence'
+@lookfor()
+def mismatch_ctrl_comments(node):
pass
-class redeclared_var:
- 'redeclaration of {0} {1}'
+@lookfor()
+def redeclared_var(node):
pass
-class undeclared_identifier:
- 'undeclared identifier: {0}'
+@lookfor()
+def undeclared_identifier(node):
pass
-class jsl_cc_not_understood:
- 'couldn\'t understand control comment using /*jsl:keyword*/ syntax'
+@lookfor()
+def jsl_cc_not_understood(node):
pass
-class nested_comment:
- 'nested comment'
+@lookfor()
+def nested_comment(node):
pass
-class legacy_cc_not_understood:
- 'couldn\'t understand control comment using /*@keyword@*/ syntax'
+@lookfor()
+def legacy_cc_not_understood(node):
pass
-class var_hides_arg:
- 'variable {0} hides argument'
+@lookfor()
+def var_hides_arg(node):
pass
-class duplicate_formal:
- 'TODO'
+@lookfor()
+def duplicate_formal(node):
pass
-class missing_semicolon:
- 'missing semicolon'
+@lookfor()
+def missing_semicolon(node):
pass
-class ambiguous_newline:
- 'unexpected end of line; it is ambiguous whether these lines are part of the same statement'
+@lookfor()
+def ambiguous_newline(node):
pass
-class missing_option_explicit:
- 'the "option explicit" control comment is missing'
+@lookfor()
+def missing_option_explicit(node):
pass
-class partial_option_explicit:
- 'the "option explicit" control comment, if used, must be in the first script tag'
+@lookfor()
+def partial_option_explicit(node):
pass
-class dup_option_explicit:
- 'duplicate "option explicit" control comment'
+@lookfor()
+def dup_option_explicit(node):
pass
-klasses = tuple([
- obj for
- obj in
- sys.modules[__name__].__dict__.values() if
- type(obj) == types.ClassType
-])
+def make_visitors():
+ functions = [
+ obj for obj in sys.modules[__name__].__dict__.values()
+ if type(obj) == types.FunctionType and hasattr(obj, '_lint_nodes')
+ ]
+ visitors = {}
+ for func in functions:
+ for node_kind in func._lint_nodes:
+ try:
+ visitors[node_kind].append(func)
+ except KeyError:
+ visitors[node_kind] = [func]
+ return visitors
+
This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.
|