| Index: third_party/closure_linter/closure_linter/runner.py
|
| diff --git a/third_party/closure_linter/closure_linter/runner.py b/third_party/closure_linter/closure_linter/runner.py
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..04e7fa4ac87a1c58e97b782dc56c638c8f4072ab
|
| --- /dev/null
|
| +++ b/third_party/closure_linter/closure_linter/runner.py
|
| @@ -0,0 +1,198 @@
|
| +#!/usr/bin/env python
|
| +#
|
| +# Copyright 2012 The Closure Linter Authors. All Rights Reserved.
|
| +# Licensed under the Apache License, Version 2.0 (the "License");
|
| +# you may not use this file except in compliance with the License.
|
| +# You may obtain a copy of the License at
|
| +#
|
| +# http://www.apache.org/licenses/LICENSE-2.0
|
| +#
|
| +# Unless required by applicable law or agreed to in writing, software
|
| +# distributed under the License is distributed on an "AS-IS" BASIS,
|
| +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
| +# See the License for the specific language governing permissions and
|
| +# limitations under the License.
|
| +
|
| +"""Main lint function. Tokenizes file, runs passes, and feeds to checker."""
|
| +
|
| +# Allow non-Google copyright
|
| +# pylint: disable=g-bad-file-header
|
| +
|
| +__author__ = 'nnaze@google.com (Nathan Naze)'
|
| +
|
| +import traceback
|
| +
|
| +import gflags as flags
|
| +
|
| +from closure_linter import checker
|
| +from closure_linter import ecmalintrules
|
| +from closure_linter import ecmametadatapass
|
| +from closure_linter import error_check
|
| +from closure_linter import errors
|
| +from closure_linter import javascriptstatetracker
|
| +from closure_linter import javascripttokenizer
|
| +
|
| +from closure_linter.common import error
|
| +from closure_linter.common import htmlutil
|
| +from closure_linter.common import tokens
|
| +
|
| +flags.DEFINE_list('limited_doc_files', ['dummy.js', 'externs.js'],
|
| + 'List of files with relaxed documentation checks. Will not '
|
| + 'report errors for missing documentation, some missing '
|
| + 'descriptions, or methods whose @return tags don\'t have a '
|
| + 'matching return statement.')
|
| +flags.DEFINE_boolean('error_trace', False,
|
| + 'Whether to show error exceptions.')
|
| +flags.ADOPT_module_key_flags(checker)
|
| +flags.ADOPT_module_key_flags(ecmalintrules)
|
| +flags.ADOPT_module_key_flags(error_check)
|
| +
|
| +
|
| +def _GetLastNonWhiteSpaceToken(start_token):
|
| + """Get the last non-whitespace token in a token stream."""
|
| + ret_token = None
|
| +
|
| + whitespace_tokens = frozenset([
|
| + tokens.TokenType.WHITESPACE, tokens.TokenType.BLANK_LINE])
|
| + for t in start_token:
|
| + if t.type not in whitespace_tokens:
|
| + ret_token = t
|
| +
|
| + return ret_token
|
| +
|
| +
|
| +def _IsHtml(filename):
|
| + return filename.endswith('.html') or filename.endswith('.htm')
|
| +
|
| +
|
| +def _Tokenize(fileobj):
|
| + """Tokenize a file.
|
| +
|
| + Args:
|
| + fileobj: file-like object (or iterable lines) with the source.
|
| +
|
| + Returns:
|
| + The first token in the token stream and the ending mode of the tokenizer.
|
| + """
|
| + tokenizer = javascripttokenizer.JavaScriptTokenizer()
|
| + start_token = tokenizer.TokenizeFile(fileobj)
|
| + return start_token, tokenizer.mode
|
| +
|
| +
|
| +def _IsLimitedDocCheck(filename, limited_doc_files):
|
| + """Whether this this a limited-doc file.
|
| +
|
| + Args:
|
| + filename: The filename.
|
| + limited_doc_files: Iterable of strings. Suffixes of filenames that should
|
| + be limited doc check.
|
| +
|
| + Returns:
|
| + Whether the file should be limited check.
|
| + """
|
| + for limited_doc_filename in limited_doc_files:
|
| + if filename.endswith(limited_doc_filename):
|
| + return True
|
| + return False
|
| +
|
| +
|
| +def Run(filename, error_handler, source=None):
|
| + """Tokenize, run passes, and check the given file.
|
| +
|
| + Args:
|
| + filename: The path of the file to check
|
| + error_handler: The error handler to report errors to.
|
| + source: A file-like object with the file source. If omitted, the file will
|
| + be read from the filename path.
|
| + """
|
| + if not source:
|
| + try:
|
| + source = open(filename)
|
| + except IOError:
|
| + error_handler.HandleFile(filename, None)
|
| + error_handler.HandleError(
|
| + error.Error(errors.FILE_NOT_FOUND, 'File not found'))
|
| + error_handler.FinishFile()
|
| + return
|
| +
|
| + if _IsHtml(filename):
|
| + source_file = htmlutil.GetScriptLines(source)
|
| + else:
|
| + source_file = source
|
| +
|
| + token, tokenizer_mode = _Tokenize(source_file)
|
| +
|
| + error_handler.HandleFile(filename, token)
|
| +
|
| + # If we did not end in the basic mode, this a failed parse.
|
| + if tokenizer_mode is not javascripttokenizer.JavaScriptModes.TEXT_MODE:
|
| + error_handler.HandleError(
|
| + error.Error(errors.FILE_IN_BLOCK,
|
| + 'File ended in mode "%s".' % tokenizer_mode,
|
| + _GetLastNonWhiteSpaceToken(token)))
|
| +
|
| + # Run the ECMA pass
|
| + error_token = None
|
| +
|
| + ecma_pass = ecmametadatapass.EcmaMetaDataPass()
|
| + error_token = RunMetaDataPass(token, ecma_pass, error_handler, filename)
|
| +
|
| + is_limited_doc_check = (
|
| + _IsLimitedDocCheck(filename, flags.FLAGS.limited_doc_files))
|
| +
|
| + _RunChecker(token, error_handler,
|
| + is_limited_doc_check,
|
| + is_html=_IsHtml(filename),
|
| + stop_token=error_token)
|
| +
|
| + error_handler.FinishFile()
|
| +
|
| +
|
| +def RunMetaDataPass(start_token, metadata_pass, error_handler, filename=''):
|
| + """Run a metadata pass over a token stream.
|
| +
|
| + Args:
|
| + start_token: The first token in a token stream.
|
| + metadata_pass: Metadata pass to run.
|
| + error_handler: The error handler to report errors to.
|
| + filename: Filename of the source.
|
| +
|
| + Returns:
|
| + The token where the error occurred (if any).
|
| + """
|
| +
|
| + try:
|
| + metadata_pass.Process(start_token)
|
| + except ecmametadatapass.ParseError, parse_err:
|
| + if flags.FLAGS.error_trace:
|
| + traceback.print_exc()
|
| + error_token = parse_err.token
|
| + error_msg = str(parse_err)
|
| + error_handler.HandleError(
|
| + error.Error(errors.FILE_DOES_NOT_PARSE,
|
| + ('Error parsing file at token "%s". Unable to '
|
| + 'check the rest of file.'
|
| + '\nError "%s"' % (error_token, error_msg)), error_token))
|
| + return error_token
|
| + except Exception: # pylint: disable=broad-except
|
| + traceback.print_exc()
|
| + error_handler.HandleError(
|
| + error.Error(
|
| + errors.FILE_DOES_NOT_PARSE,
|
| + 'Internal error in %s' % filename))
|
| +
|
| +
|
| +def _RunChecker(start_token, error_handler,
|
| + limited_doc_checks, is_html,
|
| + stop_token=None):
|
| +
|
| + state_tracker = javascriptstatetracker.JavaScriptStateTracker()
|
| +
|
| + style_checker = checker.JavaScriptStyleChecker(
|
| + state_tracker=state_tracker,
|
| + error_handler=error_handler)
|
| +
|
| + style_checker.Check(start_token,
|
| + is_html=is_html,
|
| + limited_doc_checks=limited_doc_checks,
|
| + stop_token=stop_token)
|
|
|