Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(12)

Unified Diff: third_party/closure_linter/closure_linter/error_fixer.py

Issue 2328693002: Updated linter with upstream release (2.3.19) (Closed)
Patch Set: Created 4 years, 3 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
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
index b3d3162f1d963efe234f5c5e99b6b79c5a9f0367..221550a202441a41368e82083f732ee2ff99a97b 100755
--- a/third_party/closure_linter/closure_linter/error_fixer.py
+++ b/third_party/closure_linter/closure_linter/error_fixer.py
@@ -50,6 +50,9 @@ INVERTED_AUTHOR_SPEC = re.compile(r'(?P<leading_whitespace>\s*)'
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):
@@ -68,6 +71,12 @@ class ErrorFixer(errorhandler.ErrorHandler):
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.
@@ -94,6 +103,46 @@ class ErrorFixer(errorhandler.ErrorHandler):
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.
@@ -103,24 +152,11 @@ class ErrorFixer(errorhandler.ErrorHandler):
code = error.code
token = error.token
- if code == errors.JSDOC_PREFER_QUESTION_TO_PIPE_NULL:
- iterator = token.attached_object.type_start_token
- if iterator.type == Type.DOC_START_BRACE or iterator.string.isspace():
- iterator = iterator.next
-
- leading_space = len(iterator.string) - len(iterator.string.lstrip())
- iterator.string = '%s?%s' % (' ' * leading_space,
- iterator.string.lstrip())
-
- # Cover the no outer brace case where the end token is part of the type.
- while iterator and iterator != token.attached_object.type_end_token.next:
- iterator.string = iterator.string.replace(
- 'null|', '').replace('|null', '')
- iterator = iterator.next
+ if self._fix_error_codes and code not in self._fix_error_codes:
+ return
- # Create a new flag object with updated type info.
- token.attached_object = javascriptstatetracker.JsDocFlag(token)
- self._AddFix(token)
+ 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
@@ -285,6 +321,43 @@ class ErrorFixer(errorhandler.ErrorHandler):
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()
@@ -375,42 +448,59 @@ class ErrorFixer(errorhandler.ErrorHandler):
elif code in [errors.EXTRA_GOOG_PROVIDE, errors.EXTRA_GOOG_REQUIRE]:
tokens_in_line = tokenutil.GetAllTokensInSameLine(token)
- self._DeleteTokens(tokens_in_line[0], len(tokens_in_line))
+ 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]:
- is_provide = code == errors.MISSING_GOOG_PROVIDE
- is_require = code == errors.MISSING_GOOG_REQUIRE
-
missing_namespaces = error.fix_data[0]
- need_blank_line = error.fix_data[1]
+ need_blank_line = error.fix_data[1] or (not token.previous)
- if need_blank_line is None:
- # TODO(user): This happens when there are no existing
- # goog.provide or goog.require statements to position new statements
- # relative to. Consider handling this case with a heuristic.
- return
+ insert_location = Token('', Type.NORMAL, '', token.line_number - 1)
+ dummy_first_token = insert_location
+ tokenutil.InsertTokenBefore(insert_location, token)
- insert_location = token.previous
-
- # If inserting a missing require with no existing requires, insert a
- # blank line first.
- if need_blank_line and is_require:
+ # 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(
- is_provide, missing_namespace, insert_location.line_number + 1)
+ 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 missing provide with no existing provides, insert a
- # blank line after.
- if need_blank_line and is_provide:
+ # 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.

Powered by Google App Engine
This is Rietveld 408576698