diff options
Diffstat (limited to 'tools/closure_linter-2.3.4/closure_linter/checkerbase.py')
-rwxr-xr-x | tools/closure_linter-2.3.4/closure_linter/checkerbase.py | 308 |
1 files changed, 308 insertions, 0 deletions
diff --git a/tools/closure_linter-2.3.4/closure_linter/checkerbase.py b/tools/closure_linter-2.3.4/closure_linter/checkerbase.py new file mode 100755 index 0000000..592454d --- /dev/null +++ b/tools/closure_linter-2.3.4/closure_linter/checkerbase.py @@ -0,0 +1,308 @@ +#!/usr/bin/env python +# +# Copyright 2008 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. + +"""Base classes for writing checkers that operate on tokens.""" + +__author__ = ('robbyw@google.com (Robert Walker)', + 'ajp@google.com (Andy Perelson)', + 'jacobr@google.com (Jacob Richman)') + +import StringIO +import traceback + +import gflags as flags +from closure_linter import ecmametadatapass +from closure_linter import errorrules +from closure_linter import errors +from closure_linter import javascripttokenizer +from closure_linter.common import error +from closure_linter.common import htmlutil + +FLAGS = flags.FLAGS +flags.DEFINE_boolean('debug_tokens', False, + 'Whether to print all tokens for debugging.') + +flags.DEFINE_boolean('error_trace', False, + 'Whether to show error exceptions.') + + +class LintRulesBase(object): + """Base class for all classes defining the lint rules for a language.""" + + def __init__(self): + self.__checker = None + + def Initialize(self, checker, limited_doc_checks, is_html): + """Initializes to prepare to check a file. + + Args: + checker: Class to report errors to. + limited_doc_checks: Whether doc checking is relaxed for this file. + is_html: Whether the file is an HTML file with extracted contents. + """ + self.__checker = checker + self._limited_doc_checks = limited_doc_checks + self._is_html = is_html + + def _HandleError(self, code, message, token, position=None, + fix_data=None): + """Call the HandleError function for the checker we are associated with.""" + if errorrules.ShouldReportError(code): + self.__checker.HandleError(code, message, token, position, fix_data) + + def _SetLimitedDocChecks(self, limited_doc_checks): + """Sets whether doc checking is relaxed for this file. + + Args: + limited_doc_checks: Whether doc checking is relaxed for this file. + """ + self._limited_doc_checks = limited_doc_checks + + def CheckToken(self, token, parser_state): + """Checks a token, given the current parser_state, for warnings and errors. + + Args: + token: The current token under consideration. + parser_state: Object that indicates the parser state in the page. + + Raises: + TypeError: If not overridden. + """ + raise TypeError('Abstract method CheckToken not implemented') + + def Finalize(self, parser_state, tokenizer_mode): + """Perform all checks that need to occur after all lines are processed. + + Args: + parser_state: State of the parser after parsing all tokens + tokenizer_mode: Mode of the tokenizer after parsing the entire page + + Raises: + TypeError: If not overridden. + """ + raise TypeError('Abstract method Finalize not implemented') + + +class CheckerBase(object): + """This class handles checking a LintRules object against a file.""" + + def __init__(self, error_handler, lint_rules, state_tracker, + limited_doc_files=None, metadata_pass=None): + """Initialize a checker object. + + Args: + error_handler: Object that handles errors. + lint_rules: LintRules object defining lint errors given a token + and state_tracker object. + state_tracker: Object that tracks the current state in the token stream. + limited_doc_files: List of filenames that are not required to have + documentation comments. + metadata_pass: Object that builds metadata about the token stream. + """ + self._error_handler = error_handler + self._lint_rules = lint_rules + self._state_tracker = state_tracker + self._metadata_pass = metadata_pass + self._limited_doc_files = limited_doc_files + self._tokenizer = javascripttokenizer.JavaScriptTokenizer() + self._has_errors = False + + def HandleError(self, code, message, token, position=None, + fix_data=None): + """Prints out the given error message including a line number. + + Args: + code: The error code. + message: The error to print. + token: The token where the error occurred, or None if it was a file-wide + issue. + position: The position of the error, defaults to None. + fix_data: Metadata used for fixing the error. + """ + self._has_errors = True + self._error_handler.HandleError( + error.Error(code, message, token, position, fix_data)) + + def HasErrors(self): + """Returns true if the style checker has found any errors. + + Returns: + True if the style checker has found any errors. + """ + return self._has_errors + + def Check(self, filename, source=None): + """Checks the file, printing warnings and errors as they are found. + + Args: + filename: The name of the file to check. + source: Optional. The contents of the file. Can be either a string or + file-like object. If omitted, contents will be read from disk from + the given filename. + """ + + if source is None: + try: + f = open(filename) + except IOError: + self._error_handler.HandleFile(filename, None) + self.HandleError(errors.FILE_NOT_FOUND, 'File not found', None) + self._error_handler.FinishFile() + return + else: + if type(source) in [str, unicode]: + f = StringIO.StringIO(source) + else: + f = source + + try: + if filename.endswith('.html') or filename.endswith('.htm'): + self.CheckLines(filename, htmlutil.GetScriptLines(f), True) + else: + self.CheckLines(filename, f, False) + finally: + f.close() + + def CheckLines(self, filename, lines_iter, is_html): + """Checks a file, given as an iterable of lines, for warnings and errors. + + Args: + filename: The name of the file to check. + lines_iter: An iterator that yields one line of the file at a time. + is_html: Whether the file being checked is an HTML file with extracted + contents. + + Returns: + A boolean indicating whether the full file could be checked or if checking + failed prematurely. + """ + limited_doc_checks = False + if self._limited_doc_files: + for limited_doc_filename in self._limited_doc_files: + if filename.endswith(limited_doc_filename): + limited_doc_checks = True + break + + lint_rules = self._lint_rules + lint_rules.Initialize(self, limited_doc_checks, is_html) + + token = self._tokenizer.TokenizeFile(lines_iter) + + parse_error = None + if self._metadata_pass: + try: + self._metadata_pass.Reset() + self._metadata_pass.Process(token) + except ecmametadatapass.ParseError, caught_parse_error: + if FLAGS.error_trace: + traceback.print_exc() + parse_error = caught_parse_error + except Exception: + print 'Internal error in %s' % filename + traceback.print_exc() + return False + + self._error_handler.HandleFile(filename, token) + + return self._CheckTokens(token, parse_error=parse_error, + debug_tokens=FLAGS.debug_tokens) + + def _CheckTokens(self, token, parse_error, debug_tokens): + """Checks a token stream for lint warnings/errors. + + Args: + token: The first token in the token stream to check. + parse_error: A ParseError if any errors occurred. + debug_tokens: Whether every token should be printed as it is encountered + during the pass. + + Returns: + A boolean indicating whether the full token stream could be checked or if + checking failed prematurely. + """ + result = self._ExecutePass(token, self._LintPass, parse_error, debug_tokens) + + if not result: + return False + + self._lint_rules.Finalize(self._state_tracker, self._tokenizer.mode) + self._error_handler.FinishFile() + return True + + def _LintPass(self, token): + """Checks an individual token for lint warnings/errors. + + Used to encapsulate the logic needed to check an individual token so that it + can be passed to _ExecutePass. + + Args: + token: The token to check. + """ + self._lint_rules.CheckToken(token, self._state_tracker) + + def _ExecutePass(self, token, pass_function, parse_error=None, + debug_tokens=False): + """Calls the given function for every token in the given token stream. + + As each token is passed to the given function, state is kept up to date and, + depending on the error_trace flag, errors are either caught and reported, or + allowed to bubble up so developers can see the full stack trace. If a parse + error is specified, the pass will proceed as normal until the token causing + the parse error is reached. + + Args: + token: The first token in the token stream. + pass_function: The function to call for each token in the token stream. + parse_error: A ParseError if any errors occurred. + debug_tokens: Whether every token should be printed as it is encountered + during the pass. + + Returns: + A boolean indicating whether the full token stream could be checked or if + checking failed prematurely. + + Raises: + Exception: If any error occurred while calling the given function. + """ + self._state_tracker.Reset() + while token: + if debug_tokens: + print token + + if parse_error and parse_error.token == token: + message = ('Error parsing file at token "%s". Unable to ' + 'check the rest of file.' % token.string) + self.HandleError(errors.FILE_DOES_NOT_PARSE, message, token) + self._error_handler.FinishFile() + return + + try: + self._state_tracker.HandleToken( + token, self._state_tracker.GetLastNonSpaceToken()) + pass_function(token) + self._state_tracker.HandleAfterToken(token) + except: + if FLAGS.error_trace: + raise + else: + self.HandleError(errors.FILE_DOES_NOT_PARSE, + ('Error parsing file at token "%s". Unable to ' + 'check the rest of file.' % token.string), + token) + self._error_handler.FinishFile() + return False + token = token.next + return True |