| Index: third_party/closure_linter/closure_linter/javascriptlintrules.py
|
| diff --git a/third_party/closure_linter/closure_linter/javascriptlintrules.py b/third_party/closure_linter/closure_linter/javascriptlintrules.py
|
| index d72fd56a54d56cbbdcf4a61ec134748775cc6b04..6406ff799b63b81cf36a31d820aec740db041445 100755
|
| --- a/third_party/closure_linter/closure_linter/javascriptlintrules.py
|
| +++ b/third_party/closure_linter/closure_linter/javascriptlintrules.py
|
| @@ -1,5 +1,4 @@
|
| #!/usr/bin/env python
|
| -#
|
| # Copyright 2011 The Closure Linter Authors. All Rights Reserved.
|
| #
|
| # Licensed under the Apache License, Version 2.0 (the "License");
|
| @@ -25,7 +24,7 @@ __author__ = ('robbyw@google.com (Robert Walker)',
|
| 'jacobr@google.com (Jacob Richman)')
|
|
|
| import re
|
| -from sets import Set
|
| +
|
| from closure_linter import ecmalintrules
|
| from closure_linter import error_check
|
| from closure_linter import errors
|
| @@ -51,8 +50,12 @@ class JavaScriptLintRules(ecmalintrules.EcmaScriptLintRules):
|
| ecmalintrules.EcmaScriptLintRules.__init__(self)
|
| self._namespaces_info = namespaces_info
|
| self._declared_private_member_tokens = {}
|
| - self._declared_private_members = Set()
|
| - self._used_private_members = Set()
|
| + self._declared_private_members = set()
|
| + self._used_private_members = set()
|
| + # A stack of dictionaries, one for each function scope entered. Each
|
| + # dictionary is keyed by an identifier that defines a local variable and has
|
| + # a token as its value.
|
| + self._unused_local_variables_by_scope = []
|
|
|
| def HandleMissingParameterDoc(self, token, param_name):
|
| """Handle errors associated with a parameter missing a param tag."""
|
| @@ -75,6 +78,7 @@ class JavaScriptLintRules(ecmalintrules.EcmaScriptLintRules):
|
| token.attached_object.type is not None and
|
| token.attached_object.type.find('{') != token.string.rfind('{'))
|
|
|
| + # pylint: disable=too-many-statements
|
| def CheckToken(self, token, state):
|
| """Checks a token, given the current parser_state, for warnings and errors.
|
|
|
| @@ -82,7 +86,10 @@ class JavaScriptLintRules(ecmalintrules.EcmaScriptLintRules):
|
| token: The current token under consideration
|
| state: parser_state object that indicates the current state in the page
|
| """
|
| - if self.__ContainsRecordType(token):
|
| +
|
| + # For @param don't ignore record type.
|
| + if (self.__ContainsRecordType(token) and
|
| + token.attached_object.flag_type != 'param'):
|
| # We should bail out and not emit any warnings for this annotation.
|
| # TODO(nicksantos): Support record types for real.
|
| state.GetDocComment().Invalidate()
|
| @@ -94,6 +101,9 @@ class JavaScriptLintRules(ecmalintrules.EcmaScriptLintRules):
|
| # Store some convenience variables
|
| namespaces_info = self._namespaces_info
|
|
|
| + if error_check.ShouldCheck(Rule.UNUSED_LOCAL_VARIABLES):
|
| + self._CheckUnusedLocalVariables(token, state)
|
| +
|
| if error_check.ShouldCheck(Rule.UNUSED_PRIVATE_MEMBERS):
|
| # Find all assignments to private members.
|
| if token.type == Type.SIMPLE_LVALUE:
|
| @@ -101,14 +111,20 @@ class JavaScriptLintRules(ecmalintrules.EcmaScriptLintRules):
|
| if identifier.endswith('_') and not identifier.endswith('__'):
|
| doc_comment = state.GetDocComment()
|
| suppressed = (doc_comment and doc_comment.HasFlag('suppress') and
|
| - doc_comment.GetFlag('suppress').type == 'underscore')
|
| + (doc_comment.GetFlag('suppress').type == 'underscore' or
|
| + doc_comment.GetFlag('suppress').type ==
|
| + 'unusedPrivateMembers'))
|
| if not suppressed:
|
| # Look for static members defined on a provided namespace.
|
| - namespace = namespaces_info.GetClosurizedNamespace(identifier)
|
| - provided_namespaces = namespaces_info.GetProvidedNamespaces()
|
| + if namespaces_info:
|
| + namespace = namespaces_info.GetClosurizedNamespace(identifier)
|
| + provided_namespaces = namespaces_info.GetProvidedNamespaces()
|
| + else:
|
| + namespace = None
|
| + provided_namespaces = set()
|
|
|
| # Skip cases of this.something_.somethingElse_.
|
| - regex = re.compile('^this\.[a-zA-Z_]+$')
|
| + regex = re.compile(r'^this\.[a-zA-Z_]+$')
|
| if namespace in provided_namespaces or regex.match(identifier):
|
| variable = identifier.split('.')[-1]
|
| self._declared_private_member_tokens[variable] = token
|
| @@ -132,27 +148,45 @@ class JavaScriptLintRules(ecmalintrules.EcmaScriptLintRules):
|
| self._CheckForMissingSpaceBeforeToken(
|
| token.attached_object.name_token)
|
|
|
| - if (error_check.ShouldCheck(Rule.OPTIONAL_TYPE_MARKER) and
|
| - flag.type is not None and flag.name is not None):
|
| - # Check for optional marker in type.
|
| - if (flag.type.endswith('=') and
|
| - not flag.name.startswith('opt_')):
|
| - self._HandleError(errors.JSDOC_MISSING_OPTIONAL_PREFIX,
|
| - 'Optional parameter name %s must be prefixed '
|
| - 'with opt_.' % flag.name,
|
| - token)
|
| - elif (not flag.type.endswith('=') and
|
| - flag.name.startswith('opt_')):
|
| - self._HandleError(errors.JSDOC_MISSING_OPTIONAL_TYPE,
|
| - 'Optional parameter %s type must end with =.' %
|
| - flag.name,
|
| - token)
|
| + if flag.type is not None and flag.name is not None:
|
| + if error_check.ShouldCheck(Rule.VARIABLE_ARG_MARKER):
|
| + # Check for variable arguments marker in type.
|
| + if (flag.type.startswith('...') and
|
| + flag.name != 'var_args'):
|
| + self._HandleError(errors.JSDOC_MISSING_VAR_ARGS_NAME,
|
| + 'Variable length argument %s must be renamed '
|
| + 'to var_args.' % flag.name,
|
| + token)
|
| + elif (not flag.type.startswith('...') and
|
| + flag.name == 'var_args'):
|
| + self._HandleError(errors.JSDOC_MISSING_VAR_ARGS_TYPE,
|
| + 'Variable length argument %s type must start '
|
| + 'with \'...\'.' % flag.name,
|
| + token)
|
| +
|
| + if error_check.ShouldCheck(Rule.OPTIONAL_TYPE_MARKER):
|
| + # Check for optional marker in type.
|
| + if (flag.type.endswith('=') and
|
| + not flag.name.startswith('opt_')):
|
| + self._HandleError(errors.JSDOC_MISSING_OPTIONAL_PREFIX,
|
| + 'Optional parameter name %s must be prefixed '
|
| + 'with opt_.' % flag.name,
|
| + token)
|
| + elif (not flag.type.endswith('=') and
|
| + flag.name.startswith('opt_')):
|
| + self._HandleError(errors.JSDOC_MISSING_OPTIONAL_TYPE,
|
| + 'Optional parameter %s type must end with =.' %
|
| + flag.name,
|
| + token)
|
|
|
| if flag.flag_type in state.GetDocFlag().HAS_TYPE:
|
| # Check for both missing type token and empty type braces '{}'
|
| - # Missing suppress types are reported separately and we allow enums
|
| - # without types.
|
| - if (flag.flag_type not in ('suppress', 'enum') and
|
| + # Missing suppress types are reported separately and we allow enums,
|
| + # const, private, public and protected without types.
|
| + allowed_flags = set(['suppress']).union(
|
| + state.GetDocFlag().CAN_OMIT_TYPE)
|
| +
|
| + if (flag.flag_type not in allowed_flags and
|
| (not flag.type or flag.type.isspace())):
|
| self._HandleError(errors.MISSING_JSDOC_TAG_TYPE,
|
| 'Missing type in %s tag' % token.string, token)
|
| @@ -176,7 +210,7 @@ class JavaScriptLintRules(ecmalintrules.EcmaScriptLintRules):
|
| errors.UNNECESSARY_DOUBLE_QUOTED_STRING,
|
| 'Single-quoted string preferred over double-quoted string.',
|
| token,
|
| - Position.All(token.string))
|
| + position=Position.All(token.string))
|
|
|
| elif token.type == Type.END_DOC_COMMENT:
|
| doc_comment = state.GetDocComment()
|
| @@ -187,13 +221,19 @@ class JavaScriptLintRules(ecmalintrules.EcmaScriptLintRules):
|
| self._SetLimitedDocChecks(True)
|
|
|
| if (error_check.ShouldCheck(Rule.BLANK_LINES_AT_TOP_LEVEL) and
|
| - not self._is_html and state.InTopLevel() and not state.InBlock()):
|
| + not self._is_html and
|
| + state.InTopLevel() and
|
| + not state.InNonScopeBlock()):
|
|
|
| # Check if we're in a fileoverview or constructor JsDoc.
|
| is_constructor = (
|
| doc_comment.HasFlag('constructor') or
|
| doc_comment.HasFlag('interface'))
|
| - is_file_overview = doc_comment.HasFlag('fileoverview')
|
| + # @fileoverview is an optional tag so if the dosctring is the first
|
| + # token in the file treat it as a file level docstring.
|
| + is_file_level_comment = (
|
| + doc_comment.HasFlag('fileoverview') or
|
| + not doc_comment.start_token.previous)
|
|
|
| # If the comment is not a file overview, and it does not immediately
|
| # precede some code, skip it.
|
| @@ -201,7 +241,8 @@ class JavaScriptLintRules(ecmalintrules.EcmaScriptLintRules):
|
| # behavior at the top of a file.
|
| next_token = token.next
|
| if (not next_token or
|
| - (not is_file_overview and next_token.type in Type.NON_CODE_TYPES)):
|
| + (not is_file_level_comment and
|
| + next_token.type in Type.NON_CODE_TYPES)):
|
| return
|
|
|
| # Don't require extra blank lines around suppression of extra
|
| @@ -214,7 +255,7 @@ class JavaScriptLintRules(ecmalintrules.EcmaScriptLintRules):
|
| # Find the start of this block (include comments above the block, unless
|
| # this is a file overview).
|
| block_start = doc_comment.start_token
|
| - if not is_file_overview:
|
| + if not is_file_level_comment:
|
| token = block_start.previous
|
| while token and token.type in Type.COMMENT_TYPES:
|
| block_start = token
|
| @@ -236,22 +277,25 @@ class JavaScriptLintRules(ecmalintrules.EcmaScriptLintRules):
|
| error_message = False
|
| expected_blank_lines = 0
|
|
|
| - if is_file_overview and blank_lines == 0:
|
| + # Only need blank line before file overview if it is not the beginning
|
| + # of the file, e.g. copyright is first.
|
| + if is_file_level_comment and blank_lines == 0 and block_start.previous:
|
| error_message = 'Should have a blank line before a file overview.'
|
| expected_blank_lines = 1
|
| elif is_constructor and blank_lines != 3:
|
| error_message = (
|
| 'Should have 3 blank lines before a constructor/interface.')
|
| expected_blank_lines = 3
|
| - elif not is_file_overview and not is_constructor and blank_lines != 2:
|
| + elif (not is_file_level_comment and not is_constructor and
|
| + blank_lines != 2):
|
| error_message = 'Should have 2 blank lines between top-level blocks.'
|
| expected_blank_lines = 2
|
|
|
| if error_message:
|
| self._HandleError(
|
| errors.WRONG_BLANK_LINE_COUNT, error_message,
|
| - block_start, Position.AtBeginning(),
|
| - expected_blank_lines - blank_lines)
|
| + block_start, position=Position.AtBeginning(),
|
| + fix_data=expected_blank_lines - blank_lines)
|
|
|
| elif token.type == Type.END_BLOCK:
|
| if state.InFunction() and state.IsFunctionClose():
|
| @@ -269,7 +313,7 @@ class JavaScriptLintRules(ecmalintrules.EcmaScriptLintRules):
|
| self._HandleError(
|
| errors.MISSING_RETURN_DOCUMENTATION,
|
| 'Missing @return JsDoc in function with non-trivial return',
|
| - function.doc.end_token, Position.AtBeginning())
|
| + function.doc.end_token, position=Position.AtBeginning())
|
| elif (not function.has_return and
|
| not function.has_throw and
|
| function.doc and
|
| @@ -283,23 +327,63 @@ class JavaScriptLintRules(ecmalintrules.EcmaScriptLintRules):
|
| self._HandleError(
|
| errors.UNNECESSARY_RETURN_DOCUMENTATION,
|
| 'Found @return JsDoc on function that returns nothing',
|
| - return_flag.flag_token, Position.AtBeginning())
|
| + return_flag.flag_token, position=Position.AtBeginning())
|
| +
|
| + # b/4073735. Method in object literal definition of prototype can
|
| + # safely reference 'this'.
|
| + prototype_object_literal = False
|
| + block_start = None
|
| + previous_code = None
|
| + previous_previous_code = None
|
| +
|
| + # Search for cases where prototype is defined as object literal.
|
| + # previous_previous_code
|
| + # | previous_code
|
| + # | | block_start
|
| + # | | |
|
| + # a.b.prototype = {
|
| + # c : function() {
|
| + # this.d = 1;
|
| + # }
|
| + # }
|
| +
|
| + # If in object literal, find first token of block so to find previous
|
| + # tokens to check above condition.
|
| + if state.InObjectLiteral():
|
| + block_start = state.GetCurrentBlockStart()
|
| +
|
| + # If an object literal then get previous token (code type). For above
|
| + # case it should be '='.
|
| + if block_start:
|
| + previous_code = tokenutil.SearchExcept(block_start,
|
| + Type.NON_CODE_TYPES,
|
| + reverse=True)
|
| +
|
| + # If previous token to block is '=' then get its previous token.
|
| + if previous_code and previous_code.IsOperator('='):
|
| + previous_previous_code = tokenutil.SearchExcept(previous_code,
|
| + Type.NON_CODE_TYPES,
|
| + reverse=True)
|
| +
|
| + # If variable/token before '=' ends with '.prototype' then its above
|
| + # case of prototype defined with object literal.
|
| + prototype_object_literal = (previous_previous_code and
|
| + previous_previous_code.string.endswith(
|
| + '.prototype'))
|
|
|
| - if state.InFunction() and state.IsFunctionClose():
|
| - is_immediately_called = (token.next and
|
| - token.next.type == Type.START_PAREN)
|
| if (function.has_this and function.doc and
|
| not function.doc.HasFlag('this') and
|
| not function.is_constructor and
|
| not function.is_interface and
|
| - '.prototype.' not in function.name):
|
| + '.prototype.' not in function.name and
|
| + not prototype_object_literal):
|
| self._HandleError(
|
| errors.MISSING_JSDOC_TAG_THIS,
|
| 'Missing @this JsDoc in function referencing "this". ('
|
| 'this usually means you are trying to reference "this" in '
|
| 'a static function, or you have forgotten to mark a '
|
| 'constructor with @constructor)',
|
| - function.doc.end_token, Position.AtBeginning())
|
| + function.doc.end_token, position=Position.AtBeginning())
|
|
|
| elif token.type == Type.IDENTIFIER:
|
| if token.string == 'goog.inherits' and not state.InFunction():
|
| @@ -308,7 +392,7 @@ class JavaScriptLintRules(ecmalintrules.EcmaScriptLintRules):
|
| errors.MISSING_LINE,
|
| 'Missing newline between constructor and goog.inherits',
|
| token,
|
| - Position.AtBeginning())
|
| + position=Position.AtBeginning())
|
|
|
| extra_space = state.GetLastNonSpaceToken().next
|
| while extra_space != token:
|
| @@ -325,13 +409,23 @@ class JavaScriptLintRules(ecmalintrules.EcmaScriptLintRules):
|
| elif (token.string == 'goog.provide' and
|
| not state.InFunction() and
|
| namespaces_info is not None):
|
| - namespace = tokenutil.Search(token, Type.STRING_TEXT).string
|
| + namespace = tokenutil.GetStringAfterToken(token)
|
|
|
| # Report extra goog.provide statement.
|
| - if namespaces_info.IsExtraProvide(token):
|
| + if not namespace or namespaces_info.IsExtraProvide(token):
|
| + if not namespace:
|
| + msg = 'Empty namespace in goog.provide'
|
| + else:
|
| + msg = 'Unnecessary goog.provide: ' + namespace
|
| +
|
| + # Hint to user if this is a Test namespace.
|
| + if namespace.endswith('Test'):
|
| + msg += (' *Test namespaces must be mentioned in the '
|
| + 'goog.setTestOnly() call')
|
| +
|
| self._HandleError(
|
| errors.EXTRA_GOOG_PROVIDE,
|
| - 'Unnecessary goog.provide: ' + namespace,
|
| + msg,
|
| token, position=Position.AtBeginning())
|
|
|
| if namespaces_info.IsLastProvide(token):
|
| @@ -356,7 +450,7 @@ class JavaScriptLintRules(ecmalintrules.EcmaScriptLintRules):
|
| elif (token.string == 'goog.require' and
|
| not state.InFunction() and
|
| namespaces_info is not None):
|
| - namespace = tokenutil.Search(token, Type.STRING_TEXT).string
|
| + namespace = tokenutil.GetStringAfterToken(token)
|
|
|
| # If there are no provide statements, missing provides should be
|
| # reported before the first require.
|
| @@ -370,10 +464,15 @@ class JavaScriptLintRules(ecmalintrules.EcmaScriptLintRules):
|
| True)
|
|
|
| # Report extra goog.require statement.
|
| - if namespaces_info.IsExtraRequire(token):
|
| + if not namespace or namespaces_info.IsExtraRequire(token):
|
| + if not namespace:
|
| + msg = 'Empty namespace in goog.require'
|
| + else:
|
| + msg = 'Unnecessary goog.require: ' + namespace
|
| +
|
| self._HandleError(
|
| errors.EXTRA_GOOG_REQUIRE,
|
| - 'Unnecessary goog.require: ' + namespace,
|
| + msg,
|
| token, position=Position.AtBeginning())
|
|
|
| # Report missing goog.require statements.
|
| @@ -395,14 +494,14 @@ class JavaScriptLintRules(ecmalintrules.EcmaScriptLintRules):
|
| if (not token.metadata.IsUnaryOperator() and not last_in_line
|
| and not token.next.IsComment()
|
| and not token.next.IsOperator(',')
|
| - and not token.next.type in (Type.WHITESPACE, Type.END_PAREN,
|
| + and token.next.type not in (Type.WHITESPACE, Type.END_PAREN,
|
| Type.END_BRACKET, Type.SEMICOLON,
|
| Type.START_BRACKET)):
|
| self._HandleError(
|
| errors.MISSING_SPACE,
|
| 'Missing space after "%s"' % token.string,
|
| token,
|
| - Position.AtEnd(token.string))
|
| + position=Position.AtEnd(token.string))
|
| elif token.type == Type.WHITESPACE:
|
| first_in_line = token.IsFirstInLine()
|
| last_in_line = token.IsLastInLine()
|
| @@ -417,52 +516,152 @@ class JavaScriptLintRules(ecmalintrules.EcmaScriptLintRules):
|
| errors.EXTRA_SPACE,
|
| 'Extra space after "%s"' % token.previous.string,
|
| token,
|
| - Position.All(token.string))
|
| + position=Position.All(token.string))
|
| + elif token.type == Type.SEMICOLON:
|
| + previous_token = tokenutil.SearchExcept(token, Type.NON_CODE_TYPES,
|
| + reverse=True)
|
| + if not previous_token:
|
| + self._HandleError(
|
| + errors.REDUNDANT_SEMICOLON,
|
| + 'Semicolon without any statement',
|
| + token,
|
| + position=Position.AtEnd(token.string))
|
| + elif (previous_token.type == Type.KEYWORD and
|
| + previous_token.string not in ['break', 'continue', 'return']):
|
| + self._HandleError(
|
| + errors.REDUNDANT_SEMICOLON,
|
| + ('Semicolon after \'%s\' without any statement.'
|
| + ' Looks like an error.' % previous_token.string),
|
| + token,
|
| + position=Position.AtEnd(token.string))
|
| +
|
| + def _CheckUnusedLocalVariables(self, token, state):
|
| + """Checks for unused local variables in function blocks.
|
| +
|
| + Args:
|
| + token: The token to check.
|
| + state: The state tracker.
|
| + """
|
| + # We don't use state.InFunction because that disregards scope functions.
|
| + in_function = state.FunctionDepth() > 0
|
| + if token.type == Type.SIMPLE_LVALUE or token.type == Type.IDENTIFIER:
|
| + if in_function:
|
| + identifier = token.string
|
| + # Check whether the previous token was var.
|
| + previous_code_token = tokenutil.CustomSearch(
|
| + token,
|
| + lambda t: t.type not in Type.NON_CODE_TYPES,
|
| + reverse=True)
|
| + if previous_code_token and previous_code_token.IsKeyword('var'):
|
| + # Add local variable declaration to the top of the unused locals
|
| + # stack.
|
| + self._unused_local_variables_by_scope[-1][identifier] = token
|
| + elif token.type == Type.IDENTIFIER:
|
| + # This covers most cases where the variable is used as an identifier.
|
| + self._MarkLocalVariableUsed(token)
|
| + elif token.type == Type.SIMPLE_LVALUE and '.' in identifier:
|
| + # This covers cases where a value is assigned to a property of the
|
| + # variable.
|
| + self._MarkLocalVariableUsed(token)
|
| + elif token.type == Type.START_BLOCK:
|
| + if in_function and state.IsFunctionOpen():
|
| + # Push a new map onto the stack
|
| + self._unused_local_variables_by_scope.append({})
|
| + elif token.type == Type.END_BLOCK:
|
| + if state.IsFunctionClose():
|
| + # Pop the stack and report any remaining locals as unused.
|
| + unused_local_variables = self._unused_local_variables_by_scope.pop()
|
| + for unused_token in unused_local_variables.values():
|
| + self._HandleError(
|
| + errors.UNUSED_LOCAL_VARIABLE,
|
| + 'Unused local variable: %s.' % unused_token.string,
|
| + unused_token)
|
| +
|
| + def _MarkLocalVariableUsed(self, token):
|
| + """Marks the local variable as used in the relevant scope.
|
| +
|
| + Marks the local variable as used in the scope nearest to the current
|
| + scope that matches the given token.
|
| +
|
| + Args:
|
| + token: The token representing the potential usage of a local variable.
|
| + """
|
| +
|
| + identifier = token.string.split('.')[0]
|
| + # Find the first instance of the identifier in the stack of function scopes
|
| + # and mark it used.
|
| + for unused_local_variables in reversed(
|
| + self._unused_local_variables_by_scope):
|
| + if identifier in unused_local_variables:
|
| + del unused_local_variables[identifier]
|
| + break
|
|
|
| def _ReportMissingProvides(self, missing_provides, token, need_blank_line):
|
| """Reports missing provide statements to the error handler.
|
|
|
| Args:
|
| - missing_provides: A list of strings where each string is a namespace that
|
| - should be provided, but is not.
|
| + missing_provides: A dictionary of string(key) and integer(value) where
|
| + each string(key) is a namespace that should be provided, but is not
|
| + and integer(value) is first line number where it's required.
|
| token: The token where the error was detected (also where the new provides
|
| will be inserted.
|
| need_blank_line: Whether a blank line needs to be inserted after the new
|
| provides are inserted. May be True, False, or None, where None
|
| indicates that the insert location is unknown.
|
| """
|
| +
|
| + missing_provides_msg = 'Missing the following goog.provide statements:\n'
|
| + missing_provides_msg += '\n'.join(['goog.provide(\'%s\');' % x for x in
|
| + sorted(missing_provides)])
|
| + missing_provides_msg += '\n'
|
| +
|
| + missing_provides_msg += '\nFirst line where provided: \n'
|
| + missing_provides_msg += '\n'.join(
|
| + [' %s : line %d' % (x, missing_provides[x]) for x in
|
| + sorted(missing_provides)])
|
| + missing_provides_msg += '\n'
|
| +
|
| self._HandleError(
|
| errors.MISSING_GOOG_PROVIDE,
|
| - 'Missing the following goog.provide statements:\n' +
|
| - '\n'.join(map(lambda x: 'goog.provide(\'%s\');' % x,
|
| - sorted(missing_provides))),
|
| + missing_provides_msg,
|
| token, position=Position.AtBeginning(),
|
| - fix_data=(missing_provides, need_blank_line))
|
| + fix_data=(missing_provides.keys(), need_blank_line))
|
|
|
| def _ReportMissingRequires(self, missing_requires, token, need_blank_line):
|
| """Reports missing require statements to the error handler.
|
|
|
| Args:
|
| - missing_requires: A list of strings where each string is a namespace that
|
| - should be required, but is not.
|
| + missing_requires: A dictionary of string(key) and integer(value) where
|
| + each string(key) is a namespace that should be required, but is not
|
| + and integer(value) is first line number where it's required.
|
| token: The token where the error was detected (also where the new requires
|
| will be inserted.
|
| need_blank_line: Whether a blank line needs to be inserted before the new
|
| requires are inserted. May be True, False, or None, where None
|
| indicates that the insert location is unknown.
|
| """
|
| +
|
| + missing_requires_msg = 'Missing the following goog.require statements:\n'
|
| + missing_requires_msg += '\n'.join(['goog.require(\'%s\');' % x for x in
|
| + sorted(missing_requires)])
|
| + missing_requires_msg += '\n'
|
| +
|
| + missing_requires_msg += '\nFirst line where required: \n'
|
| + missing_requires_msg += '\n'.join(
|
| + [' %s : line %d' % (x, missing_requires[x]) for x in
|
| + sorted(missing_requires)])
|
| + missing_requires_msg += '\n'
|
| +
|
| self._HandleError(
|
| errors.MISSING_GOOG_REQUIRE,
|
| - 'Missing the following goog.require statements:\n' +
|
| - '\n'.join(map(lambda x: 'goog.require(\'%s\');' % x,
|
| - sorted(missing_requires))),
|
| + missing_requires_msg,
|
| token, position=Position.AtBeginning(),
|
| - fix_data=(missing_requires, need_blank_line))
|
| + fix_data=(missing_requires.keys(), need_blank_line))
|
|
|
| - def Finalize(self, state, tokenizer_mode):
|
| + def Finalize(self, state):
|
| """Perform all checks that need to occur after all lines are processed."""
|
| # Call the base class's Finalize function.
|
| - super(JavaScriptLintRules, self).Finalize(state, tokenizer_mode)
|
| + super(JavaScriptLintRules, self).Finalize(state)
|
|
|
| if error_check.ShouldCheck(Rule.UNUSED_PRIVATE_MEMBERS):
|
| # Report an error for any declared private member that was never used.
|
| @@ -477,8 +676,8 @@ class JavaScriptLintRules(ecmalintrules.EcmaScriptLintRules):
|
|
|
| # Clear state to prepare for the next file.
|
| self._declared_private_member_tokens = {}
|
| - self._declared_private_members = Set()
|
| - self._used_private_members = Set()
|
| + self._declared_private_members = set()
|
| + self._used_private_members = set()
|
|
|
| namespaces_info = self._namespaces_info
|
| if namespaces_info is not None:
|
| @@ -508,31 +707,37 @@ class JavaScriptLintRules(ecmalintrules.EcmaScriptLintRules):
|
| token: The first token in the token stream.
|
| """
|
| sorter = requireprovidesorter.RequireProvideSorter()
|
| - provides_result = sorter.CheckProvides(token)
|
| - if provides_result:
|
| + first_provide_token = sorter.CheckProvides(token)
|
| + if first_provide_token:
|
| + new_order = sorter.GetFixedProvideString(first_provide_token)
|
| self._HandleError(
|
| errors.GOOG_PROVIDES_NOT_ALPHABETIZED,
|
| 'goog.provide classes must be alphabetized. The correct code is:\n' +
|
| - '\n'.join(
|
| - map(lambda x: 'goog.provide(\'%s\');' % x, provides_result[1])),
|
| - provides_result[0],
|
| + new_order,
|
| + first_provide_token,
|
| position=Position.AtBeginning(),
|
| - fix_data=provides_result[0])
|
| + fix_data=first_provide_token)
|
|
|
| - requires_result = sorter.CheckRequires(token)
|
| - if requires_result:
|
| + first_require_token = sorter.CheckRequires(token)
|
| + if first_require_token:
|
| + new_order = sorter.GetFixedRequireString(first_require_token)
|
| self._HandleError(
|
| errors.GOOG_REQUIRES_NOT_ALPHABETIZED,
|
| 'goog.require classes must be alphabetized. The correct code is:\n' +
|
| - '\n'.join(
|
| - map(lambda x: 'goog.require(\'%s\');' % x, requires_result[1])),
|
| - requires_result[0],
|
| + new_order,
|
| + first_require_token,
|
| position=Position.AtBeginning(),
|
| - fix_data=requires_result[0])
|
| + fix_data=first_require_token)
|
|
|
| def GetLongLineExceptions(self):
|
| - """Gets a list of regexps for lines which can be longer than the limit."""
|
| + """Gets a list of regexps for lines which can be longer than the limit.
|
| +
|
| + Returns:
|
| + A list of regexps, used as matches (rather than searches).
|
| + """
|
| return [
|
| - re.compile('goog\.require\(.+\);?\s*$'),
|
| - re.compile('goog\.provide\(.+\);?\s*$')
|
| + re.compile(r'goog\.require\(.+\);?\s*$'),
|
| + re.compile(r'goog\.provide\(.+\);?\s*$'),
|
| + re.compile(r'goog\.setTestOnly\(.+\);?\s*$'),
|
| + re.compile(r'[\s/*]*@visibility\s*{.*}[\s*/]*$'),
|
| ]
|
|
|