javascriptlint-commit Mailing List for JavaScript Lint (Page 6)
Status: Beta
Brought to you by:
matthiasmiller
You can subscribe to this list here.
2008 |
Jan
|
Feb
|
Mar
(42) |
Apr
(15) |
May
(2) |
Jun
|
Jul
|
Aug
(33) |
Sep
(3) |
Oct
|
Nov
|
Dec
|
---|---|---|---|---|---|---|---|---|---|---|---|---|
2009 |
Jan
|
Feb
|
Mar
(5) |
Apr
|
May
(2) |
Jun
|
Jul
|
Aug
(2) |
Sep
|
Oct
(43) |
Nov
(4) |
Dec
(1) |
2010 |
Jan
|
Feb
|
Mar
|
Apr
(6) |
May
|
Jun
|
Jul
|
Aug
|
Sep
|
Oct
(1) |
Nov
|
Dec
|
2011 |
Jan
|
Feb
|
Mar
|
Apr
(1) |
May
|
Jun
|
Jul
|
Aug
|
Sep
|
Oct
|
Nov
|
Dec
(1) |
2013 |
Jan
|
Feb
|
Mar
|
Apr
|
May
|
Jun
|
Jul
|
Aug
|
Sep
(20) |
Oct
(23) |
Nov
|
Dec
(1) |
2014 |
Jan
(1) |
Feb
(2) |
Mar
|
Apr
|
May
|
Jun
|
Jul
|
Aug
|
Sep
|
Oct
(4) |
Nov
|
Dec
|
2016 |
Jan
|
Feb
|
Mar
|
Apr
|
May
|
Jun
|
Jul
|
Aug
|
Sep
|
Oct
|
Nov
|
Dec
(18) |
2018 |
Jan
(7) |
Feb
|
Mar
|
Apr
|
May
|
Jun
(8) |
Jul
|
Aug
|
Sep
|
Oct
|
Nov
|
Dec
|
From: <mat...@us...> - 2009-10-06 16:06:08
|
Revision: 262 http://javascriptlint.svn.sourceforge.net/javascriptlint/?rev=262&view=rev Author: matthiasmiller Date: 2009-10-06 16:05:47 +0000 (Tue, 06 Oct 2009) Log Message: ----------- www: first cut on a markdown-based website Added Paths: ----------- trunk/www/ trunk/www/__template__ trunk/www/docs/ trunk/www/docs/index.htm trunk/www/docs/running_from_the_command_line.htm trunk/www/docs/running_from_windows_explorer.htm trunk/www/docs/running_from_your_ide.htm trunk/www/docs/running_from_your_php_website.htm trunk/www/docs/running_from_your_windows_program.htm trunk/www/download.htm trunk/www/index.htm trunk/www/online_lint.php trunk/www/static/ trunk/www/static/feed-icon-32x32.png trunk/www/static/global.css trunk/www.py Added: trunk/www/__template__ =================================================================== --- trunk/www/__template__ (rev 0) +++ trunk/www/__template__ 2009-10-06 16:05:47 UTC (rev 262) @@ -0,0 +1,22 @@ +<html> +<head> + <title>%(title)s</title> + <link rel="stylesheet" type="text/css" href="/static/global.css" /> +</head> +<body> + <div id="title"> + JavaScript Lint + </div> + <div id="nav"> + %(nav)s + <p align="center"><a href="rss.php"> + <img border="0" src="/static/feed-icon-32x32.png" width="32" height="32"></a></p> + </div> + <div id="body"> + %(body)s + </div> + <div id="footer"> + JavaScript Lint is sponsored by <a href="http://blog.outofhanwell.com/">Matthias Miller</a>. + </div> +</body> +</html> Property changes on: trunk/www/__template__ ___________________________________________________________________ Added: svn:eol-style + native Added: trunk/www/docs/index.htm =================================================================== --- trunk/www/docs/index.htm (rev 0) +++ trunk/www/docs/index.htm 2009-10-06 16:05:47 UTC (rev 262) @@ -0,0 +1,84 @@ +<!-- +@title=Documentation +--> + +Setting Up JavaScript Lint +========================== + +You can run JavaScript Lint several ways: + +* You can [integrate it into your IDE](running_from_your_ide.htm), such as Visual Studio, SciTE, or any other IDE supporting external tools. When JavaScript Lint finds an error, your IDE takes you directly to the line containing the error. + +* You can [run it through Windows Explorer](running_from_windows_explorer.htm), which Windows programmers may prefer. + +* You can [use the command line](running_from_the_command_line.htm) to integrate into your build system, or maybe you're a Linux programmer and simply prefer the command line! + +Additionally, if you are a software developer, you may want to [integrate into your Windows program](running_from_your_windows_program.htm) or [run it from your PHP website](running_from_your_php_website.htm) to take advantage of the full power of JavaScript Lint. + +Disable Warnings with Control Comment +===================================== + +JavaScript Lint has limited support for control comments. To disable warnings in part of your JavaScript file, you can add the following comments: + +> /*jsl:ignore*/ +> (code that fires warnings) +> /*jsl:end*/ + +To ignore all warnings in a file, simply place `/*jsl:ignoreall*/` at the top of the file. + +Note that this should only be used as a short-term solution for code that you maintain. If this is caused by a bug or missing feature in JavaScript Lint, please report the problem to Info\<at\>JavaScriptLint.com. + +Option Explicit +=============== + +JavaScript Lint can optionally check for variables, functions, and objects that don't exist, much like Visual Basic's "option explicit." In the interest of making JavaScript Lint accessible to the average programmer, this powerful feature is disabled by default. Although this feature requires more work to understand and implement, it provides a higher level of protection against coding errors. + +A variable that is not explicitly declared has a global scope. For example, if a function uses a counter variable and calls another function that uses a counter variable by the same name, unless these functions use the var keyword to declare the variable, the two functions will be accessing and modifying the same variable. This almost never produces the expected behavior. + +Here's what it takes to set up this feature: + +* The check for undeclared identifiers is enabled on a per-file basis with a `/*jsl:option explicit*/` comment within a script. To enforce the use of option explicit, you can modify your configuration file to warn against scripts that do not use this feature. + +* If a script references a variable, function, or object from another script, you will need to add a `/*jsl:import PathToOtherScript*/` comment in your script. This tells JavaScript Lint to check for items declared in the other script. Relative paths are resolved based on the path of the current script. + +* Your script may also reference global objects that are provided by the runtime (e.g. Firefox or Windows Scripting Host). For example, the script in a web page may reference the global window object. Add the line "+define window" to your configuration file to tell JavaScript Lint about this global. + +JavaScript Lint does not validate object properties. They do not use the var keyword and cannot be validated without executing the script. + +The warnings for undeclared identifiers will appear after other warnings that may occur in the script. This is by design, since the entire script must be examined before identifiers can be called undefined. + +Switches and Breaks +=================== + +By default, JavaScript Lint warns against missing _break_'s in switch statements. Sometimes, however, break statements are intentionally excluded. To indicate this, use the `/*jsl:fallthru*/` control comment: + +> switch (i) { +> case 1: +> break; +> case 2: +> /*jsl:fallthru*/ +> case 3: +> break; +> } + +Empty Statements +================ + +By default, JavaScript Lint warns against empty statements. However, empty statements are sometimes intention. To indicate this, use the /*jsl:pass*/ control comment: + +> while (!hasResponse()) { +> /*jsl:pass*/ +> } + +Advanced Output Format +====================== + +The following output formats may also be used: + +* `__ERROR_PREFIX__` indicates the type of message +* `__ERROR_MSG__` indicates the message contents +* `__ERROR__` indicates the full error including the error type and the message. + +If the output format is prefixed with "encode:", all backslashes, single and double quotes, tabs, carriage returns, and line breaks will be escaped as `\\`, `\'` and `\"`, `\t`, `\r`, and `\n` (respectively). + +> __Syntax Note__: In addition to the /*jsl:keyword*/ syntax, control comments can also use the traditional `/*@keyword@*/` syntax. However, the jsl syntax is recommended for interoperability with JScript conditional compilation. Property changes on: trunk/www/docs/index.htm ___________________________________________________________________ Added: svn:eol-style + native Property changes on: trunk/www/docs/running_from_the_command_line.htm ___________________________________________________________________ Added: svn:eol-style + native Property changes on: trunk/www/docs/running_from_windows_explorer.htm ___________________________________________________________________ Added: svn:eol-style + native Property changes on: trunk/www/docs/running_from_your_ide.htm ___________________________________________________________________ Added: svn:eol-style + native Property changes on: trunk/www/docs/running_from_your_php_website.htm ___________________________________________________________________ Added: svn:eol-style + native Property changes on: trunk/www/docs/running_from_your_windows_program.htm ___________________________________________________________________ Added: svn:eol-style + native Added: trunk/www/download.htm =================================================================== --- trunk/www/download.htm (rev 0) +++ trunk/www/download.htm 2009-10-06 16:05:47 UTC (rev 262) @@ -0,0 +1,28 @@ +<!-- +@title=Download +--> + +Getting Started +=============== + +To get started with JavaScript Lint, download the appropriate package and extract its contents. + +* JavaScript Lint 0.3.0 for Windows ([jsl-0.3.0-win32.zip](http://www.javascriptlint.com/download/jsl-0.3.0-win32.zip)) +* JavaScript Lint 0.3.0 for Mac OS X Intel ([jsl-0.3.0-mac.tar.gz](http://www.javascriptlint.com/download/jsl-0.3.0-mac.tar.gz)) +* JavaScript Lint 0.3.0 Source ([jsl-0.3.0-src.tar.gz](http://www.javascriptlint.com/download/jsl-0.3.0-src.tar.gz)) + +Developers can also check out from the Subversion trunk: + +> svn co https://javascriptlint.svn.sourceforge.net/svnroot/javascriptlint/trunk jsl + +The [documentation](/docs/) explains the different ways that you can run JavaScript Lint. + +JavaScript Lint continues to be improved. If you encounter problems or have suggestions or revisions, please visit the project [forum](http://sourceforge.net/forum/?group_id=168518) and [tracker](http://sourceforge.net/tracker/?group_id=168518). + +Acknowledgements +================ + +We all stand on the shoulders of giants. I would like to especially acknowledge [Douglas Crockford's](http://www.crockford.com/) work on [JSLint](http://www.jslint.com/). This lint is itself written in JavaScript and is an interesting and rather sophisticated script. Crockford's ideas about good coding practices served as a springboard for many of these lint rules. + +We are all indebted to the many people who have worked hard to develop Firefox. + Property changes on: trunk/www/download.htm ___________________________________________________________________ Added: svn:eol-style + native Added: trunk/www/index.htm =================================================================== --- trunk/www/index.htm (rev 0) +++ trunk/www/index.htm 2009-10-06 16:05:47 UTC (rev 262) @@ -0,0 +1,40 @@ +<!-- +@title=JavaScript Lint +--> + +What It Is +========== + +Many JavaScript implementations do not warn against questionable coding practices. Yes, that's nice for the site that "works best with Internet Explorer" (designed with templates, scripted with snippets copied from forums). But it's a nightmare when you actually want to write quality, maintainable code. + +That's where JavaScript Lint comes in. With JavaScript Lint, you can check all your JavaScript source code for common mistakes without actually running the script or opening the web page. + +JavaScript Lint holds an advantage over competing lints because it is based on the JavaScript engine for the [Firefox browser](http://www.spreadfirefox.com/?q=affiliates&id=123423&t=70). This provides a robust framework that can not only check JavaScript syntax but also examine the coding techniques used in the script and warn against questionable practices. + +What It Does +============ + +Here are some common mistakes that JavaScript Lint looks for: + +* Missing semicolons at the end of a line. +* Curly braces without an _if_, _for_, _while_, etc. +* Code that is never run because of a _return_, _throw_, _continue_, or _break_. +* Case statements in a switch that do not have a _break_ statement. +* Leading and trailing decimal points on a number. +* A leading zero that turns a number into octal (base 8). +* Comments within comments. +* Ambiguity whether two adjacent lines are part of the same statement. +* Statements that don't do anything. + +JavaScript Lint also looks for the following less common mistakes: + +* Regular expressions that are not preceded by a left parenthesis, assignment, colon, or comma. +* Statements that are separated by commas instead of semicolons. +* Use of increment (++) and decrement (--) except for simple statements such as "i++;" or "--i;". +* Use of the _void_ type. +* Successive plus (e.g. x+++y) or minus (e.g. x---y) signs. +* Use of labeled _for_ and _while_ loops. +* _if_, _for_, _while_, etc. without curly braces. (This check is disabled by default.) + +Advanced users can also configure JavaScript Lint to check for [undeclared identifiers](docs/). + Property changes on: trunk/www/index.htm ___________________________________________________________________ Added: svn:eol-style + native Added: trunk/www/online_lint.php =================================================================== --- trunk/www/online_lint.php (rev 0) +++ trunk/www/online_lint.php 2009-10-06 16:05:47 UTC (rev 262) @@ -0,0 +1,22 @@ +<!-- +@title=The Online Lint +--> + +Online Lint +=========== + +All warnings except "option explicit" are enabled in this online edition, although certain warnings are displayed only once. For full control over which warnings are displayed, [download the JavaScript Lint software](/download.htm). + +If you encounter any problems, please report them to Matthias Miller at Info<at>JavaScriptLint.com. + + +<form method="POST" action=""> +<p>Paste your JavaScript, HTML, or URL into the box below:</p> +<p> + <textarea name="script" rows="15" cols="75" style="width: 100%"></textarea> +</p> +<p> + <input type="submit" value="Lint"/> + +</p> +</form> Added: trunk/www/static/feed-icon-32x32.png =================================================================== (Binary files differ) Property changes on: trunk/www/static/feed-icon-32x32.png ___________________________________________________________________ Added: svn:mime-type + application/octet-stream Added: trunk/www/static/global.css =================================================================== --- trunk/www/static/global.css (rev 0) +++ trunk/www/static/global.css 2009-10-06 16:05:47 UTC (rev 262) @@ -0,0 +1,71 @@ +body +{ + font-family: Verdana,Arial,Helvetica,sans-serif; + color: #222; + background: #FFF; +} + +#title +{ + border-bottom: 2px solid #669; + color: #669; + font-size: 4em; + margin-bottom: 0.25em; +} + +#nav +{ + float: left; +} + +#nav ul +{ + margin: 0; + padding: 0; +} + +#nav li a +{ + background: #EEF; + color: #000; + border: 1px solid #AAC; + display: block; + text-decoration: none; + margin: 0.25em; + padding: 0.25em; + text-align: center; + -moz-border-radius: 5px; + -webkit-border-radius: 5px; +} + +#nav li a.active +{ + cursor: default; +} + +#nav li a.active, +#nav li a:hover +{ + background: #335; + border-color: #334; + color: #FFF; +} + +h1 +{ + color: #003; +} + +#body +{ + margin-left: 9em; +} + +#footer +{ + color: #666; + font-size: 0.8em; + font-style: italic; + text-align: right; +} + Property changes on: trunk/www/static/global.css ___________________________________________________________________ Added: svn:eol-style + native Added: trunk/www.py =================================================================== --- trunk/www.py (rev 0) +++ trunk/www.py 2009-10-06 16:05:47 UTC (rev 262) @@ -0,0 +1,98 @@ +#!/usr/bin/python +# vim: ts=4 sw=4 expandtab +import BaseHTTPServer +import re +import os +import sys + +import markdown + +DOC_ROOT = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'www') +TEMPLATE_PATH = os.path.join(DOC_ROOT, '__template__') + +NAV = [ + ('/', 'Home'), + ('/download.htm', 'Download'), + ('/online_lint.php', 'The Online Lint'), + ('/docs/', 'Documentation'), + ('/news.php', 'News'), + ('/contact_support.htm', 'Contact'), +] + +def _resolve_url(url): + urls = [ + url.rstrip('/') + '/index.htm', + url + ] + for url in urls: + path = os.path.join(DOC_ROOT, url.lstrip('/')) + if path.startswith(DOC_ROOT + os.sep) and os.path.isfile(path): + return path + +def _get_nav(path): + nav = [] + for url, name in NAV: + navpath = _resolve_url(url) + if navpath and navpath == path: + nav.append('* <a class="active">%s</a>' % name) + else: + nav.append('* [%s](%s)' % (name, url)) + return markdown.markdown('\n'.join(nav)) + +def _transform_file(path): + source = open(path).read() + if path.endswith('.css'): + return 'text/css', source + elif path.endswith('.gif'): + return 'image/gif', source + elif path.endswith('.png'): + return 'image/png', source + elif path.endswith('.htm') or path.endswith('.php'): + body = markdown.markdown(source) + keywords = re.findall(r'^@(\w+)=(.*)$', source, re.MULTILINE) + # TODO: encode + keywords = dict(keywords) + keywords['body'] = body + keywords['nav'] = _get_nav(path) + page = open(TEMPLATE_PATH).read() % keywords + return 'text/html', page + else: + raise ValueError, 'Invalid file type: %s' % path + +class _Handler(BaseHTTPServer.BaseHTTPRequestHandler): + def do_GET(self): + path = _resolve_url(self.path) + if path: + self._send_response(*_transform_file(path)) + else: + self.send_error(404, "File not found") + + def _send_response(self, contenttype, content): + self.send_response(200) + self.send_header("Content-type", contenttype) + self.send_header("Content-length", str(len(content))) + self.end_headers() + self.wfile.write(content) + + +def runserver(): + server_address = ('', 8000) + httpd = BaseHTTPServer.HTTPServer(server_address, _Handler) + httpd.serve_forever() + +def main(action=''): + if action == 'server': + runserver() + return + # TODO: Implement 'build'. + print >>sys.stderr, """\ +Usage: www.py [server|build] + +server runs a test server on localhost +build generates static HTML files from the markup +""" + sys.exit(1) + +if __name__ == '__main__': + main(*sys.argv[1:]) + Property changes on: trunk/www.py ___________________________________________________________________ Added: svn:executable + * Added: svn:eol-style + native This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <mat...@us...> - 2009-10-05 06:04:18
|
Revision: 261 http://javascriptlint.svn.sourceforge.net/javascriptlint/?rev=261&view=rev Author: matthiasmiller Date: 2009-10-05 06:04:04 +0000 (Mon, 05 Oct 2009) Log Message: ----------- Initial work to support py2exe on Windows. Modified Paths: -------------- trunk/INSTALL trunk/Makefile.SpiderMonkey trunk/setup.py Modified: trunk/INSTALL =================================================================== --- trunk/INSTALL 2009-10-03 19:20:23 UTC (rev 260) +++ trunk/INSTALL 2009-10-05 06:04:04 UTC (rev 261) @@ -3,11 +3,16 @@ * Windows Prequisites: * Visual Studio 2003 * Python + * py2exe * MozillaBuild (http://developer.mozilla.org/en/docs/Windows_Build_Prerequisites) Launch the MozillaBuild 7.1 batch file. (You may have to run this as an Administrator on Windows Vista.) Run the commands in that shell. + If you don't have a copy of Visual Studio 2003, you can download the Python + source code and compile it yourself. Python C extensions must be built with + the same compiler as the Python interpreter. + On all platforms: $ python setup.py build Modified: trunk/Makefile.SpiderMonkey =================================================================== --- trunk/Makefile.SpiderMonkey 2009-10-03 19:20:23 UTC (rev 260) +++ trunk/Makefile.SpiderMonkey 2009-10-05 06:04:04 UTC (rev 261) @@ -46,7 +46,7 @@ $(COPY_LIB): $(BUILD_DIR) $(ORIG_LIB) cp $(ORIG_LIB) $(COPY_LIB) -$(COPY_DLL): $(ORIG_LIB) +$(COPY_DLL): $(ORIG_DLL) cp $(ORIG_DLL) $(COPY_DLL) $(OS_HEADER): $(BUILD_DIR) Modified: trunk/setup.py =================================================================== --- trunk/setup.py 2009-10-03 19:20:23 UTC (rev 260) +++ trunk/setup.py 2009-10-05 06:04:04 UTC (rev 261) @@ -11,24 +11,26 @@ pass def _runmakefiles(distutils_dir, build_opt=1, args=[]): + args = ['BUILD_OPT=%i' % build_opt] + if distutils_dir: + args.append('DISTUTILS_DIR=%s' % distutils_dir) + # First build SpiderMonkey. ret = subprocess.call(['make', '-f', 'Makefile.ref', '-C', - 'spidermonkey/src', 'BUILD_OPT=%i' % build_opt] + \ - args) + 'spidermonkey/src'] + args) if ret != 0: raise _MakefileError, 'Error running make.' # Then copy the files to the build directory. - env = dict(os.environ) - if distutils_dir: - env['DISTUTILS_DIR'] = distutils_dir - ret = subprocess.call(['make', '-f', 'Makefile.SpiderMonkey', - 'BUILD_OPT=%i' % build_opt] + args, env=env) + ret = subprocess.call(['make', '-f', 'Makefile.SpiderMonkey'] + args) if ret != 0: raise _MakefileError, 'Error running make.' class _MyBuild(distutils.command.build.build): def run(self): + # py2exe is calling reinitialize_command without finalizing. + self.ensure_finalized() + _runmakefiles(self.build_platlib) distutils.command.build.build.run(self) @@ -75,7 +77,7 @@ pass else: args.update( - console = ['jsl.py'], + console = ['jsl'], options = { 'py2exe': { 'excludes': ['javascriptlint.spidermonkey_'], This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <mat...@us...> - 2009-10-03 19:20:29
|
Revision: 260 http://javascriptlint.svn.sourceforge.net/javascriptlint/?rev=260&view=rev Author: matthiasmiller Date: 2009-10-03 19:20:23 +0000 (Sat, 03 Oct 2009) Log Message: ----------- Python 2.4 doesn't have subprocess.check_call. Modified Paths: -------------- trunk/setup.py Modified: trunk/setup.py =================================================================== --- trunk/setup.py 2009-10-03 18:17:28 UTC (rev 259) +++ trunk/setup.py 2009-10-03 19:20:23 UTC (rev 260) @@ -7,18 +7,25 @@ import subprocess import sys +class _MakefileError(Exception): + pass + def _runmakefiles(distutils_dir, build_opt=1, args=[]): # First build SpiderMonkey. - subprocess.check_call(['make', '-f', 'Makefile.ref', '-C', + ret = subprocess.call(['make', '-f', 'Makefile.ref', '-C', 'spidermonkey/src', 'BUILD_OPT=%i' % build_opt] + \ - args) + args) + if ret != 0: + raise _MakefileError, 'Error running make.' # Then copy the files to the build directory. env = dict(os.environ) if distutils_dir: env['DISTUTILS_DIR'] = distutils_dir - subprocess.check_call(['make', '-f', 'Makefile.SpiderMonkey', - 'BUILD_OPT=%i' % build_opt] + args, env=env) + ret = subprocess.call(['make', '-f', 'Makefile.SpiderMonkey', + 'BUILD_OPT=%i' % build_opt] + args, env=env) + if ret != 0: + raise _MakefileError, 'Error running make.' class _MyBuild(distutils.command.build.build): def run(self): This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <mat...@us...> - 2009-10-03 18:17:35
|
Revision: 259 http://javascriptlint.svn.sourceforge.net/javascriptlint/?rev=259&view=rev Author: matthiasmiller Date: 2009-10-03 18:17:28 +0000 (Sat, 03 Oct 2009) Log Message: ----------- Improve documentation for the output format. Modified Paths: -------------- trunk/javascriptlint/conf.py Modified: trunk/javascriptlint/conf.py =================================================================== --- trunk/javascriptlint/conf.py 2009-10-03 18:10:05 UTC (rev 258) +++ trunk/javascriptlint/conf.py 2009-10-03 18:17:28 UTC (rev 259) @@ -36,8 +36,15 @@ # __FILE__ indicates current file path # __FILENAME__ indicates current file name # __LINE__ indicates current line -# __ERROR__ indicates error message +# __COL__ indicates current column +# __ERROR__ indicates error message (__ERROR_PREFIX__: __ERROR_MSG__) +# __ERROR_NAME__ indicates error name (used in configuration file) +# __ERROR_PREFIX__ indicates error prefix +# __ERROR_MSG__ indicates error message # +# For machine-friendly output, the output format can be prefixed with +# "encode:". If specified, all items will be encoded with C-slashes. +# # Visual Studio syntax (default): +output-format __FILE__(__LINE__): __ERROR__ # Alternative syntax: This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
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-03 17:39:51
|
Revision: 257 http://javascriptlint.svn.sourceforge.net/javascriptlint/?rev=257&view=rev Author: matthiasmiller Date: 2009-10-03 17:39:40 +0000 (Sat, 03 Oct 2009) Log Message: ----------- Break unreferenced_identifier into unreferenced_argument, unreferenced_variable, and unreferenced_function. Modified Paths: -------------- trunk/javascriptlint/lint.py trunk/javascriptlint/warnings.py trunk/test.py trunk/tests/warnings/unreferenced_identifier.js Modified: trunk/javascriptlint/lint.py =================================================================== --- trunk/javascriptlint/lint.py 2009-10-03 17:14:49 UTC (rev 256) +++ trunk/javascriptlint/lint.py 2009-10-03 17:39:40 UTC (rev 257) @@ -88,21 +88,31 @@ self._kids[-1]._parent = self self._kids[-1]._node = node return self._kids[-1] - def add_declaration(self, name, node): - self._identifiers[name] = node + def add_declaration(self, name, node, type_): + assert type_ in ('arg', 'function', 'var'), \ + 'Unrecognized identifier type: %s' % type_ + self._identifiers[name] = { + 'node': node, + 'type': type_ + } def add_reference(self, name, node): self._references.append((name, node)) def get_identifier(self, name): if name in self._identifiers: - return self._identifiers[name] + return self._identifiers[name]['node'] else: return None + def get_identifier_type(self, name): + if name in self._identifiers: + return self._identifiers[name]['type'] + else: + return None def get_identifiers(self): "returns a list of names" return self._identifiers.keys() def resolve_identifier(self, name): if name in self._identifiers: - return self, self._identifiers[name] + return self, self._identifiers[name]['node'] if self._parent: return self._parent.resolve_identifier(name) return None @@ -139,8 +149,8 @@ # Add all identifiers as unreferenced. Children scopes will remove # them if they are referenced. Variables need to be keyed by name # instead of node, because function parameters share the same node. - for name, node in self._identifiers.items(): - unreferenced[(self, name)] = node + for name, info in self._identifiers.items(): + unreferenced[(self, name)] = info['node'] # Remove all declared variables from the "unreferenced" set; add all # undeclared variables to the "undeclared" list. @@ -382,7 +392,7 @@ if declare_scope.get_identifier(name): report(node, 'redeclared_var', name=name) else: - declare_scope.add_declaration(name, node) + declare_scope.add_declaration(name, node, 'var') def _lint_script_parts(script_parts, script_cache, lint_error, conf, import_callback): def report_lint(node, errname, pos=None, **errargs): @@ -424,7 +434,15 @@ for ref_scope, name, node in unreferenced: # Ignore the outer scope. if ref_scope != scope: - report_lint(node, 'unreferenced_identifier', name=name) + type_ = ref_scope.get_identifier_type(name) + if type_ == 'arg': + report_lint(node, 'unreferenced_argument', name=name) + elif type_ == 'function': + report_lint(node, 'unreferenced_function', name=name) + elif type_ == 'var': + report_lint(node, 'unreferenced_variable', name=name) + else: + assert False, 'Unrecognized identifier type: %s' % type_ def _getreporter(visitor, report): def onpush(node): @@ -441,7 +459,7 @@ report(warning.node, visitor.warning, pos=pos, **warning.errargs) return onpush -def _warn_or_declare(scope, name, node, report): +def _warn_or_declare(scope, name, type_, node, report): parent_scope, other = scope.resolve_identifier(name) or (None, None) if other and other.kind == tok.FUNCTION and name in other.fn_args: report(node, 'var_hides_arg', name=name) @@ -449,7 +467,7 @@ report(node, 'redeclared_var', name=name) else: # TODO: Warn when hiding a variable in a parent scope. - scope.add_declaration(name, node) + scope.add_declaration(name, node, type_) def _get_scope_checks(scope, report): scopes = [scope] @@ -461,19 +479,19 @@ 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, node, report) + _warn_or_declare(scopes[-1], node.atom, 'var', node, report) return if node.parent.kind == tok.CATCH: - scopes[-1].add_declaration(node.atom, node) + scopes[-1].add_declaration(node.atom, node, 'var') scopes[-1].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) + _warn_or_declare(scopes[-1], node.fn_name, 'function', node, report) self._push_scope(node) for var_name in node.fn_args: - scopes[-1].add_declaration(var_name, node) + scopes[-1].add_declaration(var_name, node, 'arg') @visitation.visit('push', tok.LEXICALSCOPE, tok.WITH) def _push_scope(self, node): Modified: trunk/javascriptlint/warnings.py =================================================================== --- trunk/javascriptlint/warnings.py 2009-10-03 17:14:49 UTC (rev 256) +++ trunk/javascriptlint/warnings.py 2009-10-03 17:39:40 UTC (rev 257) @@ -75,7 +75,9 @@ '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_identifier': 'identifier is declared but never referenced: {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', Modified: trunk/test.py =================================================================== --- trunk/test.py 2009-10-03 17:14:49 UTC (rev 256) +++ trunk/test.py 2009-10-03 17:39:40 UTC (rev 257) @@ -10,7 +10,9 @@ _DEFAULT_CONF = """ # This warning triggers a lot of warnings in many of the tests, so only enable # it when specifically testing it. --unreferenced_identifier +-unreferenced_argument +-unreferenced_function +-unreferenced_variable """ class TestError(Exception): Modified: trunk/tests/warnings/unreferenced_identifier.js =================================================================== --- trunk/tests/warnings/unreferenced_identifier.js 2009-10-03 17:14:49 UTC (rev 256) +++ trunk/tests/warnings/unreferenced_identifier.js 2009-10-03 17:39:40 UTC (rev 257) @@ -1,11 +1,13 @@ /* The tests disable this warning by default becaues of noise. Enable it. */ -/*conf:+unreferenced_identifier*/ +/*conf:+unreferenced_argument*/ +/*conf:+unreferenced_function*/ +/*conf:+unreferenced_variable*/ /* outer-level functions shouldn't warn */ var unreferenced_global; function unreferenced_identifier() { /* Test an unreferenced function. */ - function unreferenced_func() { /*warning:unreferenced_identifier*/ + function unreferenced_func() { /*warning:unreferenced_function*/ return true; } function referenced_func() { @@ -14,20 +16,20 @@ referenced_var(); /* Test an unreferenced parameter. */ - var z = new function(unreferenced_parm) { /*warning:unreferenced_identifier*/ + var z = new function(unreferenced_parm) { /*warning:unreferenced_argument*/ }; z.prop = 42; /* Test an unreferenced variable. */ - var unreferenced_variable = 100; /*warning:unreferenced_identifier*/ + var unreferenced_variable = 100; /*warning:unreferenced_variable*/ /* An unreferenced duplicate parameter should give one warning. */ - function func_with_dup(unref_dup_parm, unref_dup_parm) { /*warning:unreferenced_identifier*/ /*warning:duplicate_formal*/ + function func_with_dup(unref_dup_parm, unref_dup_parm) { /*warning:unreferenced_argument*/ /*warning:duplicate_formal*/ } func_with_dup(); /* An unreferenced duplicate variable should give one warning. */ - var unref_dup_var; /*warning:unreferenced_identifier*/ + var unref_dup_var; /*warning:unreferenced_variable*/ var unref_dup_var; /*warning:redeclared_var*/ /* Test a try/catch. The error doesn't need to be referenced. */ @@ -51,7 +53,7 @@ } /* Test assignments. */ - var assigned_but_unref; /*warning:unreferenced_identifier*/ + var assigned_but_unref; /*warning:unreferenced_variable*/ assigned_but_unref = 42; function callback() { @@ -60,9 +62,9 @@ (assigned_but_ref = callback)(); /* Test increment and decrement. */ - var unref_inc; /*warning:unreferenced_identifier*/ + var unref_inc; /*warning:unreferenced_variable*/ unref_inc++; - var unref_dec; /*warning:unreferenced_identifier*/ + var unref_dec; /*warning:unreferenced_variable*/ unref_dec--; var tmp; @@ -73,7 +75,7 @@ tmp = -tmp; /* Test named functions as references. */ - var fn = function ref_func() { return 42; }; /*warning:unreferenced_identifier*/ + var fn = function ref_func() { return 42; }; /*warning:unreferenced_function*/ fn(); /* Test nested scopes. */ This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <mat...@us...> - 2009-10-03 17:14:56
|
Revision: 256 http://javascriptlint.svn.sourceforge.net/javascriptlint/?rev=256&view=rev Author: matthiasmiller Date: 2009-10-03 17:14:49 +0000 (Sat, 03 Oct 2009) Log Message: ----------- Move spidermonkey_ into a subdirectory to keep it from being installed. Modified Paths: -------------- trunk/javascriptlint/spidermonkey.py trunk/setup.py Added Paths: ----------- trunk/javascriptlint/pyspidermonkey_/ trunk/javascriptlint/pyspidermonkey_/__init__.py Removed Paths: ------------- trunk/javascriptlint/spidermonkey_.py Property changes on: trunk/javascriptlint/pyspidermonkey_ ___________________________________________________________________ Added: svn:ignore + *.pyc Copied: trunk/javascriptlint/pyspidermonkey_/__init__.py (from rev 255, trunk/javascriptlint/spidermonkey_.py) =================================================================== --- trunk/javascriptlint/pyspidermonkey_/__init__.py (rev 0) +++ trunk/javascriptlint/pyspidermonkey_/__init__.py 2009-10-03 17:14:49 UTC (rev 256) @@ -0,0 +1,21 @@ +# vim: ts=4 sw=4 expandtab +from distutils.core import setup, Extension +import os +import sys + +# Add the bin directory to the module search path +def _get_lib_path(): + import distutils.dist + import distutils.command.build + dist = distutils.dist.Distribution() + build = distutils.command.build.build(dist) + build.finalize_options() + return os.path.join(os.path.dirname(__file__), '..', '..', + build.build_platlib, 'javascriptlint') + +sys.path.insert(0, _get_lib_path()) +try: + from pyspidermonkey import * +finally: + sys.path.pop(0) + Modified: trunk/javascriptlint/spidermonkey.py =================================================================== --- trunk/javascriptlint/spidermonkey.py 2009-10-03 16:53:02 UTC (rev 255) +++ trunk/javascriptlint/spidermonkey.py 2009-10-03 17:14:49 UTC (rev 256) @@ -4,7 +4,7 @@ # import the development version first, and if that fails, it goes after the # real version. try: - from spidermonkey_ import * + from pyspidermonkey_ import * except ImportError: from pyspidermonkey import * Deleted: trunk/javascriptlint/spidermonkey_.py =================================================================== --- trunk/javascriptlint/spidermonkey_.py 2009-10-03 16:53:02 UTC (rev 255) +++ trunk/javascriptlint/spidermonkey_.py 2009-10-03 17:14:49 UTC (rev 256) @@ -1,20 +0,0 @@ -# vim: ts=4 sw=4 expandtab -from distutils.core import setup, Extension -import os -import sys - -# Add the bin directory to the module search path -def _get_lib_path(): - import distutils.dist - import distutils.command.build - dist = distutils.dist.Distribution() - build = distutils.command.build.build(dist) - build.finalize_options() - return os.path.join(os.path.dirname(__file__), '..', build.build_platlib, 'javascriptlint') - -sys.path.insert(0, _get_lib_path()) -try: - from pyspidermonkey import * -finally: - sys.path.pop(0) - Modified: trunk/setup.py =================================================================== --- trunk/setup.py 2009-10-03 16:53:02 UTC (rev 255) +++ trunk/setup.py 2009-10-03 17:14:49 UTC (rev 256) @@ -59,7 +59,7 @@ cmdclass = cmdclass, description = 'JavaScript Lint', ext_modules = [pyspidermonkey], - packages = ['javascriptlint', 'javascriptlint.pyjsl'], + packages = ['javascriptlint'], scripts = ['jsl'] ) try: @@ -71,7 +71,7 @@ console = ['jsl.py'], options = { 'py2exe': { - 'excludes': ['pyjsl.spidermonkey_'], + 'excludes': ['javascriptlint.spidermonkey_'], 'bundle_files': 1 } }, This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <mat...@us...> - 2009-10-03 16:53:15
|
Revision: 255 http://javascriptlint.svn.sourceforge.net/javascriptlint/?rev=255&view=rev Author: matthiasmiller Date: 2009-10-03 16:53:02 +0000 (Sat, 03 Oct 2009) Log Message: ----------- Move the contents of pyjsl out one level. Modified Paths: -------------- trunk/javascriptlint/jsl.py trunk/test.py Added Paths: ----------- trunk/javascriptlint/conf.py trunk/javascriptlint/htmlparse.py trunk/javascriptlint/jsparse.py trunk/javascriptlint/lint.py trunk/javascriptlint/spidermonkey.py trunk/javascriptlint/spidermonkey_.py trunk/javascriptlint/util.py trunk/javascriptlint/visitation.py trunk/javascriptlint/warnings.py Removed Paths: ------------- trunk/javascriptlint/pyjsl/ Copied: trunk/javascriptlint/conf.py (from rev 254, trunk/javascriptlint/pyjsl/conf.py) =================================================================== --- trunk/javascriptlint/conf.py (rev 0) +++ trunk/javascriptlint/conf.py 2009-10-03 16:53:02 UTC (rev 255) @@ -0,0 +1,136 @@ +# vim: ts=4 sw=4 expandtab +import os + +import warnings + +class ConfError(Exception): + def __init__(self, error): + Exception.__init__(self, error) + self.lineno = None + self.path = None + +class Setting: + wants_parm = False + wants_dir = False + +class BooleanSetting(Setting): + wants_parm = False + def __init__(self, default): + self.value = default + def load(self, enabled): + self.value = enabled + +class StringSetting(Setting): + wants_parm = True + def __init__(self, default): + self.value = default + def load(self, enabled, parm): + if not enabled: + raise ConfError, 'Expected +.' + self.value = parm + +class DeclareSetting(Setting): + wants_parm = True + def __init__(self): + self.value = [] + def load(self, enabled, parm): + if not enabled: + raise ConfError, 'Expected +.' + self.value.append(parm) + +class ProcessSetting(Setting): + wants_parm = True + wants_dir = True + def __init__(self, recurse_setting): + self.value = [] + self._recurse = recurse_setting + def load(self, enabled, parm, dir): + if dir: + parm = os.path.join(dir, parm) + self.value.append((self._recurse.value, parm)) + +class Conf: + def __init__(self): + 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), + 'define': DeclareSetting(), + 'context': BooleanSetting(False), + 'process': ProcessSetting(recurse), + # SpiderMonkey warnings + 'no_return_value': BooleanSetting(True), + 'equal_as_assign': BooleanSetting(True), + 'anon_no_return_value': BooleanSetting(True) + } + for name in warnings.warnings: + self._settings[name] = BooleanSetting(True) + self.loadline('-block_without_braces') + + def loadfile(self, path): + path = os.path.abspath(path) + conf = open(path, 'r').read() + try: + self.loadtext(conf, dir=os.path.dirname(path)) + except ConfError, error: + error.path = path + raise + + def loadtext(self, conf, dir=None): + lines = conf.splitlines() + for lineno in range(0, len(lines)): + try: + self.loadline(lines[lineno], dir) + except ConfError, error: + error.lineno = lineno + raise + + def loadline(self, line, dir=None): + assert not '\r' in line + assert not '\n' in line + + # Allow comments + if '#' in line: + line = line[:line.find('#')] + line = line.rstrip() + if not line: + return + + # Parse the +/- + if line.startswith('+'): + enabled = True + elif line.startswith('-'): + enabled = False + else: + raise ConfError, 'Expected + or -.' + line = line[1:] + + # Parse the key/parms + name = line.split()[0].lower() + parm = line[len(name):].lstrip() + + # Load the setting + setting = self._settings[name] + args = { + 'enabled': enabled + } + if setting.wants_parm: + args['parm'] = parm + elif parm: + raise ConfError, 'The %s setting does not expect a parameter.' % name + if setting.wants_dir: + args['dir'] = dir + setting.load(**args) + + def __getitem__(self, name): + if name == 'paths': + name = 'process' + elif name == 'declarations': + name = 'define' + return self._settings[name].value + Property changes on: trunk/javascriptlint/conf.py ___________________________________________________________________ Added: svn:mergeinfo + Added: svn:eol-style + native Copied: trunk/javascriptlint/htmlparse.py (from rev 254, trunk/javascriptlint/pyjsl/htmlparse.py) =================================================================== --- trunk/javascriptlint/htmlparse.py (rev 0) +++ trunk/javascriptlint/htmlparse.py 2009-10-03 16:53:02 UTC (rev 255) @@ -0,0 +1,89 @@ +# vim: ts=4 sw=4 expandtab +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 = [] + + 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 + } + + 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 + + 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 _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): + parser = _Parser() + parser.feed(s) + parser.close() + return parser.getscripts() + +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) Property changes on: trunk/javascriptlint/htmlparse.py ___________________________________________________________________ Added: svn:mergeinfo + Added: svn:eol-style + native Modified: trunk/javascriptlint/jsl.py =================================================================== --- trunk/javascriptlint/jsl.py 2009-10-03 16:41:05 UTC (rev 254) +++ trunk/javascriptlint/jsl.py 2009-10-03 16:53:02 UTC (rev 255) @@ -7,11 +7,11 @@ import unittest from optparse import OptionParser -import pyjsl.conf -import pyjsl.htmlparse -import pyjsl.jsparse -import pyjsl.lint -import pyjsl.util +import conf +import htmlparse +import jsparse +import lint +import util _lint_results = { 'warnings': 0, @@ -20,15 +20,15 @@ def _dump(paths): for path in paths: - script = pyjsl.util.readfile(path) - pyjsl.jsparse.dump_tree(script) + script = util.readfile(path) + jsparse.dump_tree(script) -def _lint(paths, conf): +def _lint(paths, conf_): def lint_error(path, line, col, errname, errdesc): _lint_results['warnings'] = _lint_results['warnings'] + 1 - print pyjsl.util.format_error(conf['output-format'], path, line, col, + print util.format_error(conf_['output-format'], path, line, col, errname, errdesc) - pyjsl.lint.lint_files(paths, lint_error, conf=conf) + lint.lint_files(paths, lint_error, conf=conf_) def _resolve_paths(path, recurse): if os.path.isfile(path): @@ -88,9 +88,9 @@ parser.print_help() sys.exit() - conf = pyjsl.conf.Conf() + conf_ = conf.Conf() if options.conf: - conf.loadfile(options.conf) + conf_.loadfile(options.conf) profile_func = _profile_disabled if options.profile: @@ -98,21 +98,21 @@ if options.unittest: suite = unittest.TestSuite(); - for module in [pyjsl.htmlparse, pyjsl.jsparse, pyjsl.util]: + for module in [htmlparse, jsparse, util]: suite.addTest(unittest.findTestCases(module)) runner = unittest.TextTestRunner(verbosity=options.verbosity) runner.run(suite) paths = [] - for recurse, path in conf['paths']: + for recurse, path in conf_['paths']: paths.extend(_resolve_paths(path, recurse)) for arg in args: paths.extend(_resolve_paths(arg, False)) if options.dump: profile_func(_dump, paths) else: - profile_func(_lint, paths, conf) + profile_func(_lint, paths, conf_) if _lint_results['errors']: sys.exit(3) Copied: trunk/javascriptlint/jsparse.py (from rev 254, trunk/javascriptlint/pyjsl/jsparse.py) =================================================================== --- trunk/javascriptlint/jsparse.py (rev 0) +++ trunk/javascriptlint/jsparse.py 2009-10-03 16:53:02 UTC (rev 255) @@ -0,0 +1,414 @@ +#!/usr/bin/python +# vim: ts=4 sw=4 expandtab +""" Parses a script into nodes. """ +import bisect +import re +import unittest + +import spidermonkey +from spidermonkey 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)] +)) + +NodePos = spidermonkey.NodePos + +class NodePositions: + " Given a string, allows [x] lookups for NodePos line and column numbers." + def __init__(self, text, start_pos=None): + # Find the length of each line and incrementally sum all of the lengths + # to determine the ending position of each line. + self._start_pos = start_pos + 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] + if self._start_pos: + if line == 0: + col += self._start_pos.col + line += self._start_pos.line + return NodePos(line, col) + def to_offset(self, pos): + pos = self._to_rel_pos(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 + start, end = self._to_rel_pos(start), self._to_rel_pos(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) + def _to_rel_pos(self, pos): + " converts a position to a position relative to self._start_pos " + if not self._start_pos: + return pos + line, col = pos.line, pos.col + line -= self._start_pos.line + if line == 0: + col -= self._start_pos.col + assert line >= 0 and col >= 0 # out-of-bounds node position + return NodePos(line, col) + +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 findpossiblecomments(script, node_positions): + 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() + end_offset = match.end()-1 + + 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) + + # Start searching immediately after the start of the comment in case + # this one was within a string or a regexp. + pos = match.start()+1 + +def parse(script, error_callback, startpos=None): + """ All node positions will be relative to startpos. This allows scripts + to be embedded in a file (for example, HTML). + """ + def _wrapped_callback(line, col, msg): + assert msg.startswith('JSMSG_') + msg = msg[6:].lower() + error_callback(line, col, msg) + + startpos = startpos or NodePos(0,0) + return spidermonkey.parse(script, _Node, _wrapped_callback, + startpos.line, startpos.col) + +def filtercomments(possible_comments, node_positions, root_node): + comment_ignore_ranges = NodeRanges() + + def process(node): + if node.kind == tok.NUMBER: + node.atom = node_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 = node_positions.to_offset(node.start_pos()) + end_offset = node_positions.to_offset(node.end_pos()) - 1 + comment_ignore_ranges.add(start_offset, end_offset) + for kid in node.kids: + if kid: + process(kid) + process(root_node) + + comments = [] + for comment in possible_comments: + start_offset = node_positions.to_offset(comment.start_pos()) + end_offset = node_positions.to_offset(comment.end_pos()) + if comment_ignore_ranges.has(start_offset): + continue + comment_ignore_ranges.add(start_offset, end_offset) + comments.append(comment) + return comments + +def findcomments(script, root_node, start_pos=None): + node_positions = NodePositions(script, start_pos) + possible_comments = findpossiblecomments(script, node_positions) + return filtercomments(possible_comments, node_positions, root_node) + +def is_compilable_unit(script): + return spidermonkey.is_compilable_unit(script) + +def _dump_node(node, depth=0): + if node is None: + print ' '*depth, + print '(None)' + print + else: + print ' '*depth, + print '%s, %s' % (_tok_names[node.kind], _op_names[node.opcode]) + print ' '*depth, + print '%s - %s' % (node.start_pos(), node.end_pos()) + if hasattr(node, 'atom'): + print ' '*depth, + print 'atom: %s' % node.atom + if node.no_semi: + print ' '*depth, + print '(no semicolon)' + print + 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 = parse(script, error_callback) + _dump_node(node) + +class TestComments(unittest.TestCase): + def _test(self, script, expected_comments): + root = parse(script, lambda line, col, msg: None) + comments = findcomments(script, root) + 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) + def testStartPos(self): + pos = NodePositions('abc\r\ndef\n\nghi', NodePos(3,4)) + self.assertEquals(pos.to_offset(NodePos(3, 4)), 0) + self.assertEquals(pos.to_offset(NodePos(3, 5)), 1) + self.assertEquals(pos.from_offset(0), NodePos(3, 4)) + self.assertEquals(pos.text(NodePos(3, 4), NodePos(3, 4)), 'a') + self.assertEquals(pos.text(NodePos(3, 4), NodePos(3, 6)), 'abc') + self.assertEquals(pos.text(NodePos(3, 6), NodePos(4, 2)), 'c\r\ndef') + +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) + +class TestCompilableUnit(unittest.TestCase): + def test(self): + tests = ( + ('var s = "', False), + ('bogon()', True), + ('int syntax_error;', True), + ('a /* b', False), + ('re = /.*', False), + ('{ // missing curly', False) + ) + for text, result in tests: + self.assertEquals(is_compilable_unit(text), result) + # NOTE: This seems like a bug. + self.assert_(is_compilable_unit("/* test")) + +class TestLineOffset(unittest.TestCase): + def testErrorPos(self): + def geterror(script, startpos): + errors = [] + def onerror(line, col, msg): + errors.append((line, col, msg)) + parse(script, onerror, startpos) + self.assertEquals(len(errors), 1) + return errors[0] + self.assertEquals(geterror(' ?', None), (0, 1, 'syntax_error')) + self.assertEquals(geterror('\n ?', None), (1, 1, 'syntax_error')) + self.assertEquals(geterror(' ?', NodePos(1,1)), (1, 2, 'syntax_error')) + self.assertEquals(geterror('\n ?', NodePos(1,1)), (2, 1, 'syntax_error')) + def testNodePos(self): + def getnodepos(script, startpos): + root = parse(script, None, startpos) + self.assertEquals(root.kind, tok.LC) + var, = root.kids + self.assertEquals(var.kind, tok.VAR) + return var.start_pos() + self.assertEquals(getnodepos('var x;', None), NodePos(0,0)) + self.assertEquals(getnodepos(' var x;', None), NodePos(0,1)) + self.assertEquals(getnodepos('\n\n var x;', None), NodePos(2,1)) + self.assertEquals(getnodepos('var x;', NodePos(3,4)), NodePos(3,4)) + self.assertEquals(getnodepos(' var x;', NodePos(3,4)), NodePos(3,5)) + self.assertEquals(getnodepos('\n\n var x;', NodePos(3,4)), NodePos(5,1)) + def testComments(self): + def testcomment(comment, startpos, expectedpos): + root = parse(comment, None, startpos) + comment, = findcomments(comment, root, startpos) + self.assertEquals(comment.start_pos(), expectedpos) + for comment in ('/*comment*/', '//comment'): + testcomment(comment, None, NodePos(0,0)) + testcomment(' %s' % comment, None, NodePos(0,1)) + testcomment('\n\n %s' % comment, None, NodePos(2,1)) + testcomment('%s' % comment, NodePos(3,4), NodePos(3,4)) + testcomment(' %s' % comment, NodePos(3,4), NodePos(3,5)) + testcomment('\n\n %s' % comment, NodePos(3,4), NodePos(5,1)) + +if __name__ == '__main__': + unittest.main() + Property changes on: trunk/javascriptlint/jsparse.py ___________________________________________________________________ Added: svn:mergeinfo + Added: svn:eol-style + native Copied: trunk/javascriptlint/lint.py (from rev 254, trunk/javascriptlint/pyjsl/lint.py) =================================================================== --- trunk/javascriptlint/lint.py (rev 0) +++ trunk/javascriptlint/lint.py 2009-10-03 16:53:02 UTC (rev 255) @@ -0,0 +1,505 @@ +#!/usr/bin/python +# vim: ts=4 sw=4 expandtab +import os.path +import re + +import conf +import htmlparse +import jsparse +import visitation +import warnings +import util + +from spidermonkey import tok, op + +_newline_kinds = ( + 'eof', 'comma', 'dot', 'semi', 'colon', 'lc', 'rc', 'lp', 'rb', 'assign', + 'relop', 'hook', 'plus', 'minus', 'star', 'divop', 'eqop', 'shop', 'or', + 'and', 'bitor', 'bitxor', 'bitand', 'else', 'try' +) + +_globals = frozenset([ + 'Array', 'Boolean', 'Math', 'Number', 'String', 'RegExp', 'Script', 'Date', + 'isNaN', 'isFinite', 'parseFloat', 'parseInt', + 'eval', 'NaN', 'Infinity', + 'escape', 'unescape', 'uneval', + 'decodeURI', 'encodeURI', 'decodeURIComponent', 'encodeURIComponent', + 'Function', 'Object', + 'Error', 'InternalError', 'EvalError', 'RangeError', 'ReferenceError', + 'SyntaxError', 'TypeError', 'URIError', + 'arguments', 'undefined' +]) + +def _find_function(node): + while node and node.kind != tok.FUNCTION: + node = node.parent + return node + +def _find_functions(node): + functions = [] + while node: + if node.kind == tok.FUNCTION: + functions.append(node) + node = node.parent + return functions + +def _parse_control_comment(comment): + """ Returns None or (keyword, parms) """ + if comment.atom.lower().startswith('jsl:'): + control_comment = comment.atom[4:] + elif comment.atom.startswith('@') and comment.atom.endswith('@'): + control_comment = comment.atom[1:-1] + else: + return None + + control_comments = { + 'ignoreall': (False), + 'ignore': (False), + 'end': (False), + 'option explicit': (False), + 'import': (True), + 'fallthru': (False), + 'pass': (False), + 'declare': (True) + } + if control_comment.lower() in control_comments: + keyword = control_comment.lower() + else: + keyword = control_comment.lower().split()[0] + if not keyword in control_comments: + return None + + parms = control_comment[len(keyword):].strip() + return (comment, keyword, parms) + +class Scope: + """ Outer-level scopes will never be associated with a node. + Inner-level scopes will always be associated with a node. + """ + def __init__(self): + self._parent = None + self._kids = [] + self._identifiers = {} + self._references = [] + self._node = None + def add_scope(self, node): + assert not node is None + self._kids.append(Scope()) + self._kids[-1]._parent = self + self._kids[-1]._node = node + return self._kids[-1] + def add_declaration(self, name, node): + self._identifiers[name] = node + def add_reference(self, name, node): + self._references.append((name, node)) + def get_identifier(self, name): + if name in self._identifiers: + return self._identifiers[name] + else: + return None + def get_identifiers(self): + "returns a list of names" + return self._identifiers.keys() + def resolve_identifier(self, name): + if name in self._identifiers: + return self, self._identifiers[name] + if self._parent: + return self._parent.resolve_identifier(name) + return None + def get_unreferenced_and_undeclared_identifiers(self): + """ Returns a tuple of unreferenced and undeclared, where each is a list + of (scope, name, node) tuples. + """ + unreferenced = {} + undeclared = [] + self._find_unreferenced_and_undeclared(unreferenced, undeclared, False) + + # Convert "unreferenced" from a dictionary of: + # { (scope, name): node } + # to a list of: + # [ (scope, name, node) ] + # sorted by node position. + unreferenced = [(key[0], key[1], node) for key, node + in unreferenced.items()] + unreferenced.sort(key=lambda x: x[2].start_pos()) + + return unreferenced, undeclared + def _find_unreferenced_and_undeclared(self, unreferenced, undeclared, + is_in_with_scope): + """ unreferenced is a dictionary, such that: + (scope, name): node + } + undeclared is a list, such that: [ + (scope, name, node) + ] + """ + if self._node and self._node.kind == tok.WITH: + is_in_with_scope = True + + # Add all identifiers as unreferenced. Children scopes will remove + # them if they are referenced. Variables need to be keyed by name + # instead of node, because function parameters share the same node. + for name, node in self._identifiers.items(): + unreferenced[(self, name)] = node + + # Remove all declared variables from the "unreferenced" set; add all + # undeclared variables to the "undeclared" list. + for name, node in self._references: + resolved = self.resolve_identifier(name) + if resolved: + # Make sure this isn't an assignment. + if node.parent.kind in (tok.ASSIGN, tok.INC, tok.DEC) and \ + node.node_index == 0 and \ + node.parent.parent.kind == tok.SEMI: + continue + unreferenced.pop((resolved[0], name), None) + else: + # with statements cannot have undeclared identifiers. + if not is_in_with_scope: + undeclared.append((self, name, node)) + + for child in self._kids: + child._find_unreferenced_and_undeclared(unreferenced, undeclared, + is_in_with_scope) + def find_scope(self, node): + for kid in self._kids: + scope = kid.find_scope(node) + if scope: + return scope + + # Always add it to the outer scope. + if not self._parent: + assert not self._node + return self + + # Conditionally add it to an inner scope. + assert self._node + if (node.start_pos() >= self._node.start_pos() and \ + node.end_pos() <= self._node.end_pos()): + return self + +class _Script: + def __init__(self): + self._imports = set() + self.scope = Scope() + def importscript(self, script): + self._imports.add(script) + def hasglobal(self, name): + return not self._findglobal(name, set()) is None + def _findglobal(self, name, searched): + """ searched is a set of all searched scripts """ + # Avoid recursion. + if self in searched: + return + + # Check this scope. + if self.scope.get_identifier(name): + return self + searched.add(self) + + # Search imported scopes. + for script in self._imports: + global_ = script._findglobal(name, searched) + if global_: + return global_ + +def lint_files(paths, lint_error, conf=conf.Conf()): + def lint_file(path, kind): + def import_script(import_path): + # The user can specify paths using backslashes (such as when + # linting Windows scripts on a posix environment. + import_path = import_path.replace('\\', os.sep) + import_path = os.path.join(os.path.dirname(path), import_path) + return lint_file(import_path, 'js') + def _lint_error(*args): + return lint_error(normpath, *args) + + normpath = util.normpath(path) + if normpath in lint_cache: + return lint_cache[normpath] + print normpath + contents = util.readfile(path) + lint_cache[normpath] = _Script() + + script_parts = [] + if kind == 'js': + script_parts.append((None, contents)) + elif kind == 'html': + for script in htmlparse.findscripts(contents): + if script['src']: + other = import_script(script['src']) + lint_cache[normpath].importscript(other) + if script['script'].strip(): + script_parts.append((script['startpos'], script['script'])) + else: + assert False, 'Unsupported file kind: %s' % kind + + _lint_script_parts(script_parts, lint_cache[normpath], _lint_error, conf, import_script) + return lint_cache[normpath] + + lint_cache = {} + for path in paths: + ext = os.path.splitext(path)[1] + if ext.lower() in ['.htm', '.html']: + lint_file(path, 'html') + else: + lint_file(path, 'js') + +def _lint_script_part(scriptpos, script, script_cache, conf, ignores, + report_native, report_lint, import_callback): + def parse_error(row, col, msg): + if not msg in ('anon_no_return_value', 'no_return_value', + 'redeclared_var', 'var_hides_arg'): + parse_errors.append((jsparse.NodePos(row, col), msg)) + + def report(node, errname, pos=None, **errargs): + if errname == 'empty_statement' and node.kind == tok.LC: + for pass_ in passes: + if pass_.start_pos() > node.start_pos() and \ + pass_.end_pos() < node.end_pos(): + passes.remove(pass_) + return + + if errname == 'missing_break': + # Find the end of the previous case/default and the beginning of + # the next case/default. + assert node.kind in (tok.CASE, tok.DEFAULT) + prevnode = node.parent.kids[node.node_index-1] + expectedfallthru = prevnode.end_pos(), node.start_pos() + elif errname == 'missing_break_for_last_case': + # Find the end of the current case/default and the end of the + # switch. + assert node.parent.kind == tok.LC + expectedfallthru = node.end_pos(), node.parent.end_pos() + else: + expectedfallthru = None + + if expectedfallthru: + start, end = expectedfallthru + for fallthru in fallthrus: + # Look for a fallthru between the end of the current case or + # default statement and the beginning of the next token. + if fallthru.start_pos() > start and fallthru.end_pos() < end: + fallthrus.remove(fallthru) + return + + report_lint(node, errname, pos, **errargs) + + parse_errors = [] + declares = [] + import_paths = [] + fallthrus = [] + passes = [] + + node_positions = jsparse.NodePositions(script, scriptpos) + possible_comments = jsparse.findpossiblecomments(script, node_positions) + + root = jsparse.parse(script, parse_error, scriptpos) + if not root: + # Report errors and quit. + for pos, msg in parse_errors: + report_native(pos, msg) + return + + comments = jsparse.filtercomments(possible_comments, node_positions, root) + start_ignore = None + for comment in comments: + cc = _parse_control_comment(comment) + if cc: + node, keyword, parms = cc + if keyword == 'declare': + if not util.isidentifier(parms): + report(node, 'jsl_cc_not_understood') + else: + declares.append((parms, node)) + elif keyword == 'ignore': + if start_ignore: + report(node, 'mismatch_ctrl_comments') + else: + start_ignore = node + elif keyword == 'end': + if start_ignore: + ignores.append((start_ignore.start_pos(), node.end_pos())) + start_ignore = None + else: + report(node, 'mismatch_ctrl_comments') + elif keyword == 'import': + if not parms: + report(node, 'jsl_cc_not_understood') + else: + import_paths.append(parms) + elif keyword == 'fallthru': + fallthrus.append(node) + elif keyword == 'pass': + passes.append(node) + else: + if comment.opcode == 'c_comment': + # Look for nested C-style comments. + nested_comment = comment.atom.find('/*') + if nested_comment < 0 and comment.atom.endswith('/'): + nested_comment = len(comment.atom) - 1 + # Report at the actual error of the location. Add two + # characters for the opening two characters. + if nested_comment >= 0: + pos = node_positions.from_offset(node_positions.to_offset(comment.start_pos()) + 2 + nested_comment) + report(comment, 'nested_comment', pos=pos) + if comment.atom.lower().startswith('jsl:'): + report(comment, 'jsl_cc_not_understood') + elif comment.atom.startswith('@'): + report(comment, 'legacy_cc_not_understood') + if start_ignore: + report(start_ignore, 'mismatch_ctrl_comments') + + # Wait to report parse errors until loading jsl:ignore directives. + for pos, msg in parse_errors: + report_native(pos, msg) + + # Find all visitors and convert them into "onpush" callbacks that call "report" + 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] + + # Push the scope/variable checks. + visitation.make_visitors(visitors, [_get_scope_checks(script_cache.scope, report)]) + + # kickoff! + _lint_node(root, visitors) + + for fallthru in fallthrus: + report(fallthru, 'invalid_fallthru') + for fallthru in passes: + report(fallthru, 'invalid_pass') + + # Process imports by copying global declarations into the universal scope. + for path in import_paths: + script_cache.importscript(import_callback(path)) + + for name, node in declares: + declare_scope = script_cache.scope.find_scope(node) + if declare_scope.get_identifier(name): + report(node, 'redeclared_var', name=name) + else: + declare_scope.add_declaration(name, node) + +def _lint_script_parts(script_parts, script_cache, lint_error, conf, import_callback): + def report_lint(node, errname, pos=None, **errargs): + errdesc = warnings.format_error(errname, **errargs) + _report(pos or node.start_pos(), errname, errdesc, True) + + def report_native(pos, errname): + # TODO: Format the error. + _report(pos, errname, errname, False) + + def _report(pos, errname, errdesc, require_key): + try: + if not conf[errname]: + return + except KeyError, err: + if require_key: + raise + + for start, end in ignores: + if pos >= start and pos <= end: + return + + return lint_error(pos.line, pos.col, errname, errdesc) + + for scriptpos, script in script_parts: + ignores = [] + _lint_script_part(scriptpos, script, script_cache, conf, ignores, + report_native, report_lint, import_callback) + + scope = script_cache.scope + unreferenced, undeclared = scope.get_unreferenced_and_undeclared_identifiers() + for decl_scope, name, node in undeclared: + if name in conf['declarations']: + continue + if name in _globals: + continue + if not script_cache.hasglobal(name): + report_lint(node, 'undeclared_identifier', name=name) + for ref_scope, name, node in unreferenced: + # Ignore the outer scope. + if ref_scope != scope: + report_lint(node, 'unreferenced_identifier', name=name) + +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: + # TODO: This is ugly hardcoding to improve the error positioning of + # "missing_semicolon" errors. + if visitor.warning in ('missing_semicolon', 'missing_semicolon_for_lambda'): + pos = warning.node.end_pos() + else: + pos = None + report(warning.node, visitor.warning, pos=pos, **warning.errargs) + return onpush + +def _warn_or_declare(scope, name, node, report): + parent_scope, other = scope.resolve_identifier(name) or (None, None) + if other and other.kind == tok.FUNCTION and name in other.fn_args: + report(node, 'var_hides_arg', name=name) + elif other and parent_scope == scope: + report(node, 'redeclared_var', name=name) + else: + # TODO: Warn when hiding a variable in a parent scope. + scope.add_declaration(name, node) + +def _get_scope_checks(scope, report): + scopes = [scope] + + 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: + return # left side of object literal + if node.parent.kind == tok.VAR: + _warn_or_declare(scopes[-1], node.atom, node, report) + return + if node.parent.kind == tok.CATCH: + scopes[-1].add_declaration(node.atom, node) + scopes[-1].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) + + @visitation.visit('push', tok.LEXICALSCOPE, tok.WITH) + def _push_scope(self, node): + scopes.append(scopes[-1].add_scope(node)) + + @visitation.visit('pop', tok.FUNCTION, tok.LEXICALSCOPE, tok.WITH) + def _pop_scope(self, node): + scopes.pop() + + 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) + + for kind in (node.kind, (node.kind, node.opcode)): + if kind in visitors['pop']: + for visitor in visitors['pop'][kind]: + visitor(node) + + Property changes on: trunk/javascriptlint/lint.py ___________________________________________________________________ Added: svn:mergeinfo + Added: svn:eol-style + native Copied: trunk/javascriptlint/spidermonkey.py (from rev 254, trunk/javascriptlint/pyjsl/spidermonkey.py) =================================================================== --- trunk/javascriptlint/spidermonkey.py (rev 0) +++ trunk/javascriptlint/spidermonkey.py 2009-10-03 16:53:02 UTC (rev 255) @@ -0,0 +1,10 @@ +# vim: ts=4 sw=4 expandtab + +# This is a wrapper script to make it easier for development. It tries to +# import the development version first, and if that fails, it goes after the +# real version. +try: + from spidermonkey_ import * +except ImportError: + from pyspidermonkey import * + Property changes on: trunk/javascriptlint/spidermonkey.py ___________________________________________________________________ Added: svn:mergeinfo + Added: svn:eol-style + native Copied: trunk/javascriptlint/spidermonkey_.py (from rev 254, trunk/javascriptlint/pyjsl/spidermonkey_.py) =================================================================== --- trunk/javascriptlint/spidermonkey_.py (rev 0) +++ trunk/javascriptlint/spidermonkey_.py 2009-10-03 16:53:02 UTC (rev 255) @@ -0,0 +1,20 @@ +# vim: ts=4 sw=4 expandtab +from distutils.core import setup, Extension +import os +import sys + +# Add the bin directory to the module search path +def _get_lib_path(): + import distutils.dist + import distutils.command.build + dist = distutils.dist.Distribution() + build = distutils.command.build.build(dist) + build.finalize_options() + return os.path.join(os.path.dirname(__file__), '..', build.build_platlib, 'javascriptlint') + +sys.path.insert(0, _get_lib_path()) +try: + from pyspidermonkey import * +finally: + sys.path.pop(0) + Property changes on: trunk/javascriptlint/spidermonkey_.py ___________________________________________________________________ Added: svn:mergeinfo + Added: svn:eol-style + native Copied: trunk/javascriptlint/util.py (from rev 254, trunk/javascriptlint/pyjsl/util.py) =================================================================== --- trunk/javascriptlint/util.py (rev 0) +++ trunk/javascriptlint/util.py 2009-10-03 16:53:02 UTC (rev 255) @@ -0,0 +1,97 @@ +# vim: ts=4 sw=4 expandtab +import codecs +import os.path +import re +import unittest + +_identifier = re.compile('^[A-Za-z_$][A-Za-z0-9_$]*$') + +def isidentifier(text): + return _identifier.match(text) + +def _encode_error_keyword(s): + s = s.replace('\\', '\\\\') + s = s.replace('"', '\\"') + s = s.replace("'", "\\'") + s = s.replace("\t", "\\t") + s = s.replace("\r", "\\r") + s = s.replace("\n", "\\n") + return s + +def format_error(output_format, path, line, col, errname, errdesc): + errprefix = 'warning' #TODO + replacements = { + '__FILE__': path, + '__FILENAME__': os.path.basename(path), + '__LINE__': str(line+1), + '__COL__': str(col), + '__ERROR__': '%s: %s' % (errprefix, errdesc), + '__ERROR_NAME__': errname, + '__ERROR_PREFIX__': errprefix, + '__ERROR_MSG__': errdesc, + '__ERROR_MSGENC__': errdesc, + } + + formatted_error = output_format + + # If the output format starts with encode:, all of the keywords should be + # encoded. + if formatted_error.startswith('encode:'): + formatted_error = formatted_error[len('encode:'):] + encoded_keywords = replacements.keys() + else: + encoded_keywords = ['__ERROR_MSGENC__'] + + for keyword in encoded_keywords: + replacements[keyword] = _encode_error_keyword(replacements[keyword]) + + regexp = '|'.join(replacements.keys()) + 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('') + assert not isidentifier('0a') + assert not isidentifier('a b') + assert isidentifier('a') + assert isidentifier('$0') + + def testEncodeKeyword(self): + self.assertEquals(_encode_error_keyword(r'normal text'), 'normal text') + self.assertEquals(_encode_error_keyword(r'a\b'), r'a\\b') + self.assertEquals(_encode_error_keyword(r"identifier's"), r"identifier\'s") + self.assertEquals(_encode_error_keyword(r'"i"'), r'\"i\"') + self.assertEquals(_encode_error_keyword('a\tb'), r'a\tb') + self.assertEquals(_encode_error_keyword('a\rb'), r'a\rb') + self.assertEquals(_encode_error_keyword('a\nb'), r'a\nb') + + def testFormattedError(self): + self.assertEquals(format_error('__FILE__', '__LINE__', 1, 2, 'name', 'desc'), + '__LINE__') + self.assertEquals(format_error('__FILE__', r'c:\my\file', 1, 2, 'name', 'desc'), + r'c:\my\file') + self.assertEquals(format_error('encode:__FILE__', r'c:\my\file', 1, 2, 'name', 'desc'), + r'c:\\my\\file') + self.assertEquals(format_error('__ERROR_MSGENC__', r'c:\my\file', 1, 2, 'name', r'a\b'), + r'a\\b') + self.assertEquals(format_error('encode:__ERROR_MSGENC__', r'c:\my\file', 1, 2, 'name', r'a\b'), + r'a\\b') + + +if __name__ == '__main__': + unittest.main() + Property changes on: trunk/javascriptlint/util.py ___________________________________________________________________ Added: svn:mergeinfo + Added: svn:eol-style + native Copied: trunk/javascriptlint/visitation.py (from rev 254, trunk/javascriptlint/pyjsl/visitation.py) =================================================================== --- trunk/javascriptlint/visitation.py (rev 0) +++ trunk/javascriptlint/visitation.py 2009-10-03 16:53:02 UTC (rev 255) @@ -0,0 +1,52 @@ +# 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' + + # 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 + Property changes on: trunk/javascriptlint/visitation.py ___________________________________________________________________ Added: svn:mergeinfo + Added: svn:eol-style + native Copied: trunk/javascriptlint/warnings.py (from rev 254, trunk/javascriptlint/pyjsl/warnings.py) =================================================================== --- trunk/javascriptlint/warnings.py (rev 0) +++ trunk/javascriptlint/warnings.py 2009-10-03 16:53:02 UTC (rev 255) @@ -0,0 +1,611 @@ +# 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 re +import sys +import types + +import util +import visitation + +from spidermonkey import tok, op + +_ALL_TOKENS = tuple(filter(lambda x: x != tok.EOF, 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', + '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 {name}', + 'undeclared_identifier': 'undeclared identifier: {name}', + 'unreferenced_identifier': 'identifier 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', + 'duplicate_formal': 'TODO', + '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' +} + +def format_error(errname, **errargs): + errdesc = warnings[errname] + try: + errdesc = re.sub(r"{(\w+)}", lambda match: errargs[match.group(1)], errdesc) + except (TypeError, KeyError):... [truncated message content] |
From: <mat...@us...> - 2009-10-03 16:41:15
|
Revision: 254 http://javascriptlint.svn.sourceforge.net/javascriptlint/?rev=254&view=rev Author: matthiasmiller Date: 2009-10-03 16:41:05 +0000 (Sat, 03 Oct 2009) Log Message: ----------- Automatically run the makefiles from setup.py. (Note that the install is still broken.) Modified Paths: -------------- trunk/INSTALL trunk/Makefile.SpiderMonkey trunk/setup.py Modified: trunk/INSTALL =================================================================== --- trunk/INSTALL 2009-10-03 15:45:19 UTC (rev 253) +++ trunk/INSTALL 2009-10-03 16:41:05 UTC (rev 254) @@ -9,6 +9,7 @@ on Windows Vista.) Run the commands in that shell. On all platforms: - $ make -f Makefile.SpiderMonkey $ python setup.py build +To install on Unix: + $ sudo python setup.py install Modified: trunk/Makefile.SpiderMonkey =================================================================== --- trunk/Makefile.SpiderMonkey 2009-10-03 15:45:19 UTC (rev 253) +++ trunk/Makefile.SpiderMonkey 2009-10-03 16:41:05 UTC (rev 254) @@ -1,3 +1,5 @@ +## THIS IS AN INTERNAL MAKEFILE FOR setup.py +## DO NOT RUN THIS MAKEFILE DIRECTLY. SPIDERMONKEY_SRC=spidermonkey/src @@ -3,5 +5,4 @@ # Load the SpiderMonkey config to find the OS define # Also use this for the SO_SUFFIX -BUILD_OPT=1 DEPTH=$(SPIDERMONKEY_SRC) include $(SPIDERMONKEY_SRC)/config.mk @@ -16,10 +17,6 @@ BUILD_DIR=build/spidermonkey -# Use a dynamically-created makefile to determine the distutils output dir -DISTUTILS_DIR_MAKEFILE=$(BUILD_DIR)/Makefile-distutils -include $(DISTUTILS_DIR_MAKEFILE) - ORIG_LIB=$(SPIDERMONKEY_SRC)/$(OBJDIR)/$(JS_LIB) COPY_LIB=$(BUILD_DIR)/$(JS_LIB) ORIG_DLL=$(SPIDERMONKEY_SRC)/$(OBJDIR)/js32.dll @@ -40,23 +37,16 @@ all: $(ALL_TARGETS) clean: - make -f Makefile.ref -C $(SPIDERMONKEY_SRC) BUILD_OPT=$(BUILD_OPT) clean - rm $(ORIG_LIB) - rm -R $(BUILD_DIR) + rm -f $(ORIG_LIB) + rm -Rf $(BUILD_DIR) -$(DISTUTILS_DIR_MAKEFILE): Makefile.SpiderMonkey $(BUILD_DIR) - python -c "import setup; print 'DISTUTILS_DIR='+setup.get_lib_path()" >> $(DISTUTILS_DIR_MAKEFILE) - $(BUILD_DIR): mkdir -p $(BUILD_DIR) $(COPY_LIB): $(BUILD_DIR) $(ORIG_LIB) cp $(ORIG_LIB) $(COPY_LIB) -$(DISTUTILS_DIR): $(DISTUTILS_DIR_MAKEFILE) - mkdir -p $(DISTUTILS_DIR) - -$(COPY_DLL): $(DISTUTILS_DIR) $(ORIG_LIB) +$(COPY_DLL): $(ORIG_LIB) cp $(ORIG_DLL) $(COPY_DLL) $(OS_HEADER): $(BUILD_DIR) @@ -65,6 +55,3 @@ $(COPY_JSAUTOCFG_H): $(ORIG_JSAUTOCFG_H) cp $(ORIG_JSAUTOCFG_H) $(COPY_JSAUTOCFG_H) -$(ORIG_LIB): - make -f Makefile.ref -C $(SPIDERMONKEY_SRC) BUILD_OPT=$(BUILD_OPT) - Modified: trunk/setup.py =================================================================== --- trunk/setup.py 2009-10-03 15:45:19 UTC (rev 253) +++ trunk/setup.py 2009-10-03 16:41:05 UTC (rev 254) @@ -1,9 +1,35 @@ #!/usr/bin/python # vim: ts=4 sw=4 expandtab from distutils.core import setup, Extension +import distutils.command.build +import distutils.command.clean import os +import subprocess import sys +def _runmakefiles(distutils_dir, build_opt=1, args=[]): + # First build SpiderMonkey. + subprocess.check_call(['make', '-f', 'Makefile.ref', '-C', + 'spidermonkey/src', 'BUILD_OPT=%i' % build_opt] + \ + args) + + # Then copy the files to the build directory. + env = dict(os.environ) + if distutils_dir: + env['DISTUTILS_DIR'] = distutils_dir + subprocess.check_call(['make', '-f', 'Makefile.SpiderMonkey', + 'BUILD_OPT=%i' % build_opt] + args, env=env) + +class _MyBuild(distutils.command.build.build): + def run(self): + _runmakefiles(self.build_platlib) + distutils.command.build.build.run(self) + +class _MyClean(distutils.command.clean.clean): + def run(self): + _runmakefiles(None, args=['clean']) + distutils.command.clean.clean.run(self) + if __name__ == '__main__': if os.name == 'nt': library = 'js32' @@ -19,6 +45,10 @@ 'javascriptlint/pyspidermonkey/nodepos.c' ] ) + cmdclass = { + 'build': _MyBuild, + 'clean': _MyClean, + } args = {} args.update( name = 'javascriptlint', @@ -26,6 +56,7 @@ author = 'Matthias Miller', author_email = 'in...@ja...', url = 'http://www.javascriptlint.com/', + cmdclass = cmdclass, description = 'JavaScript Lint', ext_modules = [pyspidermonkey], packages = ['javascriptlint', 'javascriptlint.pyjsl'], This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <mat...@us...> - 2009-10-03 15:45:29
|
Revision: 253 http://javascriptlint.svn.sourceforge.net/javascriptlint/?rev=253&view=rev Author: matthiasmiller Date: 2009-10-03 15:45:19 +0000 (Sat, 03 Oct 2009) Log Message: ----------- Create a wrapper script to run jsl to make this Unix-friendly. Modified Paths: -------------- trunk/javascriptlint/__init__.py trunk/setup.py Added Paths: ----------- trunk/jsl Modified: trunk/javascriptlint/__init__.py =================================================================== --- trunk/javascriptlint/__init__.py 2009-10-03 15:41:59 UTC (rev 252) +++ trunk/javascriptlint/__init__.py 2009-10-03 15:45:19 UTC (rev 253) @@ -0,0 +1,3 @@ +# vim: ts=4 sw=4 expandtab +from jsl import main + Added: trunk/jsl =================================================================== --- trunk/jsl (rev 0) +++ trunk/jsl 2009-10-03 15:45:19 UTC (rev 253) @@ -0,0 +1,5 @@ +#!/usr/bin/python +# vim: ts=4 sw=4 expandtab +import javascriptlint +javascriptlint.main() + Property changes on: trunk/jsl ___________________________________________________________________ Added: svn:executable + * Added: svn:eol-style + native Modified: trunk/setup.py =================================================================== --- trunk/setup.py 2009-10-03 15:41:59 UTC (rev 252) +++ trunk/setup.py 2009-10-03 15:45:19 UTC (rev 253) @@ -10,24 +10,26 @@ else: library = 'js' pyspidermonkey = Extension( - 'pyspidermonkey', + 'javascriptlint.pyspidermonkey', include_dirs = ['spidermonkey/src', 'build/spidermonkey'], library_dirs = ['build/spidermonkey'], libraries = [library], sources = [ - 'pyspidermonkey/pyspidermonkey.c', - 'pyspidermonkey/nodepos.c' + 'javascriptlint/pyspidermonkey/pyspidermonkey.c', + 'javascriptlint/pyspidermonkey/nodepos.c' ] ) args = {} args.update( - name = 'pyjsl', + name = 'javascriptlint', version = '1.0', author = 'Matthias Miller', author_email = 'in...@ja...', url = 'http://www.javascriptlint.com/', description = 'JavaScript Lint', - ext_modules = [pyspidermonkey] + ext_modules = [pyspidermonkey], + packages = ['javascriptlint', 'javascriptlint.pyjsl'], + scripts = ['jsl'] ) try: import py2exe This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <mat...@us...> - 2009-10-03 15:42:08
|
Revision: 252 http://javascriptlint.svn.sourceforge.net/javascriptlint/?rev=252&view=rev Author: matthiasmiller Date: 2009-10-03 15:41:59 +0000 (Sat, 03 Oct 2009) Log Message: ----------- Fix pyspidermonkey search path. Modified Paths: -------------- trunk/javascriptlint/pyjsl/spidermonkey_.py Modified: trunk/javascriptlint/pyjsl/spidermonkey_.py =================================================================== --- trunk/javascriptlint/pyjsl/spidermonkey_.py 2009-10-03 15:33:38 UTC (rev 251) +++ trunk/javascriptlint/pyjsl/spidermonkey_.py 2009-10-03 15:41:59 UTC (rev 252) @@ -10,7 +10,7 @@ dist = distutils.dist.Distribution() build = distutils.command.build.build(dist) build.finalize_options() - return os.path.join(os.path.dirname(__file__), '..', build.build_platlib) + return os.path.join(os.path.dirname(__file__), '..', '..', build.build_platlib) sys.path.insert(0, _get_lib_path()) try: This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <mat...@us...> - 2009-10-03 15:33:45
|
Revision: 251 http://javascriptlint.svn.sourceforge.net/javascriptlint/?rev=251&view=rev Author: matthiasmiller Date: 2009-10-03 15:33:38 +0000 (Sat, 03 Oct 2009) Log Message: ----------- Move jsl into a javascriptlint subdirectory. Modified Paths: -------------- trunk/test.py Added Paths: ----------- trunk/javascriptlint/ trunk/javascriptlint/__init__.py trunk/javascriptlint/jsl.py trunk/javascriptlint/pyjsl/ trunk/javascriptlint/pyspidermonkey/ Removed Paths: ------------- trunk/jsl.py trunk/pyjsl/ trunk/pyspidermonkey/ Property changes on: trunk/javascriptlint ___________________________________________________________________ Added: svn:ignore + *.pyc Property changes on: trunk/javascriptlint/__init__.py ___________________________________________________________________ Added: svn:eol-style + native Copied: trunk/javascriptlint/jsl.py (from rev 250, trunk/jsl.py) =================================================================== --- trunk/javascriptlint/jsl.py (rev 0) +++ trunk/javascriptlint/jsl.py 2009-10-03 15:33:38 UTC (rev 251) @@ -0,0 +1,125 @@ +#!/usr/bin/python +# vim: ts=4 sw=4 expandtab +import codecs +import glob +import os +import sys +import unittest +from optparse import OptionParser + +import pyjsl.conf +import pyjsl.htmlparse +import pyjsl.jsparse +import pyjsl.lint +import pyjsl.util + +_lint_results = { + 'warnings': 0, + 'errors': 0 +} + +def _dump(paths): + for path in paths: + script = pyjsl.util.readfile(path) + pyjsl.jsparse.dump_tree(script) + +def _lint(paths, conf): + def lint_error(path, line, col, errname, errdesc): + _lint_results['warnings'] = _lint_results['warnings'] + 1 + print pyjsl.util.format_error(conf['output-format'], path, line, col, + errname, errdesc) + pyjsl.lint.lint_files(paths, lint_error, conf=conf) + +def _resolve_paths(path, recurse): + if os.path.isfile(path): + return [path] + elif os.path.isdir(path): + dir = path + pattern = '*' + else: + dir, pattern = os.path.split(path) + + # Build a list of directories + dirs = [dir] + if recurse: + for cur_root, cur_dirs, cur_files in os.walk(dir): + for name in cur_dirs: + dirs.append(os.path.join(cur_root, name)) + + # Glob all files. + paths = [] + for dir in dirs: + paths.extend(glob.glob(os.path.join(dir, pattern))) + return paths + +def _profile_enabled(func, *args, **kwargs): + import tempfile + import hotshot + import hotshot.stats + handle, filename = tempfile.mkstemp() + profile = hotshot.Profile(filename) + profile.runcall(func, *args, **kwargs) + profile.close() + stats = hotshot.stats.load(filename) + stats = stats.sort_stats("time") + stats.print_stats() +def _profile_disabled(func, *args, **kwargs): + func(*args, **kwargs) + +def main(): + parser = OptionParser(usage="%prog [options] [files]") + add = parser.add_option + add("--conf", dest="conf", metavar="CONF", + help="set the conf file") + add("--profile", dest="profile", action="store_true", default=False, + help="turn on hotshot profiling") + add("--dump", dest="dump", action="store_true", default=False, + help="dump this script") + add("--unittest", dest="unittest", action="store_true", default=False, + help="run the python unittests") + add("--quiet", dest="verbosity", action="store_const", const=0, + help="minimal output") + add("--verbose", dest="verbosity", action="store_const", const=2, + help="verbose output") + parser.set_defaults(verbosity=1) + options, args = parser.parse_args() + + if len(sys.argv) == 1: + parser.print_help() + sys.exit() + + conf = pyjsl.conf.Conf() + if options.conf: + conf.loadfile(options.conf) + + profile_func = _profile_disabled + if options.profile: + profile_func = _profile_enabled + + if options.unittest: + suite = unittest.TestSuite(); + for module in [pyjsl.htmlparse, pyjsl.jsparse, pyjsl.util]: + suite.addTest(unittest.findTestCases(module)) + + runner = unittest.TextTestRunner(verbosity=options.verbosity) + runner.run(suite) + + paths = [] + for recurse, path in conf['paths']: + paths.extend(_resolve_paths(path, recurse)) + for arg in args: + paths.extend(_resolve_paths(arg, False)) + if options.dump: + profile_func(_dump, paths) + else: + profile_func(_lint, paths, conf) + + if _lint_results['errors']: + sys.exit(3) + if _lint_results['warnings']: + sys.exit(1) + sys.exit(1) + +if __name__ == '__main__': + main() + Property changes on: trunk/javascriptlint/jsl.py ___________________________________________________________________ Added: svn:executable + * Added: svn:mergeinfo + Added: svn:eol-style + native Property changes on: trunk/javascriptlint/pyjsl ___________________________________________________________________ Added: svn:ignore + *.pyc Added: svn:mergeinfo + Property changes on: trunk/javascriptlint/pyspidermonkey ___________________________________________________________________ Added: svn:mergeinfo + Deleted: trunk/jsl.py =================================================================== --- trunk/jsl.py 2009-10-03 15:11:21 UTC (rev 250) +++ trunk/jsl.py 2009-10-03 15:33:38 UTC (rev 251) @@ -1,125 +0,0 @@ -#!/usr/bin/python -# vim: ts=4 sw=4 expandtab -import codecs -import glob -import os -import sys -import unittest -from optparse import OptionParser - -import pyjsl.conf -import pyjsl.htmlparse -import pyjsl.jsparse -import pyjsl.lint -import pyjsl.util - -_lint_results = { - 'warnings': 0, - 'errors': 0 -} - -def _dump(paths): - for path in paths: - script = pyjsl.util.readfile(path) - pyjsl.jsparse.dump_tree(script) - -def _lint(paths, conf): - def lint_error(path, line, col, errname, errdesc): - _lint_results['warnings'] = _lint_results['warnings'] + 1 - print pyjsl.util.format_error(conf['output-format'], path, line, col, - errname, errdesc) - pyjsl.lint.lint_files(paths, lint_error, conf=conf) - -def _resolve_paths(path, recurse): - if os.path.isfile(path): - return [path] - elif os.path.isdir(path): - dir = path - pattern = '*' - else: - dir, pattern = os.path.split(path) - - # Build a list of directories - dirs = [dir] - if recurse: - for cur_root, cur_dirs, cur_files in os.walk(dir): - for name in cur_dirs: - dirs.append(os.path.join(cur_root, name)) - - # Glob all files. - paths = [] - for dir in dirs: - paths.extend(glob.glob(os.path.join(dir, pattern))) - return paths - -def _profile_enabled(func, *args, **kwargs): - import tempfile - import hotshot - import hotshot.stats - handle, filename = tempfile.mkstemp() - profile = hotshot.Profile(filename) - profile.runcall(func, *args, **kwargs) - profile.close() - stats = hotshot.stats.load(filename) - stats = stats.sort_stats("time") - stats.print_stats() -def _profile_disabled(func, *args, **kwargs): - func(*args, **kwargs) - -def main(): - parser = OptionParser(usage="%prog [options] [files]") - add = parser.add_option - add("--conf", dest="conf", metavar="CONF", - help="set the conf file") - add("--profile", dest="profile", action="store_true", default=False, - help="turn on hotshot profiling") - add("--dump", dest="dump", action="store_true", default=False, - help="dump this script") - add("--unittest", dest="unittest", action="store_true", default=False, - help="run the python unittests") - add("--quiet", dest="verbosity", action="store_const", const=0, - help="minimal output") - add("--verbose", dest="verbosity", action="store_const", const=2, - help="verbose output") - parser.set_defaults(verbosity=1) - options, args = parser.parse_args() - - if len(sys.argv) == 1: - parser.print_help() - sys.exit() - - conf = pyjsl.conf.Conf() - if options.conf: - conf.loadfile(options.conf) - - profile_func = _profile_disabled - if options.profile: - profile_func = _profile_enabled - - if options.unittest: - suite = unittest.TestSuite(); - for module in [pyjsl.htmlparse, pyjsl.jsparse, pyjsl.util]: - suite.addTest(unittest.findTestCases(module)) - - runner = unittest.TextTestRunner(verbosity=options.verbosity) - runner.run(suite) - - paths = [] - for recurse, path in conf['paths']: - paths.extend(_resolve_paths(path, recurse)) - for arg in args: - paths.extend(_resolve_paths(arg, False)) - if options.dump: - profile_func(_dump, paths) - else: - profile_func(_lint, paths, conf) - - if _lint_results['errors']: - sys.exit(3) - if _lint_results['warnings']: - sys.exit(1) - sys.exit(1) - -if __name__ == '__main__': - main() - Modified: trunk/test.py =================================================================== --- trunk/test.py 2009-10-03 15:11:21 UTC (rev 250) +++ trunk/test.py 2009-10-03 15:33:38 UTC (rev 251) @@ -4,8 +4,8 @@ import re import sys -import pyjsl.conf -import pyjsl.lint +import javascriptlint.pyjsl.conf +import javascriptlint.pyjsl.lint _DEFAULT_CONF = """ # This warning triggers a lot of warnings in many of the tests, so only enable @@ -19,7 +19,7 @@ def _get_conf(script): regexp = re.compile(r"/\*conf:([^*]*)\*/") text = '\n'.join(regexp.findall(script)) - conf = pyjsl.conf.Conf() + conf = javascriptlint.pyjsl.conf.Conf() conf.loadtext(_DEFAULT_CONF) conf.loadtext(text) return conf @@ -59,7 +59,7 @@ else: unexpected_warnings.append(warning) - pyjsl.lint.lint_files([path], lint_error, conf=conf) + javascriptlint.pyjsl.lint.lint_files([path], lint_error, conf=conf) errors = [] if expected_warnings: This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <mat...@us...> - 2009-10-03 15:11:31
|
Revision: 250 http://javascriptlint.svn.sourceforge.net/javascriptlint/?rev=250&view=rev Author: matthiasmiller Date: 2009-10-03 15:11:21 +0000 (Sat, 03 Oct 2009) Log Message: ----------- Refactor jsl.py into a "main" function. Modified Paths: -------------- trunk/jsl.py Modified: trunk/jsl.py =================================================================== --- trunk/jsl.py 2009-10-03 15:04:25 UTC (rev 249) +++ trunk/jsl.py 2009-10-03 15:11:21 UTC (rev 250) @@ -52,7 +52,7 @@ paths.extend(glob.glob(os.path.join(dir, pattern))) return paths -def profile_enabled(func, *args, **kwargs): +def _profile_enabled(func, *args, **kwargs): import tempfile import hotshot import hotshot.stats @@ -63,10 +63,10 @@ stats = hotshot.stats.load(filename) stats = stats.sort_stats("time") stats.print_stats() -def profile_disabled(func, *args, **kwargs): +def _profile_disabled(func, *args, **kwargs): func(*args, **kwargs) -if __name__ == '__main__': +def main(): parser = OptionParser(usage="%prog [options] [files]") add = parser.add_option add("--conf", dest="conf", metavar="CONF", @@ -92,9 +92,9 @@ if options.conf: conf.loadfile(options.conf) - profile_func = profile_disabled + profile_func = _profile_disabled if options.profile: - profile_func = profile_enabled + profile_func = _profile_enabled if options.unittest: suite = unittest.TestSuite(); @@ -118,4 +118,8 @@ sys.exit(3) if _lint_results['warnings']: sys.exit(1) + sys.exit(1) +if __name__ == '__main__': + main() + This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <mat...@us...> - 2009-10-03 15:04:32
|
Revision: 249 http://javascriptlint.svn.sourceforge.net/javascriptlint/?rev=249&view=rev Author: matthiasmiller Date: 2009-10-03 15:04:25 +0000 (Sat, 03 Oct 2009) Log Message: ----------- Use a different approach to allow pyspidermonkey to be used from the trunk tree. Modified Paths: -------------- trunk/jsl.py trunk/pyjsl/jsparse.py trunk/pyjsl/lint.py trunk/pyjsl/warnings.py trunk/setup.py trunk/test.py Added Paths: ----------- trunk/pyjsl/spidermonkey.py trunk/pyjsl/spidermonkey_.py Modified: trunk/jsl.py =================================================================== --- trunk/jsl.py 2009-10-03 09:35:59 UTC (rev 248) +++ trunk/jsl.py 2009-10-03 15:04:25 UTC (rev 249) @@ -7,13 +7,6 @@ import unittest from optparse import OptionParser -try: - import setup -except ImportError: - pass -else: - setup.addsearchpath() - import pyjsl.conf import pyjsl.htmlparse import pyjsl.jsparse Modified: trunk/pyjsl/jsparse.py =================================================================== --- trunk/pyjsl/jsparse.py 2009-10-03 09:35:59 UTC (rev 248) +++ trunk/pyjsl/jsparse.py 2009-10-03 15:04:25 UTC (rev 249) @@ -5,8 +5,8 @@ import re import unittest -import pyspidermonkey -from pyspidermonkey import tok, op +import spidermonkey +from spidermonkey import tok, op _tok_names = dict(zip( [getattr(tok, prop) for prop in dir(tok)], @@ -17,7 +17,7 @@ ['op.%s' % prop for prop in dir(op)] )) -NodePos = pyspidermonkey.NodePos +NodePos = spidermonkey.NodePos class NodePositions: " Given a string, allows [x] lookups for NodePos line and column numbers." @@ -203,8 +203,8 @@ error_callback(line, col, msg) startpos = startpos or NodePos(0,0) - return pyspidermonkey.parse(script, _Node, _wrapped_callback, - startpos.line, startpos.col) + return spidermonkey.parse(script, _Node, _wrapped_callback, + startpos.line, startpos.col) def filtercomments(possible_comments, node_positions, root_node): comment_ignore_ranges = NodeRanges() @@ -238,7 +238,7 @@ return filtercomments(possible_comments, node_positions, root_node) def is_compilable_unit(script): - return pyspidermonkey.is_compilable_unit(script) + return spidermonkey.is_compilable_unit(script) def _dump_node(node, depth=0): if node is None: Modified: trunk/pyjsl/lint.py =================================================================== --- trunk/pyjsl/lint.py 2009-10-03 09:35:59 UTC (rev 248) +++ trunk/pyjsl/lint.py 2009-10-03 15:04:25 UTC (rev 249) @@ -10,7 +10,7 @@ import warnings import util -from pyspidermonkey import tok, op +from spidermonkey import tok, op _newline_kinds = ( 'eof', 'comma', 'dot', 'semi', 'colon', 'lc', 'rc', 'lp', 'rb', 'assign', Added: trunk/pyjsl/spidermonkey.py =================================================================== --- trunk/pyjsl/spidermonkey.py (rev 0) +++ trunk/pyjsl/spidermonkey.py 2009-10-03 15:04:25 UTC (rev 249) @@ -0,0 +1,10 @@ +# vim: ts=4 sw=4 expandtab + +# This is a wrapper script to make it easier for development. It tries to +# import the development version first, and if that fails, it goes after the +# real version. +try: + from spidermonkey_ import * +except ImportError: + from pyspidermonkey import * + Property changes on: trunk/pyjsl/spidermonkey.py ___________________________________________________________________ Added: svn:eol-style + native Added: trunk/pyjsl/spidermonkey_.py =================================================================== --- trunk/pyjsl/spidermonkey_.py (rev 0) +++ trunk/pyjsl/spidermonkey_.py 2009-10-03 15:04:25 UTC (rev 249) @@ -0,0 +1,20 @@ +# vim: ts=4 sw=4 expandtab +from distutils.core import setup, Extension +import os +import sys + +# Add the bin directory to the module search path +def _get_lib_path(): + import distutils.dist + import distutils.command.build + dist = distutils.dist.Distribution() + build = distutils.command.build.build(dist) + build.finalize_options() + return os.path.join(os.path.dirname(__file__), '..', build.build_platlib) + +sys.path.insert(0, _get_lib_path()) +try: + from pyspidermonkey import * +finally: + sys.path.pop(0) + Property changes on: trunk/pyjsl/spidermonkey_.py ___________________________________________________________________ Added: svn:eol-style + native Modified: trunk/pyjsl/warnings.py =================================================================== --- trunk/pyjsl/warnings.py 2009-10-03 09:35:59 UTC (rev 248) +++ trunk/pyjsl/warnings.py 2009-10-03 15:04:25 UTC (rev 249) @@ -20,8 +20,9 @@ import util import visitation -from pyspidermonkey import tok, op +from spidermonkey import tok, op + _ALL_TOKENS = tuple(filter(lambda x: x != tok.EOF, tok.__dict__.values())) def _get_assigned_lambda(node): Modified: trunk/setup.py =================================================================== --- trunk/setup.py 2009-10-03 09:35:59 UTC (rev 248) +++ trunk/setup.py 2009-10-03 15:04:25 UTC (rev 249) @@ -4,18 +4,6 @@ import os import sys -# Add the bin directory to the module search path -def get_lib_path(): - import distutils.dist - import distutils.command.build - dist = distutils.dist.Distribution() - build = distutils.command.build.build(dist) - build.finalize_options() - return os.path.join(os.path.dirname(__file__), build.build_platlib) - -def addsearchpath(): - sys.path.append(get_lib_path()) - if __name__ == '__main__': if os.name == 'nt': library = 'js32' @@ -50,7 +38,7 @@ console = ['jsl.py'], options = { 'py2exe': { - 'excludes': 'setup', + 'excludes': ['pyjsl.spidermonkey_'], 'bundle_files': 1 } }, Modified: trunk/test.py =================================================================== --- trunk/test.py 2009-10-03 09:35:59 UTC (rev 248) +++ trunk/test.py 2009-10-03 15:04:25 UTC (rev 249) @@ -4,13 +4,6 @@ import re import sys -try: - import setup -except ImportError: - pass -else: - setup.addsearchpath() - import pyjsl.conf import pyjsl.lint @@ -102,7 +95,7 @@ if ext in ('.htm', '.html', '.js'): try: _testfile(file) - except test.TestError, error: + except TestError, error: haderrors = True print error sys.exit(haderrors) This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <mat...@us...> - 2009-10-03 10:22:46
|
Revision: 248 http://javascriptlint.svn.sourceforge.net/javascriptlint/?rev=248&view=rev Author: matthiasmiller Date: 2009-10-03 09:35:59 +0000 (Sat, 03 Oct 2009) Log Message: ----------- Fix missing import. Modified Paths: -------------- trunk/jsl.py Modified: trunk/jsl.py =================================================================== --- trunk/jsl.py 2009-10-03 09:07:39 UTC (rev 247) +++ trunk/jsl.py 2009-10-03 09:35:59 UTC (rev 248) @@ -17,6 +17,7 @@ import pyjsl.conf import pyjsl.htmlparse import pyjsl.jsparse +import pyjsl.lint import pyjsl.util _lint_results = { This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <mat...@us...> - 2009-10-03 09:53:20
|
Revision: 247 http://javascriptlint.svn.sourceforge.net/javascriptlint/?rev=247&view=rev Author: matthiasmiller Date: 2009-10-03 09:07:39 +0000 (Sat, 03 Oct 2009) Log Message: ----------- Make test.py a standalone script. Modified Paths: -------------- trunk/jsl.py trunk/setup.py trunk/test.py Property Changed: ---------------- trunk/test.py Modified: trunk/jsl.py =================================================================== --- trunk/jsl.py 2009-08-27 16:52:43 UTC (rev 246) +++ trunk/jsl.py 2009-10-03 09:07:39 UTC (rev 247) @@ -12,43 +12,18 @@ except ImportError: pass else: - sys.path.append(setup.get_lib_path()) + setup.addsearchpath() import pyjsl.conf import pyjsl.htmlparse import pyjsl.jsparse import pyjsl.util -import test _lint_results = { 'warnings': 0, 'errors': 0 } -def get_test_files(): - # Get a list of test files. - dir_ = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'tests') - - all_files = [] - for root, dirs, files in os.walk(dir_): - all_files += [os.path.join(dir_, root, file) for file in files] - if '.svn' in dirs: - dirs.remove('.svn') - # TODO - if 'conf' in dirs: - dirs.remove('conf') - all_files.sort() - return all_files - -def run_tests(): - for file in get_test_files(): - ext = os.path.splitext(file)[1] - if ext in ('.htm', '.html', '.js'): - try: - test.run(file) - except test.TestError, error: - print error - def _dump(paths): for path in paths: script = pyjsl.util.readfile(path) @@ -102,8 +77,6 @@ add = parser.add_option add("--conf", dest="conf", metavar="CONF", help="set the conf file") - add("-t", "--test", dest="test", action="store_true", default=False, - help="run the javascript tests") add("--profile", dest="profile", action="store_true", default=False, help="turn on hotshot profiling") add("--dump", dest="dump", action="store_true", default=False, @@ -137,9 +110,6 @@ runner = unittest.TextTestRunner(verbosity=options.verbosity) runner.run(suite) - if options.test: - profile_func(run_tests) - paths = [] for recurse, path in conf['paths']: paths.extend(_resolve_paths(path, recurse)) Modified: trunk/setup.py =================================================================== --- trunk/setup.py 2009-08-27 16:52:43 UTC (rev 246) +++ trunk/setup.py 2009-10-03 09:07:39 UTC (rev 247) @@ -2,6 +2,7 @@ # vim: ts=4 sw=4 expandtab from distutils.core import setup, Extension import os +import sys # Add the bin directory to the module search path def get_lib_path(): @@ -12,6 +13,9 @@ build.finalize_options() return os.path.join(os.path.dirname(__file__), build.build_platlib) +def addsearchpath(): + sys.path.append(get_lib_path()) + if __name__ == '__main__': if os.name == 'nt': library = 'js32' Modified: trunk/test.py =================================================================== --- trunk/test.py 2009-08-27 16:52:43 UTC (rev 246) +++ trunk/test.py 2009-10-03 09:07:39 UTC (rev 247) @@ -1,6 +1,16 @@ +#!/usr/bin/python # vim: ts=4 sw=4 expandtab +import os import re +import sys +try: + import setup +except ImportError: + pass +else: + setup.addsearchpath() + import pyjsl.conf import pyjsl.lint @@ -36,7 +46,7 @@ warnings.append((i, warning)) return warnings -def run(path): +def _testfile(path): # Parse the script and find the expected warnings. script = open(path).read() expected_warnings = _get_expected_warnings(script) @@ -70,3 +80,33 @@ if errors: raise TestError, '\n'.join(errors) +def _get_test_files(): + # Get a list of test files. + dir_ = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'tests') + + all_files = [] + for root, dirs, files in os.walk(dir_): + all_files += [os.path.join(dir_, root, file) for file in files] + if '.svn' in dirs: + dirs.remove('.svn') + # TODO + if 'conf' in dirs: + dirs.remove('conf') + all_files.sort() + return all_files + +def main(): + haderrors = False + for file in _get_test_files(): + ext = os.path.splitext(file)[1] + if ext in ('.htm', '.html', '.js'): + try: + _testfile(file) + except test.TestError, error: + haderrors = True + print error + sys.exit(haderrors) + +if __name__ == '__main__': + main() + Property changes on: trunk/test.py ___________________________________________________________________ Added: svn:executable + * This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <mat...@us...> - 2009-08-27 16:53:01
|
Revision: 246 http://javascriptlint.svn.sourceforge.net/javascriptlint/?rev=246&view=rev Author: matthiasmiller Date: 2009-08-27 16:52:43 +0000 (Thu, 27 Aug 2009) Log Message: ----------- fix error position for nested comments Modified Paths: -------------- trunk/pyjsl/lint.py Modified: trunk/pyjsl/lint.py =================================================================== --- trunk/pyjsl/lint.py 2009-08-27 16:45:33 UTC (rev 245) +++ trunk/pyjsl/lint.py 2009-08-27 16:52:43 UTC (rev 246) @@ -252,7 +252,7 @@ 'redeclared_var', 'var_hides_arg'): parse_errors.append((jsparse.NodePos(row, col), msg)) - def report(node, errname, **errargs): + def report(node, errname, pos=None, **errargs): if errname == 'empty_statement' and node.kind == tok.LC: for pass_ in passes: if pass_.start_pos() > node.start_pos() and \ @@ -283,7 +283,7 @@ fallthrus.remove(fallthru) return - report_lint(node, errname, **errargs) + report_lint(node, errname, pos, **errargs) parse_errors = [] declares = [] @@ -334,8 +334,15 @@ passes.append(node) else: if comment.opcode == 'c_comment': - if '/*' in comment.atom or comment.atom.endswith('/'): - report(comment, 'nested_comment') + # Look for nested C-style comments. + nested_comment = comment.atom.find('/*') + if nested_comment < 0 and comment.atom.endswith('/'): + nested_comment = len(comment.atom) - 1 + # Report at the actual error of the location. Add two + # characters for the opening two characters. + if nested_comment >= 0: + pos = node_positions.from_offset(node_positions.to_offset(comment.start_pos()) + 2 + nested_comment) + report(comment, 'nested_comment', pos=pos) if comment.atom.lower().startswith('jsl:'): report(comment, 'jsl_cc_not_understood') elif comment.atom.startswith('@'): @@ -378,15 +385,9 @@ declare_scope.add_declaration(name, node) def _lint_script_parts(script_parts, script_cache, lint_error, conf, import_callback): - def report_lint(node, errname, **errargs): - # TODO: This is ugly hardcoding to improve the error positioning of - # "missing_semicolon" errors. - if errname == 'missing_semicolon' or errname == 'missing_semicolon_for_lambda': - pos = node.end_pos() - else: - pos = node.start_pos() + def report_lint(node, errname, pos=None, **errargs): errdesc = warnings.format_error(errname, **errargs) - _report(pos, errname, errdesc, True) + _report(pos or node.start_pos(), errname, errdesc, True) def report_native(pos, errname): # TODO: Format the error. @@ -431,7 +432,13 @@ ret = visitor(node) assert ret is None, 'visitor should raise an exception, not return a value' except warnings.LintWarning, warning: - report(warning.node, visitor.warning, **warning.errargs) + # TODO: This is ugly hardcoding to improve the error positioning of + # "missing_semicolon" errors. + if visitor.warning in ('missing_semicolon', 'missing_semicolon_for_lambda'): + pos = warning.node.end_pos() + else: + pos = None + report(warning.node, visitor.warning, pos=pos, **warning.errargs) return onpush def _warn_or_declare(scope, name, node, report): This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <mat...@us...> - 2009-08-27 16:45:44
|
Revision: 245 http://javascriptlint.svn.sourceforge.net/javascriptlint/?rev=245&view=rev Author: matthiasmiller Date: 2009-08-27 16:45:33 +0000 (Thu, 27 Aug 2009) Log Message: ----------- ambiguous_newline: remove for new Modified Paths: -------------- trunk/DEVELOPMENT trunk/test.py Removed Paths: ------------- trunk/tests/warnings/ambiguous_newline.js Modified: trunk/DEVELOPMENT =================================================================== --- trunk/DEVELOPMENT 2009-05-26 02:26:18 UTC (rev 244) +++ trunk/DEVELOPMENT 2009-08-27 16:45:33 UTC (rev 245) @@ -13,6 +13,7 @@ > implement semicolons warning > implement line break warning > add test for syntax error +> consider reimplementing abiguous_newline ** UPGRADING SPIDERMONKEY Modified: trunk/test.py =================================================================== --- trunk/test.py 2009-05-26 02:26:18 UTC (rev 244) +++ trunk/test.py 2009-08-27 16:45:33 UTC (rev 245) @@ -31,8 +31,7 @@ for i in range(0, len(lines)): for warning in regexp.findall(lines[i]): # TODO: implement these - unimpl_warnings = ('ambiguous_newline', 'dup_option_explicit', - ) + unimpl_warnings = ('dup_option_explicit',) if not warning in unimpl_warnings: warnings.append((i, warning)) return warnings Deleted: trunk/tests/warnings/ambiguous_newline.js =================================================================== --- trunk/tests/warnings/ambiguous_newline.js 2009-05-26 02:26:18 UTC (rev 244) +++ trunk/tests/warnings/ambiguous_newline.js 2009-08-27 16:45:33 UTC (rev 245) @@ -1,261 +0,0 @@ -/*jsl:option explicit*/ -function ambiguous_newline() { - /* the EOL test is based on JSLint's documentation */ - var a, b, i, o, s; - - /* legal: , */ - s = s.substr(0, - 1); - - /* legal: . */ - s = s. - substr(0, 1); - - /* legal: ; */ - s = s.substr(0, 1); - - /* legal: : */ - o = { test : - 'works' }; - - /* legal: { */ - /* legal: } */ - if (s) { - s = i; - } - - /* legal: ( */ - s = s.substr( - 0, 1); - - /* legal: [ */ - s = o[ - 'test']; - - /* legal: = */ - s = - '?'; - - /* legal: < */ - b = (i < - 14); - - /* legal: > */ - b = (i > - 93); - - /* legal: ? */ - i = (b ? - 1 : 0); - - /* legal: ! */ - b = (! - false); - - /* legal: + */ - i = 55 + - i; - - /* legal: - */ - i = 101 - - i; - - /* legal: * */ - i = i * - 2; - - /* legal: / */ - i = i / - 43; - - /* legal: % */ - i = i % - 16; - - /* legal: ~ */ - i = ~ - 16; - - /* legal: ^ */ - i = i ^ - 32; - - /* legal: | */ - i = i | - 64; - - /* legal: & */ - i = i & - 2; - - /* legal: == */ - b = (i == - 99); - - /* legal: != */ - b = (i != - -1); - - /* legal: <= */ - b = (i <= - 4); - - /* legal: >= */ - b = (i >= - 3.14); - - /* legal: += */ - i += - 1; - - /* legal: -= */ - i -= - 1; - - /* legal: *= */ - i *= - 19; - - /* legal: /= */ - i /= - 17; - - /* legal: %= */ - i %= - 15; - - /* legal: ^= */ - i ^= - 1024; - - /* legal: |= */ - i ^= - 512; - - /* legal: &= */ - i ^= - 256; - - /* legal: << */ - i = i << - 2; - - /* legal: >> */ - i = i >> - 1; - - /* legal: || */ - b = (b || - false); - - /* legal: && */ - b = (b && - true); - - /* legal: === */ - b = (i === - 0); - - /* legal: !== */ - b = (i !== - 0); - - /* legal: <<= */ - i <<= - 1; - - /* legal: >>= */ - i >>= - 2; - - /* legal: >>> */ - i = i >>> - 4; - - /* legal: >>>= */ - i >>>= - 2; - - /* legal */ - o = - { - 'component one': 1, - 'component two': 2 - }; - - /* legal */ - o = { - one: 1, - two: 2 - }; - - /* legal */ - i = o['one' - ]; - - /* illegal */ - i = o - ['one']; /*warning:ambiguous_newline*/ - - /* illegal: identifier */ - s = i - + "?"; /*warning:ambiguous_newline*/ - - /* illegal: string */ - s = "this " - + i; /*warning:ambiguous_newline*/ - - /* illegal: number */ - i = 14 - / 2; /*warning:ambiguous_newline*/ - - /* illegal: ) */ - b = (i == 7) - || false; /*warning:ambiguous_newline*/ - - /* illegal: ) */ - s = o['test'] - + "!"; /*warning:ambiguous_newline*/ - - /* illegal: ++ */ - b = i++ /*warning:inc_dec_within_stmt*/ - || true; /*warning:ambiguous_newline*/ - - /* illegal: -- */ - s = i-- /*warning:inc_dec_within_stmt*/ - + " = i"; /*warning:ambiguous_newline*/ - - /* legal */ - if (true) /*warning:meaningless_block*/ - { - i++; - } - else - { - i--; - } - while (false) - { - i--; - } - switch (1) - { - default: - break; - } - for (i = 0; i < 1; i++) - { - s = i + ""; - } - function Test() - { - return ""; - } - try - { - s = null; - } - catch (err) - { - } -} This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <mat...@us...> - 2009-05-26 02:26:27
|
Revision: 244 http://javascriptlint.svn.sourceforge.net/javascriptlint/?rev=244&view=rev Author: matthiasmiller Date: 2009-05-26 02:26:18 +0000 (Tue, 26 May 2009) Log Message: ----------- var_hides_arg should also check parent scopes Modified Paths: -------------- trunk/pyjsl/lint.py Modified: trunk/pyjsl/lint.py =================================================================== --- trunk/pyjsl/lint.py 2009-05-20 07:37:57 UTC (rev 243) +++ trunk/pyjsl/lint.py 2009-05-26 02:26:18 UTC (rev 244) @@ -435,12 +435,13 @@ return onpush def _warn_or_declare(scope, name, node, report): - other = scope.get_identifier(name) + parent_scope, other = scope.resolve_identifier(name) or (None, None) if other and other.kind == tok.FUNCTION and name in other.fn_args: report(node, 'var_hides_arg', name=name) - elif other: + elif other and parent_scope == scope: report(node, 'redeclared_var', name=name) else: + # TODO: Warn when hiding a variable in a parent scope. scope.add_declaration(name, node) def _get_scope_checks(scope, report): This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <mat...@us...> - 2009-05-20 07:38:06
|
Revision: 243 http://javascriptlint.svn.sourceforge.net/javascriptlint/?rev=243&view=rev Author: matthiasmiller Date: 2009-05-20 07:37:57 +0000 (Wed, 20 May 2009) Log Message: ----------- support the output-format setting Modified Paths: -------------- trunk/DEVELOPMENT trunk/jsl.py trunk/pyjsl/conf.py trunk/pyjsl/util.py Modified: trunk/DEVELOPMENT =================================================================== --- trunk/DEVELOPMENT 2009-03-04 07:19:18 UTC (rev 242) +++ trunk/DEVELOPMENT 2009-05-20 07:37:57 UTC (rev 243) @@ -20,7 +20,7 @@ Use the following command to upgrade SpiderMonkey. Replace X.X.X with the version number. js-X.X.X is the directory containing the new version of SpiderMonkey. Use a relative path for pretty commit messages. - + svn_load_dirs.pl \ -t X.X.X \ -p svn_load_dirs.conf \ Modified: trunk/jsl.py =================================================================== --- trunk/jsl.py 2009-03-04 07:19:18 UTC (rev 242) +++ trunk/jsl.py 2009-05-20 07:37:57 UTC (rev 243) @@ -57,7 +57,8 @@ def _lint(paths, conf): def lint_error(path, line, col, errname, errdesc): _lint_results['warnings'] = _lint_results['warnings'] + 1 - print '%s(%i): %s' % (path, line+1, errdesc) + print pyjsl.util.format_error(conf['output-format'], path, line, col, + errname, errdesc) pyjsl.lint.lint_files(paths, lint_error, conf=conf) def _resolve_paths(path, recurse): Modified: trunk/pyjsl/conf.py =================================================================== --- trunk/pyjsl/conf.py 2009-03-04 07:19:18 UTC (rev 242) +++ trunk/pyjsl/conf.py 2009-05-20 07:37:57 UTC (rev 243) @@ -55,7 +55,7 @@ self._settings = { 'recurse': recurse, 'show_context': BooleanSetting(False), - 'output-format': StringSetting('TODO'), + 'output-format': StringSetting('__FILE__(__LINE__): __ERROR__'), 'lambda_assign_requires_semicolon': BooleanSetting(False), 'legacy_control_comments': BooleanSetting(True), 'jscript_function_extensions': BooleanSetting(False), Modified: trunk/pyjsl/util.py =================================================================== --- trunk/pyjsl/util.py 2009-03-04 07:19:18 UTC (rev 242) +++ trunk/pyjsl/util.py 2009-05-20 07:37:57 UTC (rev 243) @@ -9,6 +9,46 @@ def isidentifier(text): return _identifier.match(text) +def _encode_error_keyword(s): + s = s.replace('\\', '\\\\') + s = s.replace('"', '\\"') + s = s.replace("'", "\\'") + s = s.replace("\t", "\\t") + s = s.replace("\r", "\\r") + s = s.replace("\n", "\\n") + return s + +def format_error(output_format, path, line, col, errname, errdesc): + errprefix = 'warning' #TODO + replacements = { + '__FILE__': path, + '__FILENAME__': os.path.basename(path), + '__LINE__': str(line+1), + '__COL__': str(col), + '__ERROR__': '%s: %s' % (errprefix, errdesc), + '__ERROR_NAME__': errname, + '__ERROR_PREFIX__': errprefix, + '__ERROR_MSG__': errdesc, + '__ERROR_MSGENC__': errdesc, + } + + formatted_error = output_format + + # If the output format starts with encode:, all of the keywords should be + # encoded. + if formatted_error.startswith('encode:'): + formatted_error = formatted_error[len('encode:'):] + encoded_keywords = replacements.keys() + else: + encoded_keywords = ['__ERROR_MSGENC__'] + + for keyword in encoded_keywords: + replacements[keyword] = _encode_error_keyword(replacements[keyword]) + + regexp = '|'.join(replacements.keys()) + 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() @@ -30,6 +70,28 @@ assert isidentifier('a') assert isidentifier('$0') + def testEncodeKeyword(self): + self.assertEquals(_encode_error_keyword(r'normal text'), 'normal text') + self.assertEquals(_encode_error_keyword(r'a\b'), r'a\\b') + self.assertEquals(_encode_error_keyword(r"identifier's"), r"identifier\'s") + self.assertEquals(_encode_error_keyword(r'"i"'), r'\"i\"') + self.assertEquals(_encode_error_keyword('a\tb'), r'a\tb') + self.assertEquals(_encode_error_keyword('a\rb'), r'a\rb') + self.assertEquals(_encode_error_keyword('a\nb'), r'a\nb') + + def testFormattedError(self): + self.assertEquals(format_error('__FILE__', '__LINE__', 1, 2, 'name', 'desc'), + '__LINE__') + self.assertEquals(format_error('__FILE__', r'c:\my\file', 1, 2, 'name', 'desc'), + r'c:\my\file') + self.assertEquals(format_error('encode:__FILE__', r'c:\my\file', 1, 2, 'name', 'desc'), + r'c:\\my\\file') + self.assertEquals(format_error('__ERROR_MSGENC__', r'c:\my\file', 1, 2, 'name', r'a\b'), + r'a\\b') + self.assertEquals(format_error('encode:__ERROR_MSGENC__', r'c:\my\file', 1, 2, 'name', r'a\b'), + r'a\\b') + + 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...> - 2009-03-04 07:19:21
|
Revision: 242 http://javascriptlint.svn.sourceforge.net/javascriptlint/?rev=242&view=rev Author: matthiasmiller Date: 2009-03-04 07:19:18 +0000 (Wed, 04 Mar 2009) Log Message: ----------- expand the named parameters Modified Paths: -------------- trunk/pyjsl/lint.py trunk/pyjsl/warnings.py Modified: trunk/pyjsl/lint.py =================================================================== --- trunk/pyjsl/lint.py 2009-03-04 06:52:38 UTC (rev 241) +++ trunk/pyjsl/lint.py 2009-03-04 07:19:18 UTC (rev 242) @@ -252,7 +252,7 @@ 'redeclared_var', 'var_hides_arg'): parse_errors.append((jsparse.NodePos(row, col), msg)) - def report(node, errname): + def report(node, errname, **errargs): if errname == 'empty_statement' and node.kind == tok.LC: for pass_ in passes: if pass_.start_pos() > node.start_pos() and \ @@ -283,7 +283,7 @@ fallthrus.remove(fallthru) return - report_lint(node, errname) + report_lint(node, errname, **errargs) parse_errors = [] declares = [] @@ -373,19 +373,19 @@ for name, node in declares: declare_scope = script_cache.scope.find_scope(node) if declare_scope.get_identifier(name): - report(node, 'redeclared_var') + report(node, 'redeclared_var', name=name) else: declare_scope.add_declaration(name, node) def _lint_script_parts(script_parts, script_cache, lint_error, conf, import_callback): - def report_lint(node, errname): + def report_lint(node, errname, **errargs): # TODO: This is ugly hardcoding to improve the error positioning of # "missing_semicolon" errors. if errname == 'missing_semicolon' or errname == 'missing_semicolon_for_lambda': pos = node.end_pos() else: pos = node.start_pos() - errdesc = warnings.warnings[errname] + errdesc = warnings.format_error(errname, **errargs) _report(pos, errname, errdesc, True) def report_native(pos, errname): @@ -419,11 +419,11 @@ if name in _globals: continue if not script_cache.hasglobal(name): - report_lint(node, 'undeclared_identifier') + report_lint(node, 'undeclared_identifier', name=name) for ref_scope, name, node in unreferenced: # Ignore the outer scope. if ref_scope != scope: - report_lint(node, 'unreferenced_identifier') + report_lint(node, 'unreferenced_identifier', name=name) def _getreporter(visitor, report): def onpush(node): @@ -431,15 +431,15 @@ ret = visitor(node) assert ret is None, 'visitor should raise an exception, not return a value' except warnings.LintWarning, warning: - report(warning.node, visitor.warning) + report(warning.node, visitor.warning, **warning.errargs) 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: - report(node, 'var_hides_arg') + report(node, 'var_hides_arg', name=name) elif other: - report(node, 'redeclared_var') + report(node, 'redeclared_var', name=name) else: scope.add_declaration(name, node) Modified: trunk/pyjsl/warnings.py =================================================================== --- trunk/pyjsl/warnings.py 2009-03-04 06:52:38 UTC (rev 241) +++ trunk/pyjsl/warnings.py 2009-03-04 07:19:18 UTC (rev 242) @@ -72,13 +72,13 @@ '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}', - 'unreferenced_identifier': 'identifier is declared but never referenced: {0}', + 'redeclared_var': 'redeclaration of {name}', + 'undeclared_identifier': 'undeclared identifier: {name}', + 'unreferenced_identifier': 'identifier 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 {0} hides argument', + 'var_hides_arg': 'variable {name} hides argument', 'duplicate_formal': 'TODO', 'missing_semicolon': 'missing semicolon', 'missing_semicolon_for_lambda': 'missing semicolon for lambda assignment', @@ -89,10 +89,18 @@ '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 {0} does not always return a value', + 'no_return_value': 'function {name} does not always return a value', 'anon_no_return_value': 'anonymous function does not always return value' } +def format_error(errname, **errargs): + errdesc = warnings[errname] + try: + errdesc = re.sub(r"{(\w+)}", lambda match: errargs[match.group(1)], errdesc) + except (TypeError, KeyError): + raise KeyError, 'Invalid keyword in error: ' + errdesc + return errdesc + _visitors = [] def lookfor(*args): def decorate(fn): @@ -104,8 +112,9 @@ return decorate class LintWarning(Exception): - def __init__(self, node): + 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' " @@ -496,6 +505,8 @@ 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): @@ -510,10 +521,10 @@ returns = filter(is_return_without_val, exit_points) returns.sort(key=lambda node: node.start_pos()) if returns: - raise LintWarning, returns[0] + raise LintWarning(returns[0], name=name) # Warn if the function sometimes exits naturally. if None in exit_points: - raise LintWarning, node + raise LintWarning(node, name=name) @lookfor(tok.FUNCTION) def no_return_value(node): This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <mat...@us...> - 2009-03-04 06:52:40
|
Revision: 241 http://javascriptlint.svn.sourceforge.net/javascriptlint/?rev=241&view=rev Author: matthiasmiller Date: 2009-03-04 06:52:38 +0000 (Wed, 04 Mar 2009) Log Message: ----------- fix exception when importing a path using backslashes on a Unix environment Modified Paths: -------------- trunk/pyjsl/lint.py Added Paths: ----------- trunk/tests/control_comments/import-slashes.js Modified: trunk/pyjsl/lint.py =================================================================== --- trunk/pyjsl/lint.py 2009-03-04 06:47:42 UTC (rev 240) +++ trunk/pyjsl/lint.py 2009-03-04 06:52:38 UTC (rev 241) @@ -206,6 +206,9 @@ def lint_files(paths, lint_error, conf=conf.Conf()): def lint_file(path, kind): def import_script(import_path): + # The user can specify paths using backslashes (such as when + # linting Windows scripts on a posix environment. + import_path = import_path.replace('\\', os.sep) import_path = os.path.join(os.path.dirname(path), import_path) return lint_file(import_path, 'js') def _lint_error(*args): Added: trunk/tests/control_comments/import-slashes.js =================================================================== --- trunk/tests/control_comments/import-slashes.js (rev 0) +++ trunk/tests/control_comments/import-slashes.js 2009-03-04 06:52:38 UTC (rev 241) @@ -0,0 +1,5 @@ +/*jsl:option explicit*/ + +/* Try importing using backslashes instead of forward slashes. */ +/*jsl:import ..\control_comments\import-slashes.js */ + Property changes on: trunk/tests/control_comments/import-slashes.js ___________________________________________________________________ Added: svn:eol-style + native This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <mat...@us...> - 2009-03-04 06:47:49
|
Revision: 240 http://javascriptlint.svn.sourceforge.net/javascriptlint/?rev=240&view=rev Author: matthiasmiller Date: 2009-03-04 06:47:42 +0000 (Wed, 04 Mar 2009) Log Message: ----------- start improving error descriptions Modified Paths: -------------- trunk/jsl.py trunk/pyjsl/lint.py trunk/test.py Modified: trunk/jsl.py =================================================================== --- trunk/jsl.py 2009-03-04 01:41:05 UTC (rev 239) +++ trunk/jsl.py 2009-03-04 06:47:42 UTC (rev 240) @@ -55,9 +55,9 @@ pyjsl.jsparse.dump_tree(script) def _lint(paths, conf): - def lint_error(path, line, col, errname): + def lint_error(path, line, col, errname, errdesc): _lint_results['warnings'] = _lint_results['warnings'] + 1 - print '%s(%i): %s' % (path, line+1, errname) + print '%s(%i): %s' % (path, line+1, errdesc) pyjsl.lint.lint_files(paths, lint_error, conf=conf) def _resolve_paths(path, recurse): Modified: trunk/pyjsl/lint.py =================================================================== --- trunk/pyjsl/lint.py 2009-03-04 01:41:05 UTC (rev 239) +++ trunk/pyjsl/lint.py 2009-03-04 06:47:42 UTC (rev 240) @@ -382,12 +382,14 @@ pos = node.end_pos() else: pos = node.start_pos() - _report(pos, errname, True) + errdesc = warnings.warnings[errname] + _report(pos, errname, errdesc, True) def report_native(pos, errname): - _report(pos, errname, False) + # TODO: Format the error. + _report(pos, errname, errname, False) - def _report(pos, errname, require_key): + def _report(pos, errname, errdesc, require_key): try: if not conf[errname]: return @@ -399,7 +401,7 @@ if pos >= start and pos <= end: return - return lint_error(pos.line, pos.col, errname) + return lint_error(pos.line, pos.col, errname, errdesc) for scriptpos, script in script_parts: ignores = [] Modified: trunk/test.py =================================================================== --- trunk/test.py 2009-03-04 01:41:05 UTC (rev 239) +++ trunk/test.py 2009-03-04 06:47:42 UTC (rev 240) @@ -44,7 +44,7 @@ unexpected_warnings = [] conf = _get_conf(script) - def lint_error(path, line, col, errname): + def lint_error(path, line, col, errname, errdesc): warning = (line, errname) # Bad hack to fix line numbers on ambiguous else statements This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <mat...@us...> - 2009-03-04 01:43:11
|
Revision: 239 http://javascriptlint.svn.sourceforge.net/javascriptlint/?rev=239&view=rev Author: matthiasmiller Date: 2009-03-04 01:41:05 +0000 (Wed, 04 Mar 2009) Log Message: ----------- document bug with </script> inside comment Modified Paths: -------------- trunk/pyjsl/jsparse.py trunk/tests/html/script_tag_in_js_literal.html Added Paths: ----------- trunk/tests/html/script_tag_in_js_comment.html Modified: trunk/pyjsl/jsparse.py =================================================================== --- trunk/pyjsl/jsparse.py 2009-03-03 17:02:59 UTC (rev 238) +++ trunk/pyjsl/jsparse.py 2009-03-04 01:41:05 UTC (rev 239) @@ -367,6 +367,8 @@ ) for text, result in tests: self.assertEquals(is_compilable_unit(text), result) + # NOTE: This seems like a bug. + self.assert_(is_compilable_unit("/* test")) class TestLineOffset(unittest.TestCase): def testErrorPos(self): Copied: trunk/tests/html/script_tag_in_js_comment.html (from rev 237, trunk/tests/html/script_tag_in_js_literal.html) =================================================================== --- trunk/tests/html/script_tag_in_js_comment.html (rev 0) +++ trunk/tests/html/script_tag_in_js_comment.html 2009-03-04 01:41:05 UTC (rev 239) @@ -0,0 +1,12 @@ +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN"> +<html> +<head> + <title>JavaScript Lint Test Page</title> + <script type="text/javascript"><!-- + /* alert the </script> end tag */ /*warning:unterminated_comment*/ + //--> + </script> +</head> +<body> +</body> +</html> Modified: trunk/tests/html/script_tag_in_js_literal.html =================================================================== --- trunk/tests/html/script_tag_in_js_literal.html 2009-03-03 17:02:59 UTC (rev 238) +++ trunk/tests/html/script_tag_in_js_literal.html 2009-03-04 01:41:05 UTC (rev 239) @@ -3,7 +3,7 @@ <head> <title>JavaScript Lint Test Page</title> <script type="text/javascript"><!-- - /* alert the </script> end tag */ + /*jsl:declare window*/ var x = "</script>"; window.alert(x); //--> This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <mat...@us...> - 2009-03-03 17:03:09
|
Revision: 238 http://javascriptlint.svn.sourceforge.net/javascriptlint/?rev=238&view=rev Author: matthiasmiller Date: 2009-03-03 17:02:59 +0000 (Tue, 03 Mar 2009) Log Message: ----------- missing_semicolon: implement the missing semicolon warning This is not the most elegant solution, but it requires the least invasive changes to spidermonkey. Modified Paths: -------------- trunk/pyjsl/jsparse.py trunk/pyjsl/lint.py trunk/pyjsl/warnings.py trunk/pyspidermonkey/pyspidermonkey.c trunk/spidermonkey/src/jsparse.c trunk/spidermonkey/src/jsparse.h trunk/test.py trunk/tests/control_comments/declare.js trunk/tests/warnings/missing_semicolon.js trunk/tests/warnings/unreferenced_identifier.js Added Paths: ----------- trunk/tests/warnings/missing_semicolon_for_lambda.js Removed Paths: ------------- trunk/tests/conf/lambda_assign_requires_semicolon.js Modified: trunk/pyjsl/jsparse.py =================================================================== --- trunk/pyjsl/jsparse.py 2008-09-01 15:57:37 UTC (rev 237) +++ trunk/pyjsl/jsparse.py 2009-03-03 17:02:59 UTC (rev 238) @@ -253,6 +253,9 @@ if hasattr(node, 'atom'): print ' '*depth, print 'atom: %s' % node.atom + if node.no_semi: + print ' '*depth, + print '(no semicolon)' print for node in node.kids: _dump_node(node, depth+1) Modified: trunk/pyjsl/lint.py =================================================================== --- trunk/pyjsl/lint.py 2008-09-01 15:57:37 UTC (rev 237) +++ trunk/pyjsl/lint.py 2009-03-03 17:02:59 UTC (rev 238) @@ -376,7 +376,13 @@ def _lint_script_parts(script_parts, script_cache, lint_error, conf, import_callback): def report_lint(node, errname): - _report(node.start_pos(), errname, True) + # TODO: This is ugly hardcoding to improve the error positioning of + # "missing_semicolon" errors. + if errname == 'missing_semicolon' or errname == 'missing_semicolon_for_lambda': + pos = node.end_pos() + else: + pos = node.start_pos() + _report(pos, errname, True) def report_native(pos, errname): _report(pos, errname, False) Modified: trunk/pyjsl/warnings.py =================================================================== --- trunk/pyjsl/warnings.py 2008-09-01 15:57:37 UTC (rev 237) +++ trunk/pyjsl/warnings.py 2009-03-03 17:02:59 UTC (rev 238) @@ -22,6 +22,24 @@ import visitation from pyspidermonkey import tok, op +_ALL_TOKENS = tuple(filter(lambda x: x != tok.EOF, 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 = { @@ -63,6 +81,7 @@ 'var_hides_arg': 'variable {0} hides argument', 'duplicate_formal': 'TODO', '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', @@ -538,10 +557,21 @@ def duplicate_formal(node): pass -@lookfor() +@lookfor(*_ALL_TOKENS) def missing_semicolon(node): - pass + if node.no_semi: + if not _get_assigned_lambda(node): + raise LintWarning, node +@lookfor(*_ALL_TOKENS) +def missing_semicolon_for_lambda(node): + if node.no_semi: + # spidermonkey sometimes returns incorrect positions for var + # statements, so use the position of the lambda instead. + lambda_ = _get_assigned_lambda(node) + if lambda_: + raise LintWarning, lambda_ + @lookfor() def ambiguous_newline(node): pass Modified: trunk/pyspidermonkey/pyspidermonkey.c =================================================================== --- trunk/pyspidermonkey/pyspidermonkey.c 2008-09-01 15:57:37 UTC (rev 237) +++ trunk/pyspidermonkey/pyspidermonkey.c 2009-03-03 17:02:59 UTC (rev 238) @@ -303,6 +303,9 @@ goto fail; } + if (PyObject_SetAttrString(pynode, "no_semi", PyBool_FromLong(jsnode->pn_no_semi)) == -1) + goto fail; + switch (jsnode->pn_arity) { case PN_FUNC: kids = PyTuple_New(1); Modified: trunk/spidermonkey/src/jsparse.c =================================================================== --- trunk/spidermonkey/src/jsparse.c 2008-09-01 15:57:37 UTC (rev 237) +++ trunk/spidermonkey/src/jsparse.c 2009-03-03 17:02:59 UTC (rev 238) @@ -248,6 +248,7 @@ pn->pn_next = NULL; pn->pn_ts = ts; pn->pn_source = NULL; + pn->pn_no_semi = JS_FALSE; return pn; } @@ -324,6 +325,7 @@ pn->pn_next = NULL; pn->pn_ts = NULL; pn->pn_source = NULL; + pn->pn_no_semi = JS_FALSE; return pn; } @@ -2738,7 +2740,8 @@ * insertion after do-while. See the testcase and discussion in * http://bugzilla.mozilla.org/show_bug.cgi?id=238945. */ - (void) js_MatchToken(cx, ts, TOK_SEMI); + if (!js_MatchToken(cx, ts, TOK_SEMI)) + pn->pn_no_semi = JS_TRUE; return pn; } break; @@ -3476,6 +3479,8 @@ return NULL; pn->pn_type = TOK_SEMI; pn->pn_kid = NULL; + if (tt == TOK_EOL) + pn->pn_no_semi = JS_TRUE; return pn; #if JS_HAS_DEBUGGER_KEYWORD @@ -3588,7 +3593,8 @@ } } - (void) js_MatchToken(cx, ts, TOK_SEMI); + if (!js_MatchToken(cx, ts, TOK_SEMI)) + pn->pn_no_semi = JS_TRUE; return pn; } @@ -4311,6 +4317,7 @@ pn2->pn_next = NULL; pn2->pn_ts = ts; pn2->pn_source = NULL; + pn2->pn_no_semi = JS_FALSE; pn = pn2; } } Modified: trunk/spidermonkey/src/jsparse.h =================================================================== --- trunk/spidermonkey/src/jsparse.h 2008-09-01 15:57:37 UTC (rev 237) +++ trunk/spidermonkey/src/jsparse.h 2009-03-03 17:02:59 UTC (rev 238) @@ -317,6 +317,7 @@ JSParseNode *pn_next; /* to align dval and pn_u on RISCs */ JSTokenStream *pn_ts; /* token stream for error reports */ JSAtom *pn_source; /* saved source for decompilation */ + JSBool pn_no_semi; /* missing semicolon */ }; #define pn_funAtom pn_u.func.funAtom Modified: trunk/test.py =================================================================== --- trunk/test.py 2008-09-01 15:57:37 UTC (rev 237) +++ trunk/test.py 2009-03-03 17:02:59 UTC (rev 238) @@ -32,7 +32,6 @@ for warning in regexp.findall(lines[i]): # TODO: implement these unimpl_warnings = ('ambiguous_newline', 'dup_option_explicit', - 'missing_semicolon' ) if not warning in unimpl_warnings: warnings.append((i, warning)) Deleted: trunk/tests/conf/lambda_assign_requires_semicolon.js =================================================================== --- trunk/tests/conf/lambda_assign_requires_semicolon.js 2008-09-01 15:57:37 UTC (rev 237) +++ trunk/tests/conf/lambda_assign_requires_semicolon.js 2009-03-03 17:02:59 UTC (rev 238) @@ -1,43 +0,0 @@ -/*conf:-lambda_assign_requires_semicolon*/ - - -/* Test with a simple variable. */ -var x = function() { - return {}; -} -x(); - -var a, b = function() { }, c -b(); /*warning:missing_semicolon*/ -var d, e = function() { } -e(); - -var y; -y = function() { - return []; -} -y(); - -global = function() { - return null; -} -global(); - -function Foo() -{ - this.bar = 10; - - /* Test an assignment to a member. */ - this.setBar = function(bar) { - this.bar = bar; - } - - this.setBar(this.bar * 2); -} - -/* Test an assignment to a prototype. */ -Foo.prototype.getBar = function() { - return this.bar; -} - -var foo = new Foo(); Modified: trunk/tests/control_comments/declare.js =================================================================== --- trunk/tests/control_comments/declare.js 2008-09-01 15:57:37 UTC (rev 237) +++ trunk/tests/control_comments/declare.js 2009-03-03 17:02:59 UTC (rev 238) @@ -9,8 +9,8 @@ /*jsl:declare document*//*warning:redeclared_var*/ } -var i = 10 -/*jsl:declare sample*//*warning:missing_semicolon*/ +var i = 10 /*warning:missing_semicolon*/ +/*jsl:declare sample*/ /* declare was scoped */ window.alert('JavaScript Lint');/*warning:undeclared_identifier*/ Modified: trunk/tests/warnings/missing_semicolon.js =================================================================== --- trunk/tests/warnings/missing_semicolon.js 2008-09-01 15:57:37 UTC (rev 237) +++ trunk/tests/warnings/missing_semicolon.js 2009-03-03 17:02:59 UTC (rev 238) @@ -1,19 +1,23 @@ /*jsl:option explicit*/ + function missing_semicolon() { /* missing semicolon after return */ function MissingSemicolonOnReturnStatement() { - return 0 - } /*warning:missing_semicolon*/ + return 0 /*warning:missing_semicolon*/ + } + function MissingSemicolonOnReturnStatement2() { return 0 } /*warning:missing_semicolon*/ - /* missing semicolon after return */ /* missing semicolon after lambda */ function x() { - this.y = function() { return 0 } /*warning:missing_semicolon*/ - } /*warning:missing_semicolon*/ + this.y = function() { return 0; } /*warning:missing_semicolon_for_lambda*/ + } - /* missing semicolon after return */ /* missing semicolon after lambda */ x.prototype.z = function() { - return 1 - } /*warning:missing_semicolon*/ -} /*warning:missing_semicolon*/ + return 1; + } /*warning:missing_semicolon_for_lambda*/ + + do x++; + while (x < 10) /*warning:missing_semicolon*/ +} + Copied: trunk/tests/warnings/missing_semicolon_for_lambda.js (from rev 237, trunk/tests/conf/lambda_assign_requires_semicolon.js) =================================================================== --- trunk/tests/warnings/missing_semicolon_for_lambda.js (rev 0) +++ trunk/tests/warnings/missing_semicolon_for_lambda.js 2009-03-03 17:02:59 UTC (rev 238) @@ -0,0 +1,43 @@ +/*jsl:option explicit*/ +/*jsl:declare global*/ + +/* Test with a simple variable. */ +var x = function() { + return {}; +} /*warning:missing_semicolon_for_lambda*/ +x(); + +var a, b = function() { }, c /*warning:missing_semicolon*/ +b(); +var d, e = function() { } /*warning:missing_semicolon_for_lambda*/ +e(); + +var y; +y = function() { + return []; +} /*warning:missing_semicolon_for_lambda*/ +y(); + +global = function() { + return null; +} /*warning:missing_semicolon_for_lambda*/ +global(); + +function Foo() +{ + this.bar = 10; + + /* Test an assignment to a member. */ + this.setBar = function(bar) { + this.bar = bar; + } /*warning:missing_semicolon_for_lambda*/ + + this.setBar(this.bar * 2); +} + +/* Test an assignment to a prototype. */ +Foo.prototype.getBar = function() { + return this.bar; +} /*warning:missing_semicolon_for_lambda*/ + +var foo = new Foo(); Modified: trunk/tests/warnings/unreferenced_identifier.js =================================================================== --- trunk/tests/warnings/unreferenced_identifier.js 2008-09-01 15:57:37 UTC (rev 237) +++ trunk/tests/warnings/unreferenced_identifier.js 2009-03-03 17:02:59 UTC (rev 238) @@ -15,7 +15,7 @@ /* Test an unreferenced parameter. */ var z = new function(unreferenced_parm) { /*warning:unreferenced_identifier*/ - } + }; z.prop = 42; /* Test an unreferenced variable. */ @@ -80,7 +80,7 @@ function get_callback(parm) { return function() { return parm; - } + }; } return get_callback(42); } This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |