Index: third_party/closure_linter/closure_linter/error_fixer.py |
diff --git a/third_party/closure_linter/closure_linter/error_fixer.py b/third_party/closure_linter/closure_linter/error_fixer.py |
deleted file mode 100755 |
index 221550a202441a41368e82083f732ee2ff99a97b..0000000000000000000000000000000000000000 |
--- a/third_party/closure_linter/closure_linter/error_fixer.py |
+++ /dev/null |
@@ -1,620 +0,0 @@ |
-#!/usr/bin/env python |
-# |
-# Copyright 2007 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 class responsible for automatically fixing simple style violations.""" |
- |
-# Allow non-Google copyright |
-# pylint: disable=g-bad-file-header |
- |
-__author__ = 'robbyw@google.com (Robert Walker)' |
- |
-import re |
- |
-import gflags as flags |
-from closure_linter import errors |
-from closure_linter import javascriptstatetracker |
-from closure_linter import javascripttokens |
-from closure_linter import requireprovidesorter |
-from closure_linter import tokenutil |
-from closure_linter.common import errorhandler |
- |
-# Shorthand |
-Token = javascripttokens.JavaScriptToken |
-Type = javascripttokens.JavaScriptTokenType |
- |
-END_OF_FLAG_TYPE = re.compile(r'(}?\s*)$') |
- |
-# Regex to represent common mistake inverting author name and email as |
-# @author User Name (user@company) |
-INVERTED_AUTHOR_SPEC = re.compile(r'(?P<leading_whitespace>\s*)' |
- r'(?P<name>[^(]+)' |
- r'(?P<whitespace_after_name>\s+)' |
- r'\(' |
- r'(?P<email>[^\s]+@[^)\s]+)' |
- r'\)' |
- r'(?P<trailing_characters>.*)') |
- |
-FLAGS = flags.FLAGS |
-flags.DEFINE_boolean('disable_indentation_fixing', False, |
- 'Whether to disable automatic fixing of indentation.') |
-flags.DEFINE_list('fix_error_codes', [], 'A list of specific error codes to ' |
- 'fix. Defaults to all supported error codes when empty. ' |
- 'See errors.py for a list of error codes.') |
- |
- |
-class ErrorFixer(errorhandler.ErrorHandler): |
- """Object that fixes simple style errors.""" |
- |
- def __init__(self, external_file=None): |
- """Initialize the error fixer. |
- |
- Args: |
- external_file: If included, all output will be directed to this file |
- instead of overwriting the files the errors are found in. |
- """ |
- errorhandler.ErrorHandler.__init__(self) |
- |
- self._file_name = None |
- self._file_token = None |
- self._external_file = external_file |
- |
- try: |
- self._fix_error_codes = set([errors.ByName(error.upper()) for error in |
- FLAGS.fix_error_codes]) |
- except KeyError as ke: |
- raise ValueError('Unknown error code ' + ke.args[0]) |
- |
- def HandleFile(self, filename, first_token): |
- """Notifies this ErrorPrinter that subsequent errors are in filename. |
- |
- Args: |
- filename: The name of the file about to be checked. |
- first_token: The first token in the file. |
- """ |
- self._file_name = filename |
- self._file_is_html = filename.endswith('.html') or filename.endswith('.htm') |
- self._file_token = first_token |
- self._file_fix_count = 0 |
- self._file_changed_lines = set() |
- |
- def _AddFix(self, tokens): |
- """Adds the fix to the internal count. |
- |
- Args: |
- tokens: The token or sequence of tokens changed to fix an error. |
- """ |
- self._file_fix_count += 1 |
- if hasattr(tokens, 'line_number'): |
- self._file_changed_lines.add(tokens.line_number) |
- else: |
- for token in tokens: |
- self._file_changed_lines.add(token.line_number) |
- |
- def _FixJsDocPipeNull(self, js_type): |
- """Change number|null or null|number to ?number. |
- |
- Args: |
- js_type: The typeannotation.TypeAnnotation instance to fix. |
- """ |
- |
- # Recurse into all sub_types if the error was at a deeper level. |
- map(self._FixJsDocPipeNull, js_type.IterTypes()) |
- |
- if js_type.type_group and len(js_type.sub_types) == 2: |
- # Find and remove the null sub_type: |
- sub_type = None |
- for sub_type in js_type.sub_types: |
- if sub_type.identifier == 'null': |
- map(tokenutil.DeleteToken, sub_type.tokens) |
- self._AddFix(sub_type.tokens) |
- break |
- else: |
- return |
- |
- first_token = js_type.FirstToken() |
- question_mark = Token('?', Type.DOC_TYPE_MODIFIER, first_token.line, |
- first_token.line_number) |
- tokenutil.InsertTokenBefore(question_mark, first_token) |
- js_type.tokens.insert(0, question_mark) |
- js_type.tokens.remove(sub_type) |
- js_type.sub_types.remove(sub_type) |
- js_type.or_null = True |
- |
- # Now also remove the separator, which is in the parent's token list, |
- # either before or after the sub_type, there is exactly one. Scan for it. |
- for token in js_type.tokens: |
- if (token and isinstance(token, Token) and |
- token.type == Type.DOC_TYPE_MODIFIER and token.string == '|'): |
- tokenutil.DeleteToken(token) |
- js_type.tokens.remove(token) |
- self._AddFix(token) |
- break |
- |
- def HandleError(self, error): |
- """Attempts to fix the error. |
- |
- Args: |
- error: The error object |
- """ |
- code = error.code |
- token = error.token |
- |
- if self._fix_error_codes and code not in self._fix_error_codes: |
- return |
- |
- if code == errors.JSDOC_PREFER_QUESTION_TO_PIPE_NULL: |
- self._FixJsDocPipeNull(token.attached_object.jstype) |
- |
- elif code == errors.JSDOC_MISSING_OPTIONAL_TYPE: |
- iterator = token.attached_object.type_end_token |
- if iterator.type == Type.DOC_END_BRACE or iterator.string.isspace(): |
- iterator = iterator.previous |
- |
- ending_space = len(iterator.string) - len(iterator.string.rstrip()) |
- iterator.string = '%s=%s' % (iterator.string.rstrip(), |
- ' ' * ending_space) |
- |
- # Create a new flag object with updated type info. |
- token.attached_object = javascriptstatetracker.JsDocFlag(token) |
- self._AddFix(token) |
- |
- elif code == errors.JSDOC_MISSING_VAR_ARGS_TYPE: |
- iterator = token.attached_object.type_start_token |
- if iterator.type == Type.DOC_START_BRACE or iterator.string.isspace(): |
- iterator = iterator.next |
- |
- starting_space = len(iterator.string) - len(iterator.string.lstrip()) |
- iterator.string = '%s...%s' % (' ' * starting_space, |
- iterator.string.lstrip()) |
- |
- # Create a new flag object with updated type info. |
- token.attached_object = javascriptstatetracker.JsDocFlag(token) |
- self._AddFix(token) |
- |
- elif code in (errors.MISSING_SEMICOLON_AFTER_FUNCTION, |
- errors.MISSING_SEMICOLON): |
- semicolon_token = Token(';', Type.SEMICOLON, token.line, |
- token.line_number) |
- tokenutil.InsertTokenAfter(semicolon_token, token) |
- token.metadata.is_implied_semicolon = False |
- semicolon_token.metadata.is_implied_semicolon = False |
- self._AddFix(token) |
- |
- elif code in (errors.ILLEGAL_SEMICOLON_AFTER_FUNCTION, |
- errors.REDUNDANT_SEMICOLON, |
- errors.COMMA_AT_END_OF_LITERAL): |
- self._DeleteToken(token) |
- self._AddFix(token) |
- |
- elif code == errors.INVALID_JSDOC_TAG: |
- if token.string == '@returns': |
- token.string = '@return' |
- self._AddFix(token) |
- |
- elif code == errors.FILE_MISSING_NEWLINE: |
- # This error is fixed implicitly by the way we restore the file |
- self._AddFix(token) |
- |
- elif code == errors.MISSING_SPACE: |
- if error.fix_data: |
- token.string = error.fix_data |
- self._AddFix(token) |
- elif error.position: |
- if error.position.IsAtBeginning(): |
- tokenutil.InsertSpaceTokenAfter(token.previous) |
- elif error.position.IsAtEnd(token.string): |
- tokenutil.InsertSpaceTokenAfter(token) |
- else: |
- token.string = error.position.Set(token.string, ' ') |
- self._AddFix(token) |
- |
- elif code == errors.EXTRA_SPACE: |
- if error.position: |
- token.string = error.position.Set(token.string, '') |
- self._AddFix(token) |
- |
- elif code == errors.MISSING_LINE: |
- if error.position.IsAtBeginning(): |
- tokenutil.InsertBlankLineAfter(token.previous) |
- else: |
- tokenutil.InsertBlankLineAfter(token) |
- self._AddFix(token) |
- |
- elif code == errors.EXTRA_LINE: |
- self._DeleteToken(token) |
- self._AddFix(token) |
- |
- elif code == errors.WRONG_BLANK_LINE_COUNT: |
- if not token.previous: |
- # TODO(user): Add an insertBefore method to tokenutil. |
- return |
- |
- num_lines = error.fix_data |
- should_delete = False |
- |
- if num_lines < 0: |
- num_lines *= -1 |
- should_delete = True |
- |
- for unused_i in xrange(1, num_lines + 1): |
- if should_delete: |
- # TODO(user): DeleteToken should update line numbers. |
- self._DeleteToken(token.previous) |
- else: |
- tokenutil.InsertBlankLineAfter(token.previous) |
- self._AddFix(token) |
- |
- elif code == errors.UNNECESSARY_DOUBLE_QUOTED_STRING: |
- end_quote = tokenutil.Search(token, Type.DOUBLE_QUOTE_STRING_END) |
- if end_quote: |
- single_quote_start = Token( |
- "'", Type.SINGLE_QUOTE_STRING_START, token.line, token.line_number) |
- single_quote_end = Token( |
- "'", Type.SINGLE_QUOTE_STRING_START, end_quote.line, |
- token.line_number) |
- |
- tokenutil.InsertTokenAfter(single_quote_start, token) |
- tokenutil.InsertTokenAfter(single_quote_end, end_quote) |
- self._DeleteToken(token) |
- self._DeleteToken(end_quote) |
- self._AddFix([token, end_quote]) |
- |
- elif code == errors.MISSING_BRACES_AROUND_TYPE: |
- fixed_tokens = [] |
- start_token = token.attached_object.type_start_token |
- |
- if start_token.type != Type.DOC_START_BRACE: |
- leading_space = ( |
- len(start_token.string) - len(start_token.string.lstrip())) |
- if leading_space: |
- start_token = tokenutil.SplitToken(start_token, leading_space) |
- # Fix case where start and end token were the same. |
- if token.attached_object.type_end_token == start_token.previous: |
- token.attached_object.type_end_token = start_token |
- |
- new_token = Token('{', Type.DOC_START_BRACE, start_token.line, |
- start_token.line_number) |
- tokenutil.InsertTokenAfter(new_token, start_token.previous) |
- token.attached_object.type_start_token = new_token |
- fixed_tokens.append(new_token) |
- |
- end_token = token.attached_object.type_end_token |
- if end_token.type != Type.DOC_END_BRACE: |
- # If the start token was a brace, the end token will be a |
- # FLAG_ENDING_TYPE token, if there wasn't a starting brace then |
- # the end token is the last token of the actual type. |
- last_type = end_token |
- if not fixed_tokens: |
- last_type = end_token.previous |
- |
- while last_type.string.isspace(): |
- last_type = last_type.previous |
- |
- # If there was no starting brace then a lone end brace wouldn't have |
- # been type end token. Now that we've added any missing start brace, |
- # see if the last effective type token was an end brace. |
- if last_type.type != Type.DOC_END_BRACE: |
- trailing_space = (len(last_type.string) - |
- len(last_type.string.rstrip())) |
- if trailing_space: |
- tokenutil.SplitToken(last_type, |
- len(last_type.string) - trailing_space) |
- |
- new_token = Token('}', Type.DOC_END_BRACE, last_type.line, |
- last_type.line_number) |
- tokenutil.InsertTokenAfter(new_token, last_type) |
- token.attached_object.type_end_token = new_token |
- fixed_tokens.append(new_token) |
- |
- self._AddFix(fixed_tokens) |
- |
- elif code == errors.LINE_STARTS_WITH_OPERATOR: |
- # Remove whitespace following the operator so the line starts clean. |
- self._StripSpace(token, before=False) |
- |
- # Remove the operator. |
- tokenutil.DeleteToken(token) |
- self._AddFix(token) |
- |
- insertion_point = tokenutil.GetPreviousCodeToken(token) |
- |
- # Insert a space between the previous token and the new operator. |
- space = Token(' ', Type.WHITESPACE, insertion_point.line, |
- insertion_point.line_number) |
- tokenutil.InsertTokenAfter(space, insertion_point) |
- |
- # Insert the operator on the end of the previous line. |
- new_token = Token(token.string, token.type, insertion_point.line, |
- insertion_point.line_number) |
- tokenutil.InsertTokenAfter(new_token, space) |
- self._AddFix(new_token) |
- |
- elif code == errors.LINE_ENDS_WITH_DOT: |
- # Remove whitespace preceding the operator to remove trailing whitespace. |
- self._StripSpace(token, before=True) |
- |
- # Remove the dot. |
- tokenutil.DeleteToken(token) |
- self._AddFix(token) |
- |
- insertion_point = tokenutil.GetNextCodeToken(token) |
- |
- # Insert the dot at the beginning of the next line of code. |
- new_token = Token(token.string, token.type, insertion_point.line, |
- insertion_point.line_number) |
- tokenutil.InsertTokenBefore(new_token, insertion_point) |
- self._AddFix(new_token) |
- |
- elif code == errors.GOOG_REQUIRES_NOT_ALPHABETIZED: |
- require_start_token = error.fix_data |
- sorter = requireprovidesorter.RequireProvideSorter() |
- sorter.FixRequires(require_start_token) |
- |
- self._AddFix(require_start_token) |
- |
- elif code == errors.GOOG_PROVIDES_NOT_ALPHABETIZED: |
- provide_start_token = error.fix_data |
- sorter = requireprovidesorter.RequireProvideSorter() |
- sorter.FixProvides(provide_start_token) |
- |
- self._AddFix(provide_start_token) |
- |
- elif code == errors.UNNECESSARY_BRACES_AROUND_INHERIT_DOC: |
- if token.previous.string == '{' and token.next.string == '}': |
- self._DeleteToken(token.previous) |
- self._DeleteToken(token.next) |
- self._AddFix([token]) |
- |
- elif code == errors.INVALID_AUTHOR_TAG_DESCRIPTION: |
- match = INVERTED_AUTHOR_SPEC.match(token.string) |
- if match: |
- token.string = '%s%s%s(%s)%s' % (match.group('leading_whitespace'), |
- match.group('email'), |
- match.group('whitespace_after_name'), |
- match.group('name'), |
- match.group('trailing_characters')) |
- self._AddFix(token) |
- |
- elif (code == errors.WRONG_INDENTATION and |
- not FLAGS.disable_indentation_fixing): |
- token = tokenutil.GetFirstTokenInSameLine(token) |
- actual = error.position.start |
- expected = error.position.length |
- |
- # Cases where first token is param but with leading spaces. |
- if (len(token.string.lstrip()) == len(token.string) - actual and |
- token.string.lstrip()): |
- token.string = token.string.lstrip() |
- actual = 0 |
- |
- if token.type in (Type.WHITESPACE, Type.PARAMETERS) and actual != 0: |
- token.string = token.string.lstrip() + (' ' * expected) |
- self._AddFix([token]) |
- else: |
- # We need to add indentation. |
- new_token = Token(' ' * expected, Type.WHITESPACE, |
- token.line, token.line_number) |
- # Note that we'll never need to add indentation at the first line, |
- # since it will always not be indented. Therefore it's safe to assume |
- # token.previous exists. |
- tokenutil.InsertTokenAfter(new_token, token.previous) |
- self._AddFix([token]) |
- |
- elif code in [errors.MALFORMED_END_OF_SCOPE_COMMENT, |
- errors.MISSING_END_OF_SCOPE_COMMENT]: |
- # Only fix cases where }); is found with no trailing content on the line |
- # other than a comment. Value of 'token' is set to } for this error. |
- if (token.type == Type.END_BLOCK and |
- token.next.type == Type.END_PAREN and |
- token.next.next.type == Type.SEMICOLON): |
- current_token = token.next.next.next |
- removed_tokens = [] |
- while current_token and current_token.line_number == token.line_number: |
- if current_token.IsAnyType(Type.WHITESPACE, |
- Type.START_SINGLE_LINE_COMMENT, |
- Type.COMMENT): |
- removed_tokens.append(current_token) |
- current_token = current_token.next |
- else: |
- return |
- |
- if removed_tokens: |
- self._DeleteTokens(removed_tokens[0], len(removed_tokens)) |
- |
- whitespace_token = Token(' ', Type.WHITESPACE, token.line, |
- token.line_number) |
- start_comment_token = Token('//', Type.START_SINGLE_LINE_COMMENT, |
- token.line, token.line_number) |
- comment_token = Token(' goog.scope', Type.COMMENT, token.line, |
- token.line_number) |
- insertion_tokens = [whitespace_token, start_comment_token, |
- comment_token] |
- |
- tokenutil.InsertTokensAfter(insertion_tokens, token.next.next) |
- self._AddFix(removed_tokens + insertion_tokens) |
- |
- elif code in [errors.EXTRA_GOOG_PROVIDE, errors.EXTRA_GOOG_REQUIRE]: |
- tokens_in_line = tokenutil.GetAllTokensInSameLine(token) |
- num_delete_tokens = len(tokens_in_line) |
- # If line being deleted is preceded and succeed with blank lines then |
- # delete one blank line also. |
- if (tokens_in_line[0].previous and tokens_in_line[-1].next |
- and tokens_in_line[0].previous.type == Type.BLANK_LINE |
- and tokens_in_line[-1].next.type == Type.BLANK_LINE): |
- num_delete_tokens += 1 |
- self._DeleteTokens(tokens_in_line[0], num_delete_tokens) |
- self._AddFix(tokens_in_line) |
- |
- elif code in [errors.MISSING_GOOG_PROVIDE, errors.MISSING_GOOG_REQUIRE]: |
- missing_namespaces = error.fix_data[0] |
- need_blank_line = error.fix_data[1] or (not token.previous) |
- |
- insert_location = Token('', Type.NORMAL, '', token.line_number - 1) |
- dummy_first_token = insert_location |
- tokenutil.InsertTokenBefore(insert_location, token) |
- |
- # If inserting a blank line check blank line does not exist before |
- # token to avoid extra blank lines. |
- if (need_blank_line and insert_location.previous |
- and insert_location.previous.type != Type.BLANK_LINE): |
- tokenutil.InsertBlankLineAfter(insert_location) |
- insert_location = insert_location.next |
- |
- for missing_namespace in missing_namespaces: |
- new_tokens = self._GetNewRequireOrProvideTokens( |
- code == errors.MISSING_GOOG_PROVIDE, |
- missing_namespace, insert_location.line_number + 1) |
- tokenutil.InsertLineAfter(insert_location, new_tokens) |
- insert_location = new_tokens[-1] |
- self._AddFix(new_tokens) |
- |
- # If inserting a blank line check blank line does not exist after |
- # token to avoid extra blank lines. |
- if (need_blank_line and insert_location.next |
- and insert_location.next.type != Type.BLANK_LINE): |
- tokenutil.InsertBlankLineAfter(insert_location) |
- |
- tokenutil.DeleteToken(dummy_first_token) |
- |
- def _StripSpace(self, token, before): |
- """Strip whitespace tokens either preceding or following the given token. |
- |
- Args: |
- token: The token. |
- before: If true, strip space before the token, if false, after it. |
- """ |
- token = token.previous if before else token.next |
- while token and token.type == Type.WHITESPACE: |
- tokenutil.DeleteToken(token) |
- token = token.previous if before else token.next |
- |
- def _GetNewRequireOrProvideTokens(self, is_provide, namespace, line_number): |
- """Returns a list of tokens to create a goog.require/provide statement. |
- |
- Args: |
- is_provide: True if getting tokens for a provide, False for require. |
- namespace: The required or provided namespaces to get tokens for. |
- line_number: The line number the new require or provide statement will be |
- on. |
- |
- Returns: |
- Tokens to create a new goog.require or goog.provide statement. |
- """ |
- string = 'goog.require' |
- if is_provide: |
- string = 'goog.provide' |
- line_text = string + '(\'' + namespace + '\');\n' |
- return [ |
- Token(string, Type.IDENTIFIER, line_text, line_number), |
- Token('(', Type.START_PAREN, line_text, line_number), |
- Token('\'', Type.SINGLE_QUOTE_STRING_START, line_text, line_number), |
- Token(namespace, Type.STRING_TEXT, line_text, line_number), |
- Token('\'', Type.SINGLE_QUOTE_STRING_END, line_text, line_number), |
- Token(')', Type.END_PAREN, line_text, line_number), |
- Token(';', Type.SEMICOLON, line_text, line_number) |
- ] |
- |
- def _DeleteToken(self, token): |
- """Deletes the specified token from the linked list of tokens. |
- |
- Updates instance variables pointing to tokens such as _file_token if |
- they reference the deleted token. |
- |
- Args: |
- token: The token to delete. |
- """ |
- if token == self._file_token: |
- self._file_token = token.next |
- |
- tokenutil.DeleteToken(token) |
- |
- def _DeleteTokens(self, token, token_count): |
- """Deletes the given number of tokens starting with the given token. |
- |
- Updates instance variables pointing to tokens such as _file_token if |
- they reference the deleted token. |
- |
- Args: |
- token: The first token to delete. |
- token_count: The total number of tokens to delete. |
- """ |
- if token == self._file_token: |
- for unused_i in xrange(token_count): |
- self._file_token = self._file_token.next |
- |
- tokenutil.DeleteTokens(token, token_count) |
- |
- def FinishFile(self): |
- """Called when the current file has finished style checking. |
- |
- Used to go back and fix any errors in the file. It currently supports both |
- js and html files. For js files it does a simple dump of all tokens, but in |
- order to support html file, we need to merge the original file with the new |
- token set back together. This works because the tokenized html file is the |
- original html file with all non js lines kept but blanked out with one blank |
- line token per line of html. |
- """ |
- if self._file_fix_count: |
- # Get the original file content for html. |
- if self._file_is_html: |
- f = open(self._file_name, 'r') |
- original_lines = f.readlines() |
- f.close() |
- |
- f = self._external_file |
- if not f: |
- error_noun = 'error' if self._file_fix_count == 1 else 'errors' |
- print 'Fixed %d %s in %s' % ( |
- self._file_fix_count, error_noun, self._file_name) |
- f = open(self._file_name, 'w') |
- |
- token = self._file_token |
- # Finding the first not deleted token. |
- while token.is_deleted: |
- token = token.next |
- # If something got inserted before first token (e.g. due to sorting) |
- # then move to start. Bug 8398202. |
- while token.previous: |
- token = token.previous |
- char_count = 0 |
- line = '' |
- while token: |
- line += token.string |
- char_count += len(token.string) |
- |
- if token.IsLastInLine(): |
- # We distinguish if a blank line in html was from stripped original |
- # file or newly added error fix by looking at the "org_line_number" |
- # field on the token. It is only set in the tokenizer, so for all |
- # error fixes, the value should be None. |
- if (line or not self._file_is_html or |
- token.orig_line_number is None): |
- f.write(line) |
- f.write('\n') |
- else: |
- f.write(original_lines[token.orig_line_number - 1]) |
- line = '' |
- if char_count > 80 and token.line_number in self._file_changed_lines: |
- print 'WARNING: Line %d of %s is now longer than 80 characters.' % ( |
- token.line_number, self._file_name) |
- |
- char_count = 0 |
- |
- token = token.next |
- |
- if not self._external_file: |
- # Close the file if we created it |
- f.close() |