15821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#!/usr/bin/env python
25821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# Copyright 2011 The Closure Linter Authors. All Rights Reserved.
35821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#
45821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# Licensed under the Apache License, Version 2.0 (the "License");
55821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# you may not use this file except in compliance with the License.
65821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# You may obtain a copy of the License at
75821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#
85821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#      http://www.apache.org/licenses/LICENSE-2.0
95821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#
105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# Unless required by applicable law or agreed to in writing, software
115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# distributed under the License is distributed on an "AS-IS" BASIS,
125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# See the License for the specific language governing permissions and
145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# limitations under the License.
155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)"""Methods for checking JS files for common style guide violations.
175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)These style guide violations should only apply to JavaScript and not an Ecma
195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)scripting languages.
205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)"""
215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)__author__ = ('robbyw@google.com (Robert Walker)',
235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)              'ajp@google.com (Andy Perelson)',
245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)              'jacobr@google.com (Jacob Richman)')
255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)import re
275f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)
285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)from closure_linter import ecmalintrules
295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)from closure_linter import error_check
305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)from closure_linter import errors
315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)from closure_linter import javascripttokenizer
325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)from closure_linter import javascripttokens
335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)from closure_linter import requireprovidesorter
345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)from closure_linter import tokenutil
355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)from closure_linter.common import error
365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)from closure_linter.common import position
375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# Shorthand
395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)Error = error.Error
405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)Position = position.Position
415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)Rule = error_check.Rule
425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)Type = javascripttokens.JavaScriptTokenType
435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)class JavaScriptLintRules(ecmalintrules.EcmaScriptLintRules):
465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  """JavaScript lint rules that catch JavaScript specific style errors."""
475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def __init__(self, namespaces_info):
495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    """Initializes a JavaScriptLintRules instance."""
505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    ecmalintrules.EcmaScriptLintRules.__init__(self)
515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self._namespaces_info = namespaces_info
525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self._declared_private_member_tokens = {}
535f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    self._declared_private_members = set()
545f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    self._used_private_members = set()
555f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    # A stack of dictionaries, one for each function scope entered. Each
565f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    # dictionary is keyed by an identifier that defines a local variable and has
575f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    # a token as its value.
585f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    self._unused_local_variables_by_scope = []
595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def HandleMissingParameterDoc(self, token, param_name):
615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    """Handle errors associated with a parameter missing a param tag."""
625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self._HandleError(errors.MISSING_PARAMETER_DOCUMENTATION,
635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                      'Missing docs for parameter: "%s"' % param_name, token)
645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def __ContainsRecordType(self, token):
665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    """Check whether the given token contains a record type.
675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    Args:
695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      token: The token being checked
705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    Returns:
725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      True if the token contains a record type, False otherwise.
735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    """
745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # If we see more than one left-brace in the string of an annotation token,
755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # then there's a record type in there.
765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return (
775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        token and token.type == Type.DOC_FLAG and
785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        token.attached_object.type is not None and
795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        token.attached_object.type.find('{') != token.string.rfind('{'))
805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
815f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  # pylint: disable=too-many-statements
825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def CheckToken(self, token, state):
835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    """Checks a token, given the current parser_state, for warnings and errors.
845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    Args:
865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      token: The current token under consideration
875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      state: parser_state object that indicates the current state in the page
885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    """
895f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)
905f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    # For @param don't ignore record type.
915f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    if (self.__ContainsRecordType(token) and
925f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)        token.attached_object.flag_type != 'param'):
935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      # We should bail out and not emit any warnings for this annotation.
945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      # TODO(nicksantos): Support record types for real.
955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      state.GetDocComment().Invalidate()
965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return
975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # Call the base class's CheckToken function.
995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    super(JavaScriptLintRules, self).CheckToken(token, state)
1005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # Store some convenience variables
1025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    namespaces_info = self._namespaces_info
1035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1045f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    if error_check.ShouldCheck(Rule.UNUSED_LOCAL_VARIABLES):
1055f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)      self._CheckUnusedLocalVariables(token, state)
1065f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)
1075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if error_check.ShouldCheck(Rule.UNUSED_PRIVATE_MEMBERS):
1085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      # Find all assignments to private members.
1095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if token.type == Type.SIMPLE_LVALUE:
1105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        identifier = token.string
1115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        if identifier.endswith('_') and not identifier.endswith('__'):
1125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          doc_comment = state.GetDocComment()
1135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          suppressed = (doc_comment and doc_comment.HasFlag('suppress') and
1145f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)                        (doc_comment.GetFlag('suppress').type == 'underscore' or
1155f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)                         doc_comment.GetFlag('suppress').type ==
1165f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)                         'unusedPrivateMembers'))
1175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          if not suppressed:
1185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            # Look for static members defined on a provided namespace.
1195f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)            if namespaces_info:
1205f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)              namespace = namespaces_info.GetClosurizedNamespace(identifier)
1215f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)              provided_namespaces = namespaces_info.GetProvidedNamespaces()
1225f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)            else:
1235f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)              namespace = None
1245f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)              provided_namespaces = set()
1255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            # Skip cases of this.something_.somethingElse_.
1275f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)            regex = re.compile(r'^this\.[a-zA-Z_]+$')
1285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            if namespace in provided_namespaces or regex.match(identifier):
1295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)              variable = identifier.split('.')[-1]
1305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)              self._declared_private_member_tokens[variable] = token
1315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)              self._declared_private_members.add(variable)
1325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        elif not identifier.endswith('__'):
1335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          # Consider setting public members of private members to be a usage.
1345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          for piece in identifier.split('.'):
1355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            if piece.endswith('_'):
1365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)              self._used_private_members.add(piece)
1375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      # Find all usages of private members.
1395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if token.type == Type.IDENTIFIER:
1405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        for piece in token.string.split('.'):
1415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          if piece.endswith('_'):
1425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            self._used_private_members.add(piece)
1435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if token.type == Type.DOC_FLAG:
1455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      flag = token.attached_object
1465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if flag.flag_type == 'param' and flag.name_token is not None:
1485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        self._CheckForMissingSpaceBeforeToken(
1495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            token.attached_object.name_token)
1505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1515f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)        if flag.type is not None and flag.name is not None:
1525f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)          if error_check.ShouldCheck(Rule.VARIABLE_ARG_MARKER):
1535f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)            # Check for variable arguments marker in type.
1545f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)            if (flag.type.startswith('...') and
1555f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)                flag.name != 'var_args'):
1565f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)              self._HandleError(errors.JSDOC_MISSING_VAR_ARGS_NAME,
1575f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)                                'Variable length argument %s must be renamed '
1585f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)                                'to var_args.' % flag.name,
1595f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)                                token)
1605f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)            elif (not flag.type.startswith('...') and
1615f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)                  flag.name == 'var_args'):
1625f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)              self._HandleError(errors.JSDOC_MISSING_VAR_ARGS_TYPE,
1635f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)                                'Variable length argument %s type must start '
1645f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)                                'with \'...\'.' % flag.name,
1655f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)                                token)
1665f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)
1675f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)          if error_check.ShouldCheck(Rule.OPTIONAL_TYPE_MARKER):
1685f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)            # Check for optional marker in type.
1695f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)            if (flag.type.endswith('=') and
1705f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)                not flag.name.startswith('opt_')):
1715f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)              self._HandleError(errors.JSDOC_MISSING_OPTIONAL_PREFIX,
1725f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)                                'Optional parameter name %s must be prefixed '
1735f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)                                'with opt_.' % flag.name,
1745f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)                                token)
1755f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)            elif (not flag.type.endswith('=') and
1765f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)                  flag.name.startswith('opt_')):
1775f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)              self._HandleError(errors.JSDOC_MISSING_OPTIONAL_TYPE,
1785f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)                                'Optional parameter %s type must end with =.' %
1795f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)                                flag.name,
1805f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)                                token)
1815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if flag.flag_type in state.GetDocFlag().HAS_TYPE:
1835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        # Check for both missing type token and empty type braces '{}'
1845f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)        # Missing suppress types are reported separately and we allow enums,
1855f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)        # const, private, public and protected without types.
1865f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)        allowed_flags = set(['suppress']).union(
1875f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)            state.GetDocFlag().CAN_OMIT_TYPE)
1885f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)
1895f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)        if (flag.flag_type not in allowed_flags and
1905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            (not flag.type or flag.type.isspace())):
1915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          self._HandleError(errors.MISSING_JSDOC_TAG_TYPE,
1925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                            'Missing type in %s tag' % token.string, token)
1935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        elif flag.name_token and flag.type_end_token and tokenutil.Compare(
1955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            flag.type_end_token, flag.name_token) > 0:
1965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          self._HandleError(
1975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)              errors.OUT_OF_ORDER_JSDOC_TAG_TYPE,
1985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)              'Type should be immediately after %s tag' % token.string,
1995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)              token)
2005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    elif token.type == Type.DOUBLE_QUOTE_STRING_START:
2025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      next_token = token.next
2035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      while next_token.type == Type.STRING_TEXT:
2045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        if javascripttokenizer.JavaScriptTokenizer.SINGLE_QUOTE.search(
2055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            next_token.string):
2065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          break
2075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        next_token = next_token.next
2085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      else:
2095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        self._HandleError(
2105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            errors.UNNECESSARY_DOUBLE_QUOTED_STRING,
2115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            'Single-quoted string preferred over double-quoted string.',
2125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            token,
2135f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)            position=Position.All(token.string))
2145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    elif token.type == Type.END_DOC_COMMENT:
2165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      doc_comment = state.GetDocComment()
2175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      # When @externs appears in a @fileoverview comment, it should trigger
2195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      # the same limited doc checks as a special filename like externs.js.
2205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if doc_comment.HasFlag('fileoverview') and doc_comment.HasFlag('externs'):
2215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        self._SetLimitedDocChecks(True)
2225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (error_check.ShouldCheck(Rule.BLANK_LINES_AT_TOP_LEVEL) and
2245f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)          not self._is_html and
2255f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)          state.InTopLevel() and
2265f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)          not state.InNonScopeBlock()):
2275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        # Check if we're in a fileoverview or constructor JsDoc.
2295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        is_constructor = (
2305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            doc_comment.HasFlag('constructor') or
2315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            doc_comment.HasFlag('interface'))
2325f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)        # @fileoverview is an optional tag so if the dosctring is the first
2335f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)        # token in the file treat it as a file level docstring.
2345f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)        is_file_level_comment = (
2355f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)            doc_comment.HasFlag('fileoverview') or
2365f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)            not doc_comment.start_token.previous)
2375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        # If the comment is not a file overview, and it does not immediately
2395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        # precede some code, skip it.
2405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        # NOTE: The tokenutil methods are not used here because of their
2415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        # behavior at the top of a file.
2425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        next_token = token.next
2435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        if (not next_token or
2445f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)            (not is_file_level_comment and
2455f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)             next_token.type in Type.NON_CODE_TYPES)):
2465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          return
2475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        # Don't require extra blank lines around suppression of extra
2495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        # goog.require errors.
2505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        if (doc_comment.SuppressionOnly() and
2515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            next_token.type == Type.IDENTIFIER and
2525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            next_token.string in ['goog.provide', 'goog.require']):
2535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          return
2545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        # Find the start of this block (include comments above the block, unless
2565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        # this is a file overview).
2575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        block_start = doc_comment.start_token
2585f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)        if not is_file_level_comment:
2595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          token = block_start.previous
2605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          while token and token.type in Type.COMMENT_TYPES:
2615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            block_start = token
2625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            token = token.previous
2635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        # Count the number of blank lines before this block.
2655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        blank_lines = 0
2665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        token = block_start.previous
2675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        while token and token.type in [Type.WHITESPACE, Type.BLANK_LINE]:
2685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          if token.type == Type.BLANK_LINE:
2695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            # A blank line.
2705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            blank_lines += 1
2715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          elif token.type == Type.WHITESPACE and not token.line.strip():
2725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            # A line with only whitespace on it.
2735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            blank_lines += 1
2745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          token = token.previous
2755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        # Log errors.
2775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        error_message = False
2785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        expected_blank_lines = 0
2795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2805f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)        # Only need blank line before file overview if it is not the beginning
2815f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)        # of the file, e.g. copyright is first.
2825f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)        if is_file_level_comment and blank_lines == 0 and block_start.previous:
2835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          error_message = 'Should have a blank line before a file overview.'
2845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          expected_blank_lines = 1
2855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        elif is_constructor and blank_lines != 3:
2865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          error_message = (
2875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)              'Should have 3 blank lines before a constructor/interface.')
2885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          expected_blank_lines = 3
2895f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)        elif (not is_file_level_comment and not is_constructor and
2905f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)              blank_lines != 2):
2915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          error_message = 'Should have 2 blank lines between top-level blocks.'
2925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          expected_blank_lines = 2
2935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        if error_message:
2955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          self._HandleError(
2965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)              errors.WRONG_BLANK_LINE_COUNT, error_message,
2975f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)              block_start, position=Position.AtBeginning(),
2985f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)              fix_data=expected_blank_lines - blank_lines)
2995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    elif token.type == Type.END_BLOCK:
3015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if state.InFunction() and state.IsFunctionClose():
3025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        is_immediately_called = (token.next and
3035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                 token.next.type == Type.START_PAREN)
3045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        function = state.GetFunction()
3065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        if not self._limited_doc_checks:
3075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          if (function.has_return and function.doc and
3085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)              not is_immediately_called and
3095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)              not function.doc.HasFlag('return') and
3105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)              not function.doc.InheritsDocumentation() and
3115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)              not function.doc.HasFlag('constructor')):
3125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            # Check for proper documentation of return value.
3135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            self._HandleError(
3145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                errors.MISSING_RETURN_DOCUMENTATION,
3155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                'Missing @return JsDoc in function with non-trivial return',
3165f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)                function.doc.end_token, position=Position.AtBeginning())
3175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          elif (not function.has_return and
3185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                not function.has_throw and
3195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                function.doc and
3205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                function.doc.HasFlag('return') and
3215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                not state.InInterfaceMethod()):
3225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            return_flag = function.doc.GetFlag('return')
3235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            if (return_flag.type is None or (
3245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                'undefined' not in return_flag.type and
3255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                'void' not in return_flag.type and
3265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                '*' not in return_flag.type)):
3275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)              self._HandleError(
3285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                  errors.UNNECESSARY_RETURN_DOCUMENTATION,
3295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                  'Found @return JsDoc on function that returns nothing',
3305f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)                  return_flag.flag_token, position=Position.AtBeginning())
3315f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)
3325f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)        # b/4073735. Method in object literal definition of prototype can
3335f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)        # safely reference 'this'.
3345f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)        prototype_object_literal = False
3355f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)        block_start = None
3365f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)        previous_code = None
3375f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)        previous_previous_code = None
3385f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)
3395f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)        # Search for cases where prototype is defined as object literal.
3405f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)        #       previous_previous_code
3415f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)        #       |       previous_code
3425f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)        #       |       | block_start
3435f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)        #       |       | |
3445f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)        # a.b.prototype = {
3455f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)        #   c : function() {
3465f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)        #     this.d = 1;
3475f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)        #   }
3485f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)        # }
3495f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)
3505f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)        # If in object literal, find first token of block so to find previous
3515f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)        # tokens to check above condition.
3525f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)        if state.InObjectLiteral():
3535f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)          block_start = state.GetCurrentBlockStart()
3545f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)
3555f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)        # If an object literal then get previous token (code type). For above
3565f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)        # case it should be '='.
3575f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)        if block_start:
3585f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)          previous_code = tokenutil.SearchExcept(block_start,
3595f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)                                                 Type.NON_CODE_TYPES,
3605f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)                                                 reverse=True)
3615f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)
3625f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)        # If previous token to block is '=' then get its previous token.
3635f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)        if previous_code and previous_code.IsOperator('='):
3645f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)          previous_previous_code = tokenutil.SearchExcept(previous_code,
3655f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)                                                          Type.NON_CODE_TYPES,
3665f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)                                                          reverse=True)
3675f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)
3685f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)        # If variable/token before '=' ends with '.prototype' then its above
3695f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)        # case of prototype defined with object literal.
3705f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)        prototype_object_literal = (previous_previous_code and
3715f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)                                    previous_previous_code.string.endswith(
3725f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)                                        '.prototype'))
3735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        if (function.has_this and function.doc and
3755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            not function.doc.HasFlag('this') and
3765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            not function.is_constructor and
3775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            not function.is_interface and
3785f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)            '.prototype.' not in function.name and
3795f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)            not prototype_object_literal):
3805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          self._HandleError(
3815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)              errors.MISSING_JSDOC_TAG_THIS,
3825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)              'Missing @this JsDoc in function referencing "this". ('
3835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)              'this usually means you are trying to reference "this" in '
3845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)              'a static function, or you have forgotten to mark a '
3855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)              'constructor with @constructor)',
3865f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)              function.doc.end_token, position=Position.AtBeginning())
3875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    elif token.type == Type.IDENTIFIER:
3895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if token.string == 'goog.inherits' and not state.InFunction():
3905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        if state.GetLastNonSpaceToken().line_number == token.line_number:
3915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          self._HandleError(
3925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)              errors.MISSING_LINE,
3935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)              'Missing newline between constructor and goog.inherits',
3945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)              token,
3955f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)              position=Position.AtBeginning())
3965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        extra_space = state.GetLastNonSpaceToken().next
3985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        while extra_space != token:
3995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          if extra_space.type == Type.BLANK_LINE:
4005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            self._HandleError(
4015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                errors.EXTRA_LINE,
4025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                'Extra line between constructor and goog.inherits',
4035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                extra_space)
4045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          extra_space = extra_space.next
4055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        # TODO(robbyw): Test the last function was a constructor.
4075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        # TODO(robbyw): Test correct @extends and @implements documentation.
4085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      elif (token.string == 'goog.provide' and
4105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            not state.InFunction() and
4115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            namespaces_info is not None):
4125f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)        namespace = tokenutil.GetStringAfterToken(token)
4135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        # Report extra goog.provide statement.
4155f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)        if not namespace or namespaces_info.IsExtraProvide(token):
4165f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)          if not namespace:
4175f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)            msg = 'Empty namespace in goog.provide'
4185f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)          else:
4195f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)            msg = 'Unnecessary goog.provide: ' +  namespace
4205f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)
4215f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)            # Hint to user if this is a Test namespace.
4225f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)            if namespace.endswith('Test'):
4235f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)              msg += (' *Test namespaces must be mentioned in the '
4245f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)                      'goog.setTestOnly() call')
4255f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)
4265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          self._HandleError(
4275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)              errors.EXTRA_GOOG_PROVIDE,
4285f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)              msg,
4295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)              token, position=Position.AtBeginning())
4305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        if namespaces_info.IsLastProvide(token):
4325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          # Report missing provide statements after the last existing provide.
4335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          missing_provides = namespaces_info.GetMissingProvides()
4345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          if missing_provides:
4355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            self._ReportMissingProvides(
4365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                missing_provides,
4375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                tokenutil.GetLastTokenInSameLine(token).next,
4385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                False)
4395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          # If there are no require statements, missing requires should be
4415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          # reported after the last provide.
4425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          if not namespaces_info.GetRequiredNamespaces():
4435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            missing_requires = namespaces_info.GetMissingRequires()
4445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            if missing_requires:
4455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)              self._ReportMissingRequires(
4465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                  missing_requires,
4475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                  tokenutil.GetLastTokenInSameLine(token).next,
4485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                  True)
4495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      elif (token.string == 'goog.require' and
4515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            not state.InFunction() and
4525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            namespaces_info is not None):
4535f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)        namespace = tokenutil.GetStringAfterToken(token)
4545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        # If there are no provide statements, missing provides should be
4565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        # reported before the first require.
4575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        if (namespaces_info.IsFirstRequire(token) and
4585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            not namespaces_info.GetProvidedNamespaces()):
4595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          missing_provides = namespaces_info.GetMissingProvides()
4605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          if missing_provides:
4615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            self._ReportMissingProvides(
4625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                missing_provides,
4635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                tokenutil.GetFirstTokenInSameLine(token),
4645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                True)
4655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        # Report extra goog.require statement.
4675f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)        if not namespace or namespaces_info.IsExtraRequire(token):
4685f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)          if not namespace:
4695f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)            msg = 'Empty namespace in goog.require'
4705f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)          else:
4715f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)            msg = 'Unnecessary goog.require: ' + namespace
4725f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)
4735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          self._HandleError(
4745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)              errors.EXTRA_GOOG_REQUIRE,
4755f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)              msg,
4765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)              token, position=Position.AtBeginning())
4775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        # Report missing goog.require statements.
4795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        if namespaces_info.IsLastRequire(token):
4805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          missing_requires = namespaces_info.GetMissingRequires()
4815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          if missing_requires:
4825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            self._ReportMissingRequires(
4835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                missing_requires,
4845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                tokenutil.GetLastTokenInSameLine(token).next,
4855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                False)
4865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    elif token.type == Type.OPERATOR:
4885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      last_in_line = token.IsLastInLine()
4895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      # If the token is unary and appears to be used in a unary context
4905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      # it's ok.  Otherwise, if it's at the end of the line or immediately
4915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      # before a comment, it's ok.
4925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      # Don't report an error before a start bracket - it will be reported
4935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      # by that token's space checks.
4945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (not token.metadata.IsUnaryOperator() and not last_in_line
4955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          and not token.next.IsComment()
4965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          and not token.next.IsOperator(',')
4975f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)          and token.next.type not in (Type.WHITESPACE, Type.END_PAREN,
4985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                      Type.END_BRACKET, Type.SEMICOLON,
4995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                      Type.START_BRACKET)):
5005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        self._HandleError(
5015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            errors.MISSING_SPACE,
5025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            'Missing space after "%s"' % token.string,
5035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            token,
5045f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)            position=Position.AtEnd(token.string))
5055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    elif token.type == Type.WHITESPACE:
5065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      first_in_line = token.IsFirstInLine()
5075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      last_in_line = token.IsLastInLine()
5085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      # Check whitespace length if it's not the first token of the line and
5095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      # if it's not immediately before a comment.
5105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if not last_in_line and not first_in_line and not token.next.IsComment():
5115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        # Ensure there is no space after opening parentheses.
5125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        if (token.previous.type in (Type.START_PAREN, Type.START_BRACKET,
5135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                    Type.FUNCTION_NAME)
5145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            or token.next.type == Type.START_PARAMETERS):
5155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          self._HandleError(
5165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)              errors.EXTRA_SPACE,
5175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)              'Extra space after "%s"' % token.previous.string,
5185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)              token,
5195f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)              position=Position.All(token.string))
5205f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    elif token.type == Type.SEMICOLON:
5215f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)      previous_token = tokenutil.SearchExcept(token, Type.NON_CODE_TYPES,
5225f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)                                              reverse=True)
5235f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)      if not previous_token:
5245f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)        self._HandleError(
5255f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)            errors.REDUNDANT_SEMICOLON,
5265f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)            'Semicolon without any statement',
5275f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)            token,
5285f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)            position=Position.AtEnd(token.string))
5295f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)      elif (previous_token.type == Type.KEYWORD and
5305f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)            previous_token.string not in ['break', 'continue', 'return']):
5315f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)        self._HandleError(
5325f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)            errors.REDUNDANT_SEMICOLON,
5335f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)            ('Semicolon after \'%s\' without any statement.'
5345f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)             ' Looks like an error.' % previous_token.string),
5355f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)            token,
5365f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)            position=Position.AtEnd(token.string))
5375f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)
5385f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  def _CheckUnusedLocalVariables(self, token, state):
5395f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    """Checks for unused local variables in function blocks.
5405f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)
5415f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    Args:
5425f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)      token: The token to check.
5435f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)      state: The state tracker.
5445f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    """
5455f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    # We don't use state.InFunction because that disregards scope functions.
5465f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    in_function = state.FunctionDepth() > 0
5475f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    if token.type == Type.SIMPLE_LVALUE or token.type == Type.IDENTIFIER:
5485f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)      if in_function:
5495f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)        identifier = token.string
5505f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)        # Check whether the previous token was var.
5515f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)        previous_code_token = tokenutil.CustomSearch(
5525f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)            token,
5535f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)            lambda t: t.type not in Type.NON_CODE_TYPES,
5545f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)            reverse=True)
5555f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)        if previous_code_token and previous_code_token.IsKeyword('var'):
5565f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)          # Add local variable declaration to the top of the unused locals
5575f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)          # stack.
5585f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)          self._unused_local_variables_by_scope[-1][identifier] = token
5595f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)        elif token.type == Type.IDENTIFIER:
5605f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)          # This covers most cases where the variable is used as an identifier.
5615f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)          self._MarkLocalVariableUsed(token)
5625f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)        elif token.type == Type.SIMPLE_LVALUE and '.' in identifier:
5635f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)          # This covers cases where a value is assigned to a property of the
5645f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)          # variable.
5655f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)          self._MarkLocalVariableUsed(token)
5665f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    elif token.type == Type.START_BLOCK:
5675f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)      if in_function and state.IsFunctionOpen():
5685f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)        # Push a new map onto the stack
5695f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)        self._unused_local_variables_by_scope.append({})
5705f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    elif token.type == Type.END_BLOCK:
5715f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)      if state.IsFunctionClose():
5725f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)        # Pop the stack and report any remaining locals as unused.
5735f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)        unused_local_variables = self._unused_local_variables_by_scope.pop()
5745f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)        for unused_token in unused_local_variables.values():
5755f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)          self._HandleError(
5765f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)              errors.UNUSED_LOCAL_VARIABLE,
5775f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)              'Unused local variable: %s.' % unused_token.string,
5785f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)              unused_token)
5795f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)
5805f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  def _MarkLocalVariableUsed(self, token):
5815f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    """Marks the local variable as used in the relevant scope.
5825f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)
5835f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    Marks the local variable as used in the scope nearest to the current
5845f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    scope that matches the given token.
5855f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)
5865f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    Args:
5875f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)      token: The token representing the potential usage of a local variable.
5885f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    """
5895f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)
5905f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    identifier = token.string.split('.')[0]
5915f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    # Find the first instance of the identifier in the stack of function scopes
5925f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    # and mark it used.
5935f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    for unused_local_variables in reversed(
5945f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)        self._unused_local_variables_by_scope):
5955f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)      if identifier in unused_local_variables:
5965f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)        del unused_local_variables[identifier]
5975f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)        break
5985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def _ReportMissingProvides(self, missing_provides, token, need_blank_line):
6005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    """Reports missing provide statements to the error handler.
6015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    Args:
6035f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)      missing_provides: A dictionary of string(key) and integer(value) where
6045f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)          each string(key) is a namespace that should be provided, but is not
6055f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)          and integer(value) is first line number where it's required.
6065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      token: The token where the error was detected (also where the new provides
6075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          will be inserted.
6085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      need_blank_line: Whether a blank line needs to be inserted after the new
6095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          provides are inserted. May be True, False, or None, where None
6105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          indicates that the insert location is unknown.
6115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    """
6125f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)
6135f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    missing_provides_msg = 'Missing the following goog.provide statements:\n'
6145f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    missing_provides_msg += '\n'.join(['goog.provide(\'%s\');' % x for x in
6155f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)                                       sorted(missing_provides)])
6165f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    missing_provides_msg += '\n'
6175f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)
6185f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    missing_provides_msg += '\nFirst line where provided: \n'
6195f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    missing_provides_msg += '\n'.join(
6205f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)        ['  %s : line %d' % (x, missing_provides[x]) for x in
6215f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)         sorted(missing_provides)])
6225f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    missing_provides_msg += '\n'
6235f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)
6245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self._HandleError(
6255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        errors.MISSING_GOOG_PROVIDE,
6265f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)        missing_provides_msg,
6275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        token, position=Position.AtBeginning(),
6285f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)        fix_data=(missing_provides.keys(), need_blank_line))
6295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def _ReportMissingRequires(self, missing_requires, token, need_blank_line):
6315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    """Reports missing require statements to the error handler.
6325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    Args:
6345f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)      missing_requires: A dictionary of string(key) and integer(value) where
6355f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)          each string(key) is a namespace that should be required, but is not
6365f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)          and integer(value) is first line number where it's required.
6375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      token: The token where the error was detected (also where the new requires
6385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          will be inserted.
6395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      need_blank_line: Whether a blank line needs to be inserted before the new
6405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          requires are inserted. May be True, False, or None, where None
6415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          indicates that the insert location is unknown.
6425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    """
6435f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)
6445f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    missing_requires_msg = 'Missing the following goog.require statements:\n'
6455f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    missing_requires_msg += '\n'.join(['goog.require(\'%s\');' % x for x in
6465f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)                                       sorted(missing_requires)])
6475f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    missing_requires_msg += '\n'
6485f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)
6495f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    missing_requires_msg += '\nFirst line where required: \n'
6505f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    missing_requires_msg += '\n'.join(
6515f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)        ['  %s : line %d' % (x, missing_requires[x]) for x in
6525f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)         sorted(missing_requires)])
6535f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    missing_requires_msg += '\n'
6545f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)
6555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self._HandleError(
6565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        errors.MISSING_GOOG_REQUIRE,
6575f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)        missing_requires_msg,
6585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        token, position=Position.AtBeginning(),
6595f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)        fix_data=(missing_requires.keys(), need_blank_line))
6605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6615f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  def Finalize(self, state):
6625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    """Perform all checks that need to occur after all lines are processed."""
6635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # Call the base class's Finalize function.
6645f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    super(JavaScriptLintRules, self).Finalize(state)
6655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if error_check.ShouldCheck(Rule.UNUSED_PRIVATE_MEMBERS):
6675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      # Report an error for any declared private member that was never used.
6685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      unused_private_members = (self._declared_private_members -
6695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                self._used_private_members)
6705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      for variable in unused_private_members:
6725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        token = self._declared_private_member_tokens[variable]
6735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        self._HandleError(errors.UNUSED_PRIVATE_MEMBER,
6745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                          'Unused private member: %s.' % token.string,
6755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                          token)
6765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      # Clear state to prepare for the next file.
6785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      self._declared_private_member_tokens = {}
6795f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)      self._declared_private_members = set()
6805f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)      self._used_private_members = set()
6815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    namespaces_info = self._namespaces_info
6835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if namespaces_info is not None:
6845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      # If there are no provide or require statements, missing provides and
6855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      # requires should be reported on line 1.
6865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (not namespaces_info.GetProvidedNamespaces() and
6875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          not namespaces_info.GetRequiredNamespaces()):
6885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        missing_provides = namespaces_info.GetMissingProvides()
6895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        if missing_provides:
6905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          self._ReportMissingProvides(
6915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)              missing_provides, state.GetFirstToken(), None)
6925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        missing_requires = namespaces_info.GetMissingRequires()
6945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        if missing_requires:
6955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          self._ReportMissingRequires(
6965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)              missing_requires, state.GetFirstToken(), None)
6975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self._CheckSortedRequiresProvides(state.GetFirstToken())
6995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
7005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def _CheckSortedRequiresProvides(self, token):
7015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    """Checks that all goog.require and goog.provide statements are sorted.
7025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
7035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    Note that this method needs to be run after missing statements are added to
7045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    preserve alphabetical order.
7055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
7065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    Args:
7075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      token: The first token in the token stream.
7085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    """
7095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    sorter = requireprovidesorter.RequireProvideSorter()
7105f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    first_provide_token = sorter.CheckProvides(token)
7115f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    if first_provide_token:
7125f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)      new_order = sorter.GetFixedProvideString(first_provide_token)
7135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      self._HandleError(
7145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          errors.GOOG_PROVIDES_NOT_ALPHABETIZED,
7155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          'goog.provide classes must be alphabetized.  The correct code is:\n' +
7165f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)          new_order,
7175f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)          first_provide_token,
7185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          position=Position.AtBeginning(),
7195f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)          fix_data=first_provide_token)
7205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
7215f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    first_require_token = sorter.CheckRequires(token)
7225f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    if first_require_token:
7235f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)      new_order = sorter.GetFixedRequireString(first_require_token)
7245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      self._HandleError(
7255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          errors.GOOG_REQUIRES_NOT_ALPHABETIZED,
7265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          'goog.require classes must be alphabetized.  The correct code is:\n' +
7275f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)          new_order,
7285f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)          first_require_token,
7295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          position=Position.AtBeginning(),
7305f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)          fix_data=first_require_token)
7315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
7325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def GetLongLineExceptions(self):
7335f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    """Gets a list of regexps for lines which can be longer than the limit.
7345f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)
7355f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    Returns:
7365f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)      A list of regexps, used as matches (rather than searches).
7375f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    """
7385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return [
7395f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)        re.compile(r'goog\.require\(.+\);?\s*$'),
7405f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)        re.compile(r'goog\.provide\(.+\);?\s*$'),
7415f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)        re.compile(r'goog\.setTestOnly\(.+\);?\s*$'),
7425f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)        re.compile(r'[\s/*]*@visibility\s*{.*}[\s*/]*$'),
7435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        ]
744