12da489cd246702bee5938545b18a6f710ed214bcJamie Gennis#!/usr/bin/env python
22da489cd246702bee5938545b18a6f710ed214bcJamie Gennis#
32da489cd246702bee5938545b18a6f710ed214bcJamie Gennis# Copyright 2007 The Closure Linter Authors. All Rights Reserved.
42da489cd246702bee5938545b18a6f710ed214bcJamie Gennis#
52da489cd246702bee5938545b18a6f710ed214bcJamie Gennis# Licensed under the Apache License, Version 2.0 (the "License");
62da489cd246702bee5938545b18a6f710ed214bcJamie Gennis# you may not use this file except in compliance with the License.
72da489cd246702bee5938545b18a6f710ed214bcJamie Gennis# You may obtain a copy of the License at
82da489cd246702bee5938545b18a6f710ed214bcJamie Gennis#
92da489cd246702bee5938545b18a6f710ed214bcJamie Gennis#      http://www.apache.org/licenses/LICENSE-2.0
102da489cd246702bee5938545b18a6f710ed214bcJamie Gennis#
112da489cd246702bee5938545b18a6f710ed214bcJamie Gennis# Unless required by applicable law or agreed to in writing, software
122da489cd246702bee5938545b18a6f710ed214bcJamie Gennis# distributed under the License is distributed on an "AS-IS" BASIS,
132da489cd246702bee5938545b18a6f710ed214bcJamie Gennis# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
142da489cd246702bee5938545b18a6f710ed214bcJamie Gennis# See the License for the specific language governing permissions and
152da489cd246702bee5938545b18a6f710ed214bcJamie Gennis# limitations under the License.
162da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
172da489cd246702bee5938545b18a6f710ed214bcJamie Gennis"""Light weight EcmaScript state tracker that reads tokens and tracks state."""
182da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
192da489cd246702bee5938545b18a6f710ed214bcJamie Gennis__author__ = ('robbyw@google.com (Robert Walker)',
202da489cd246702bee5938545b18a6f710ed214bcJamie Gennis              'ajp@google.com (Andy Perelson)')
212da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
222da489cd246702bee5938545b18a6f710ed214bcJamie Gennisimport re
232da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
242da489cd246702bee5938545b18a6f710ed214bcJamie Gennisfrom closure_linter import javascripttokenizer
252da489cd246702bee5938545b18a6f710ed214bcJamie Gennisfrom closure_linter import javascripttokens
262da489cd246702bee5938545b18a6f710ed214bcJamie Gennisfrom closure_linter import tokenutil
272da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
282da489cd246702bee5938545b18a6f710ed214bcJamie Gennis# Shorthand
292da489cd246702bee5938545b18a6f710ed214bcJamie GennisType = javascripttokens.JavaScriptTokenType
302da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
312da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
322da489cd246702bee5938545b18a6f710ed214bcJamie Gennisclass DocFlag(object):
332da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  """Generic doc flag object.
342da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
352da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  Attribute:
362da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    flag_type: param, return, define, type, etc.
372da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    flag_token: The flag token.
382da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    type_start_token: The first token specifying the flag type,
392da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      including braces.
402da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    type_end_token: The last token specifying the flag type,
412da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      including braces.
422da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    type: The type spec.
432da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    name_token: The token specifying the flag name.
442da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    name: The flag name
452da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    description_start_token: The first token in the description.
462da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    description_end_token: The end token in the description.
472da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    description: The description.
482da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  """
492da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
502da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  # Please keep these lists alphabetized.
512da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
522da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  # The list of standard jsdoc tags is from
532da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  STANDARD_DOC = frozenset([
542da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      'author',
552da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      'bug',
562da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      'const',
572da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      'constructor',
582da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      'define',
592da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      'deprecated',
602da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      'enum',
612da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      'export',
622da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      'extends',
632da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      'externs',
642da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      'fileoverview',
652da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      'implements',
662da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      'implicitCast',
672da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      'interface',
682da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      'lends',
692da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      'license',
702da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      'noalias',
712da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      'nocompile',
722da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      'nosideeffects',
732da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      'override',
742da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      'owner',
752da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      'param',
762da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      'preserve',
772da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      'private',
782da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      'return',
792da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      'see',
802da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      'supported',
812da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      'template',
822da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      'this',
832da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      'type',
842da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      'typedef',
852da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      ])
862da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
872da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  ANNOTATION = frozenset(['preserveTry', 'suppress'])
882da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
892da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  LEGAL_DOC = STANDARD_DOC | ANNOTATION
902da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
912da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  # Includes all Closure Compiler @suppress types.
922da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  # Not all of these annotations are interpreted by Closure Linter.
932da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  #
942da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  # Specific cases:
952da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  # - accessControls is supported by the compiler at the expression
962da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  #   and method level to suppress warnings about private/protected
972da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  #   access (method level applies to all references in the method).
982da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  #   The linter mimics the compiler behavior.
992da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  SUPPRESS_TYPES = frozenset([
1002da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      'accessControls',
1012da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      'ambiguousFunctionDecl',
1022da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      'checkRegExp',
1032da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      'checkTypes',
1042da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      'checkVars',
1052da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      'const',
1062da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      'constantProperty',
1072da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      'deprecated',
1082da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      'duplicate',
1092da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      'es5Strict',
1102da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      'externsValidation',
1112da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      'extraProvide',
1122da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      'extraRequire',
1132da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      'fileoverviewTags',
1142da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      'globalThis',
1152da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      'internetExplorerChecks',
1162da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      'invalidCasts',
1172da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      'missingProperties',
1182da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      'missingProvide',
1192da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      'missingRequire',
1202da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      'nonStandardJsDocs',
1212da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      'strictModuleDepCheck',
1222da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      'tweakValidation',
1232da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      'typeInvalidation',
1242da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      'undefinedNames',
1252da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      'undefinedVars',
1262da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      'underscore',
1272da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      'unknownDefines',
1282da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      'uselessCode',
1292da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      'visibility',
1302da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      'with'])
1312da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
1322da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  HAS_DESCRIPTION = frozenset([
1332da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    'define', 'deprecated', 'desc', 'fileoverview', 'license', 'param',
1342da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    'preserve', 'return', 'supported'])
1352da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
1362da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  HAS_TYPE = frozenset([
1372da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      'define', 'enum', 'extends', 'implements', 'param', 'return', 'type',
1382da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      'suppress'])
1392da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
1402da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  TYPE_ONLY = frozenset(['enum', 'extends', 'implements',  'suppress', 'type'])
1412da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
1422da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  HAS_NAME = frozenset(['param'])
1432da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
1442da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  EMPTY_COMMENT_LINE = re.compile(r'^\s*\*?\s*$')
1452da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  EMPTY_STRING = re.compile(r'^\s*$')
1462da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
1472da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  def __init__(self, flag_token):
1482da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    """Creates the DocFlag object and attaches it to the given start token.
1492da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
1502da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    Args:
1512da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      flag_token: The starting token of the flag.
1522da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    """
1532da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    self.flag_token = flag_token
1542da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    self.flag_type = flag_token.string.strip().lstrip('@')
1552da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
1562da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    # Extract type, if applicable.
1572da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    self.type = None
1582da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    self.type_start_token = None
1592da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    self.type_end_token = None
1602da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    if self.flag_type in self.HAS_TYPE:
1612da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      brace = tokenutil.SearchUntil(flag_token, [Type.DOC_START_BRACE],
1622da489cd246702bee5938545b18a6f710ed214bcJamie Gennis                                    Type.FLAG_ENDING_TYPES)
1632da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      if brace:
1642da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        end_token, contents = _GetMatchingEndBraceAndContents(brace)
1652da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        self.type = contents
1662da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        self.type_start_token = brace
1672da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        self.type_end_token = end_token
1682da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      elif (self.flag_type in self.TYPE_ONLY and
1692da489cd246702bee5938545b18a6f710ed214bcJamie Gennis          flag_token.next.type not in Type.FLAG_ENDING_TYPES):
1702da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        self.type_start_token = flag_token.next
1712da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        self.type_end_token, self.type = _GetEndTokenAndContents(
1722da489cd246702bee5938545b18a6f710ed214bcJamie Gennis            self.type_start_token)
1732da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        if self.type is not None:
1742da489cd246702bee5938545b18a6f710ed214bcJamie Gennis          self.type = self.type.strip()
1752da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
1762da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    # Extract name, if applicable.
1772da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    self.name_token = None
1782da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    self.name = None
1792da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    if self.flag_type in self.HAS_NAME:
1802da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      # Handle bad case, name could be immediately after flag token.
1812da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      self.name_token = _GetNextIdentifierToken(flag_token)
1822da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
1832da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      # Handle good case, if found token is after type start, look for
1842da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      # identifier after type end, since types contain identifiers.
1852da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      if (self.type and self.name_token and
1862da489cd246702bee5938545b18a6f710ed214bcJamie Gennis          tokenutil.Compare(self.name_token, self.type_start_token) > 0):
1872da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        self.name_token = _GetNextIdentifierToken(self.type_end_token)
1882da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
1892da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      if self.name_token:
1902da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        self.name = self.name_token.string
1912da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
1922da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    # Extract description, if applicable.
1932da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    self.description_start_token = None
1942da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    self.description_end_token = None
1952da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    self.description = None
1962da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    if self.flag_type in self.HAS_DESCRIPTION:
1972da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      search_start_token = flag_token
1982da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      if self.name_token and self.type_end_token:
1992da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        if tokenutil.Compare(self.type_end_token, self.name_token) > 0:
2002da489cd246702bee5938545b18a6f710ed214bcJamie Gennis          search_start_token = self.type_end_token
2012da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        else:
2022da489cd246702bee5938545b18a6f710ed214bcJamie Gennis          search_start_token = self.name_token
2032da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      elif self.name_token:
2042da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        search_start_token = self.name_token
2052da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      elif self.type:
2062da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        search_start_token = self.type_end_token
2072da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
2082da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      interesting_token = tokenutil.Search(search_start_token,
2092da489cd246702bee5938545b18a6f710ed214bcJamie Gennis          Type.FLAG_DESCRIPTION_TYPES | Type.FLAG_ENDING_TYPES)
2102da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      if interesting_token.type in Type.FLAG_DESCRIPTION_TYPES:
2112da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        self.description_start_token = interesting_token
2122da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        self.description_end_token, self.description = (
2132da489cd246702bee5938545b18a6f710ed214bcJamie Gennis            _GetEndTokenAndContents(interesting_token))
2142da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
2152da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
2162da489cd246702bee5938545b18a6f710ed214bcJamie Gennisclass DocComment(object):
2172da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  """JavaScript doc comment object.
2182da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
2192da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  Attributes:
2202da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    ordered_params: Ordered list of parameters documented.
2212da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    start_token: The token that starts the doc comment.
2222da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    end_token: The token that ends the doc comment.
2232da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    suppressions: Map of suppression type to the token that added it.
2242da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  """
2252da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  def __init__(self, start_token):
2262da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    """Create the doc comment object.
2272da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
2282da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    Args:
2292da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      start_token: The first token in the doc comment.
2302da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    """
2312da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    self.__params = {}
2322da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    self.ordered_params = []
2332da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    self.__flags = {}
2342da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    self.start_token = start_token
2352da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    self.end_token = None
2362da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    self.suppressions = {}
2372da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    self.invalidated = False
2382da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
2392da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  def Invalidate(self):
2402da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    """Indicate that the JSDoc is well-formed but we had problems parsing it.
2412da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
2422da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    This is a short-circuiting mechanism so that we don't emit false
2432da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    positives about well-formed doc comments just because we don't support
2442da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    hot new syntaxes.
2452da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    """
2462da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    self.invalidated = True
2472da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
2482da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  def IsInvalidated(self):
2492da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    """Test whether Invalidate() has been called."""
2502da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    return self.invalidated
2512da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
2522da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  def AddParam(self, name, param_type):
2532da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    """Add a new documented parameter.
2542da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
2552da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    Args:
2562da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      name: The name of the parameter to document.
2572da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      param_type: The parameter's declared JavaScript type.
2582da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    """
2592da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    self.ordered_params.append(name)
2602da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    self.__params[name] = param_type
2612da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
2622da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  def AddSuppression(self, token):
2632da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    """Add a new error suppression flag.
2642da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
2652da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    Args:
2662da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      token: The suppression flag token.
2672da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    """
2682da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    #TODO(user): Error if no braces
2692da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    brace = tokenutil.SearchUntil(token, [Type.DOC_START_BRACE],
2702da489cd246702bee5938545b18a6f710ed214bcJamie Gennis                                  [Type.DOC_FLAG])
2712da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    if brace:
2722da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      end_token, contents = _GetMatchingEndBraceAndContents(brace)
2732da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      for suppression in contents.split('|'):
2742da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        self.suppressions[suppression] = token
2752da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
2762da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  def SuppressionOnly(self):
2772da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    """Returns whether this comment contains only suppression flags."""
2782da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    for flag_type in self.__flags.keys():
2792da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      if flag_type != 'suppress':
2802da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        return False
2812da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    return True
2822da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
2832da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  def AddFlag(self, flag):
2842da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    """Add a new document flag.
2852da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
2862da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    Args:
2872da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      flag: DocFlag object.
2882da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    """
2892da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    self.__flags[flag.flag_type] = flag
2902da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
2912da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  def InheritsDocumentation(self):
2922da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    """Test if the jsdoc implies documentation inheritance.
2932da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
2942da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    Returns:
2952da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        True if documentation may be pulled off the superclass.
2962da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    """
2972da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    return self.HasFlag('inheritDoc') or self.HasFlag('override')
2982da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
2992da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  def HasFlag(self, flag_type):
3002da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    """Test if the given flag has been set.
3012da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
3022da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    Args:
3032da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      flag_type: The type of the flag to check.
3042da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
3052da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    Returns:
3062da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      True if the flag is set.
3072da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    """
3082da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    return flag_type in self.__flags
3092da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
3102da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  def GetFlag(self, flag_type):
3112da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    """Gets the last flag of the given type.
3122da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
3132da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    Args:
3142da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      flag_type: The type of the flag to get.
3152da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
3162da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    Returns:
3172da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      The last instance of the given flag type in this doc comment.
3182da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    """
3192da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    return self.__flags[flag_type]
3202da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
3212da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  def CompareParameters(self, params):
3222da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    """Computes the edit distance and list from the function params to the docs.
3232da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
3242da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    Uses the Levenshtein edit distance algorithm, with code modified from
3252da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    http://en.wikibooks.org/wiki/Algorithm_implementation/Strings/Levenshtein_distance#Python
3262da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
3272da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    Args:
3282da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      params: The parameter list for the function declaration.
3292da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
3302da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    Returns:
3312da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      The edit distance, the edit list.
3322da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    """
3332da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    source_len, target_len = len(self.ordered_params), len(params)
3342da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    edit_lists = [[]]
3352da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    distance = [[]]
3362da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    for i in range(target_len+1):
3372da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      edit_lists[0].append(['I'] * i)
3382da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      distance[0].append(i)
3392da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
3402da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    for j in range(1, source_len+1):
3412da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      edit_lists.append([['D'] * j])
3422da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      distance.append([j])
3432da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
3442da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    for i in range(source_len):
3452da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      for j in range(target_len):
3462da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        cost = 1
3472da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        if self.ordered_params[i] == params[j]:
3482da489cd246702bee5938545b18a6f710ed214bcJamie Gennis          cost = 0
3492da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
3502da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        deletion = distance[i][j+1] + 1
3512da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        insertion = distance[i+1][j] + 1
3522da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        substitution = distance[i][j] + cost
3532da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
3542da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        edit_list = None
3552da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        best = None
3562da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        if deletion <= insertion and deletion <= substitution:
3572da489cd246702bee5938545b18a6f710ed214bcJamie Gennis          # Deletion is best.
3582da489cd246702bee5938545b18a6f710ed214bcJamie Gennis          best = deletion
3592da489cd246702bee5938545b18a6f710ed214bcJamie Gennis          edit_list = list(edit_lists[i][j+1])
3602da489cd246702bee5938545b18a6f710ed214bcJamie Gennis          edit_list.append('D')
3612da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
3622da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        elif insertion <= substitution:
3632da489cd246702bee5938545b18a6f710ed214bcJamie Gennis          # Insertion is best.
3642da489cd246702bee5938545b18a6f710ed214bcJamie Gennis          best = insertion
3652da489cd246702bee5938545b18a6f710ed214bcJamie Gennis          edit_list = list(edit_lists[i+1][j])
3662da489cd246702bee5938545b18a6f710ed214bcJamie Gennis          edit_list.append('I')
3672da489cd246702bee5938545b18a6f710ed214bcJamie Gennis          edit_lists[i+1].append(edit_list)
3682da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
3692da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        else:
3702da489cd246702bee5938545b18a6f710ed214bcJamie Gennis          # Substitution is best.
3712da489cd246702bee5938545b18a6f710ed214bcJamie Gennis          best = substitution
3722da489cd246702bee5938545b18a6f710ed214bcJamie Gennis          edit_list = list(edit_lists[i][j])
3732da489cd246702bee5938545b18a6f710ed214bcJamie Gennis          if cost:
3742da489cd246702bee5938545b18a6f710ed214bcJamie Gennis            edit_list.append('S')
3752da489cd246702bee5938545b18a6f710ed214bcJamie Gennis          else:
3762da489cd246702bee5938545b18a6f710ed214bcJamie Gennis            edit_list.append('=')
3772da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
3782da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        edit_lists[i+1].append(edit_list)
3792da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        distance[i+1].append(best)
3802da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
3812da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    return distance[source_len][target_len], edit_lists[source_len][target_len]
3822da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
3832da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  def __repr__(self):
3842da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    """Returns a string representation of this object.
3852da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
3862da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    Returns:
3872da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      A string representation of this object.
3882da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    """
3892da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    return '<DocComment: %s, %s>' % (str(self.__params), str(self.__flags))
3902da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
3912da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
3922da489cd246702bee5938545b18a6f710ed214bcJamie Gennis#
3932da489cd246702bee5938545b18a6f710ed214bcJamie Gennis# Helper methods used by DocFlag and DocComment to parse out flag information.
3942da489cd246702bee5938545b18a6f710ed214bcJamie Gennis#
3952da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
3962da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
3972da489cd246702bee5938545b18a6f710ed214bcJamie Gennisdef _GetMatchingEndBraceAndContents(start_brace):
3982da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  """Returns the matching end brace and contents between the two braces.
3992da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
4002da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  If any FLAG_ENDING_TYPE token is encountered before a matching end brace, then
4012da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  that token is used as the matching ending token. Contents will have all
4022da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  comment prefixes stripped out of them, and all comment prefixes in between the
4032da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  start and end tokens will be split out into separate DOC_PREFIX tokens.
4042da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
4052da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  Args:
4062da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    start_brace: The DOC_START_BRACE token immediately before desired contents.
4072da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
4082da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  Returns:
4092da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    The matching ending token (DOC_END_BRACE or FLAG_ENDING_TYPE) and a string
4102da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    of the contents between the matching tokens, minus any comment prefixes.
4112da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  """
4122da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  open_count = 1
4132da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  close_count = 0
4142da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  contents = []
4152da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
4162da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  # We don't consider the start brace part of the type string.
4172da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  token = start_brace.next
4182da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  while open_count != close_count:
4192da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    if token.type == Type.DOC_START_BRACE:
4202da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      open_count += 1
4212da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    elif token.type == Type.DOC_END_BRACE:
4222da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      close_count += 1
4232da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
4242da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    if token.type != Type.DOC_PREFIX:
4252da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      contents.append(token.string)
4262da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
4272da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    if token.type in Type.FLAG_ENDING_TYPES:
4282da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      break
4292da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    token = token.next
4302da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
4312da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  #Don't include the end token (end brace, end doc comment, etc.) in type.
4322da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  token = token.previous
4332da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  contents = contents[:-1]
4342da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
4352da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  return token, ''.join(contents)
4362da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
4372da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
4382da489cd246702bee5938545b18a6f710ed214bcJamie Gennisdef _GetNextIdentifierToken(start_token):
4392da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  """Searches for and returns the first identifier at the beginning of a token.
4402da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
4412da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  Searches each token after the start to see if it starts with an identifier.
4422da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  If found, will split the token into at most 3 piecies: leading whitespace,
4432da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  identifier, rest of token, returning the identifier token. If no identifier is
4442da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  found returns None and changes no tokens. Search is abandoned when a
4452da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  FLAG_ENDING_TYPE token is found.
4462da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
4472da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  Args:
4482da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    start_token: The token to start searching after.
4492da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
4502da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  Returns:
4512da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    The identifier token is found, None otherwise.
4522da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  """
4532da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  token = start_token.next
4542da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
4552da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  while token and not token.type in Type.FLAG_ENDING_TYPES:
4562da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    match = javascripttokenizer.JavaScriptTokenizer.IDENTIFIER.match(
4572da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        token.string)
4582da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    if (match is not None and token.type == Type.COMMENT and
4592da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        len(token.string) == len(match.group(0))):
4602da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      return token
4612da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
4622da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    token = token.next
4632da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
4642da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  return None
4652da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
4662da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
4672da489cd246702bee5938545b18a6f710ed214bcJamie Gennisdef _GetEndTokenAndContents(start_token):
4682da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  """Returns last content token and all contents before FLAG_ENDING_TYPE token.
4692da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
4702da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  Comment prefixes are split into DOC_PREFIX tokens and stripped from the
4712da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  returned contents.
4722da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
4732da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  Args:
4742da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    start_token: The token immediately before the first content token.
4752da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
4762da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  Returns:
4772da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    The last content token and a string of all contents including start and
4782da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    end tokens, with comment prefixes stripped.
4792da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  """
4802da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  iterator = start_token
4812da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  last_line = iterator.line_number
4822da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  last_token = None
4832da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  contents = ''
4842da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  doc_depth = 0
4852da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  while not iterator.type in Type.FLAG_ENDING_TYPES or doc_depth > 0:
4862da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    if (iterator.IsFirstInLine() and
4872da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        DocFlag.EMPTY_COMMENT_LINE.match(iterator.line)):
4882da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      # If we have a blank comment line, consider that an implicit
4892da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      # ending of the description. This handles a case like:
4902da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      #
4912da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      # * @return {boolean} True
4922da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      # *
4932da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      # * Note: This is a sentence.
4942da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      #
4952da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      # The note is not part of the @return description, but there was
4962da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      # no definitive ending token. Rather there was a line containing
4972da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      # only a doc comment prefix or whitespace.
4982da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      break
4992da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
5002da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    # b/2983692
5012da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    # don't prematurely match against a @flag if inside a doc flag
5022da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    # need to think about what is the correct behavior for unterminated
5032da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    # inline doc flags
5042da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    if (iterator.type == Type.DOC_START_BRACE and
5052da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        iterator.next.type == Type.DOC_INLINE_FLAG):
5062da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      doc_depth += 1
5072da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    elif (iterator.type == Type.DOC_END_BRACE and
5082da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        doc_depth > 0):
5092da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      doc_depth -= 1
5102da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
5112da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    if iterator.type in Type.FLAG_DESCRIPTION_TYPES:
5122da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      contents += iterator.string
5132da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      last_token = iterator
5142da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
5152da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    iterator = iterator.next
5162da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    if iterator.line_number != last_line:
5172da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      contents += '\n'
5182da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      last_line = iterator.line_number
5192da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
5202da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  end_token = last_token
5212da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  if DocFlag.EMPTY_STRING.match(contents):
5222da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    contents = None
5232da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  else:
5242da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    # Strip trailing newline.
5252da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    contents = contents[:-1]
5262da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
5272da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  return end_token, contents
5282da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
5292da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
5302da489cd246702bee5938545b18a6f710ed214bcJamie Gennisclass Function(object):
5312da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  """Data about a JavaScript function.
5322da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
5332da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  Attributes:
5342da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    block_depth: Block depth the function began at.
5352da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    doc: The DocComment associated with the function.
5362da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    has_return: If the function has a return value.
5372da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    has_this: If the function references the 'this' object.
5382da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    is_assigned: If the function is part of an assignment.
5392da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    is_constructor: If the function is a constructor.
5402da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    name: The name of the function, whether given in the function keyword or
5412da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        as the lvalue the function is assigned to.
5422da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  """
5432da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
5442da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  def __init__(self, block_depth, is_assigned, doc, name):
5452da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    self.block_depth = block_depth
5462da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    self.is_assigned = is_assigned
5472da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    self.is_constructor = doc and doc.HasFlag('constructor')
5482da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    self.is_interface = doc and doc.HasFlag('interface')
5492da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    self.has_return = False
5502da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    self.has_throw = False
5512da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    self.has_this = False
5522da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    self.name = name
5532da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    self.doc = doc
5542da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
5552da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
5562da489cd246702bee5938545b18a6f710ed214bcJamie Gennisclass StateTracker(object):
5572da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  """EcmaScript state tracker.
5582da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
5592da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  Tracks block depth, function names, etc. within an EcmaScript token stream.
5602da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  """
5612da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
5622da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  OBJECT_LITERAL = 'o'
5632da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  CODE = 'c'
5642da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
5652da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  def __init__(self, doc_flag=DocFlag):
5662da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    """Initializes a JavaScript token stream state tracker.
5672da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
5682da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    Args:
5692da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      doc_flag: An optional custom DocFlag used for validating
5702da489cd246702bee5938545b18a6f710ed214bcJamie Gennis          documentation flags.
5712da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    """
5722da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    self._doc_flag = doc_flag
5732da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    self.Reset()
5742da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
5752da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  def Reset(self):
5762da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    """Resets the state tracker to prepare for processing a new page."""
5772da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    self._block_depth = 0
5782da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    self._is_block_close = False
5792da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    self._paren_depth = 0
5802da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    self._functions = []
5812da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    self._functions_by_name = {}
5822da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    self._last_comment = None
5832da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    self._doc_comment = None
5842da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    self._cumulative_params = None
5852da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    self._block_types = []
5862da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    self._last_non_space_token = None
5872da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    self._last_line = None
5882da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    self._first_token = None
5892da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    self._documented_identifiers = set()
5902da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
5912da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  def InFunction(self):
5922da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    """Returns true if the current token is within a function.
5932da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
5942da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    Returns:
5952da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      True if the current token is within a function.
5962da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    """
5972da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    return bool(self._functions)
5982da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
5992da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  def InConstructor(self):
6002da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    """Returns true if the current token is within a constructor.
6012da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
6022da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    Returns:
6032da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      True if the current token is within a constructor.
6042da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    """
6052da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    return self.InFunction() and self._functions[-1].is_constructor
6062da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
6072da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  def InInterfaceMethod(self):
6082da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    """Returns true if the current token is within an interface method.
6092da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
6102da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    Returns:
6112da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      True if the current token is within an interface method.
6122da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    """
6132da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    if self.InFunction():
6142da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      if self._functions[-1].is_interface:
6152da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        return True
6162da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      else:
6172da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        name = self._functions[-1].name
6182da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        prototype_index = name.find('.prototype.')
6192da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        if prototype_index != -1:
6202da489cd246702bee5938545b18a6f710ed214bcJamie Gennis          class_function_name = name[0:prototype_index]
6212da489cd246702bee5938545b18a6f710ed214bcJamie Gennis          if (class_function_name in self._functions_by_name and
6222da489cd246702bee5938545b18a6f710ed214bcJamie Gennis              self._functions_by_name[class_function_name].is_interface):
6232da489cd246702bee5938545b18a6f710ed214bcJamie Gennis            return True
6242da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
6252da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    return False
6262da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
6272da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  def InTopLevelFunction(self):
6282da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    """Returns true if the current token is within a top level function.
6292da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
6302da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    Returns:
6312da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      True if the current token is within a top level function.
6322da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    """
6332da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    return len(self._functions) == 1 and self.InTopLevel()
6342da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
6352da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  def InAssignedFunction(self):
6362da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    """Returns true if the current token is within a function variable.
6372da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
6382da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    Returns:
6392da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      True if if the current token is within a function variable
6402da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    """
6412da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    return self.InFunction() and self._functions[-1].is_assigned
6422da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
6432da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  def IsFunctionOpen(self):
6442da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    """Returns true if the current token is a function block open.
6452da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
6462da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    Returns:
6472da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      True if the current token is a function block open.
6482da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    """
6492da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    return (self._functions and
6502da489cd246702bee5938545b18a6f710ed214bcJamie Gennis            self._functions[-1].block_depth == self._block_depth - 1)
6512da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
6522da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  def IsFunctionClose(self):
6532da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    """Returns true if the current token is a function block close.
6542da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
6552da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    Returns:
6562da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      True if the current token is a function block close.
6572da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    """
6582da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    return (self._functions and
6592da489cd246702bee5938545b18a6f710ed214bcJamie Gennis            self._functions[-1].block_depth == self._block_depth)
6602da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
6612da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  def InBlock(self):
6622da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    """Returns true if the current token is within a block.
6632da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
6642da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    Returns:
6652da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      True if the current token is within a block.
6662da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    """
6672da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    return bool(self._block_depth)
6682da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
6692da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  def IsBlockClose(self):
6702da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    """Returns true if the current token is a block close.
6712da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
6722da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    Returns:
6732da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      True if the current token is a block close.
6742da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    """
6752da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    return self._is_block_close
6762da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
6772da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  def InObjectLiteral(self):
6782da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    """Returns true if the current token is within an object literal.
6792da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
6802da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    Returns:
6812da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      True if the current token is within an object literal.
6822da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    """
6832da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    return self._block_depth and self._block_types[-1] == self.OBJECT_LITERAL
6842da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
6852da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  def InObjectLiteralDescendant(self):
6862da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    """Returns true if the current token has an object literal ancestor.
6872da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
6882da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    Returns:
6892da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      True if the current token has an object literal ancestor.
6902da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    """
6912da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    return self.OBJECT_LITERAL in self._block_types
6922da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
6932da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  def InParentheses(self):
6942da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    """Returns true if the current token is within parentheses.
6952da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
6962da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    Returns:
6972da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      True if the current token is within parentheses.
6982da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    """
6992da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    return bool(self._paren_depth)
7002da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
7012da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  def InTopLevel(self):
7022da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    """Whether we are at the top level in the class.
7032da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
7042da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    This function call is language specific.  In some languages like
7052da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    JavaScript, a function is top level if it is not inside any parenthesis.
7062da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    In languages such as ActionScript, a function is top level if it is directly
7072da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    within a class.
7082da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    """
7092da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    raise TypeError('Abstract method InTopLevel not implemented')
7102da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
7112da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  def GetBlockType(self, token):
7122da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    """Determine the block type given a START_BLOCK token.
7132da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
7142da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    Code blocks come after parameters, keywords  like else, and closing parens.
7152da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
7162da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    Args:
7172da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      token: The current token. Can be assumed to be type START_BLOCK.
7182da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    Returns:
7192da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      Code block type for current token.
7202da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    """
7212da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    raise TypeError('Abstract method GetBlockType not implemented')
7222da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
7232da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  def GetParams(self):
7242da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    """Returns the accumulated input params as an array.
7252da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
7262da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    In some EcmasSript languages, input params are specified like
7272da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    (param:Type, param2:Type2, ...)
7282da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    in other they are specified just as
7292da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    (param, param2)
7302da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    We handle both formats for specifying parameters here and leave
7312da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    it to the compilers for each language to detect compile errors.
7322da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    This allows more code to be reused between lint checkers for various
7332da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    EcmaScript languages.
7342da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
7352da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    Returns:
7362da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      The accumulated input params as an array.
7372da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    """
7382da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    params = []
7392da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    if self._cumulative_params:
7402da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      params = re.compile(r'\s+').sub('', self._cumulative_params).split(',')
7412da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      # Strip out the type from parameters of the form name:Type.
7422da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      params = map(lambda param: param.split(':')[0], params)
7432da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
7442da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    return params
7452da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
7462da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  def GetLastComment(self):
7472da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    """Return the last plain comment that could be used as documentation.
7482da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
7492da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    Returns:
7502da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      The last plain comment that could be used as documentation.
7512da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    """
7522da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    return self._last_comment
7532da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
7542da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  def GetDocComment(self):
7552da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    """Return the most recent applicable documentation comment.
7562da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
7572da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    Returns:
7582da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      The last applicable documentation comment.
7592da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    """
7602da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    return self._doc_comment
7612da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
7622da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  def HasDocComment(self, identifier):
7632da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    """Returns whether the identifier has been documented yet.
7642da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
7652da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    Args:
7662da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      identifier: The identifier.
7672da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
7682da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    Returns:
7692da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      Whether the identifier has been documented yet.
7702da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    """
7712da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    return identifier in self._documented_identifiers
7722da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
7732da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  def InDocComment(self):
7742da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    """Returns whether the current token is in a doc comment.
7752da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
7762da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    Returns:
7772da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      Whether the current token is in a doc comment.
7782da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    """
7792da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    return self._doc_comment and self._doc_comment.end_token is None
7802da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
7812da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  def GetDocFlag(self):
7822da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    """Returns the current documentation flags.
7832da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
7842da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    Returns:
7852da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      The current documentation flags.
7862da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    """
7872da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    return self._doc_flag
7882da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
7892da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  def IsTypeToken(self, t):
7902da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    if self.InDocComment() and t.type not in (Type.START_DOC_COMMENT,
7912da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        Type.DOC_FLAG, Type.DOC_INLINE_FLAG, Type.DOC_PREFIX):
7922da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      f = tokenutil.SearchUntil(t, [Type.DOC_FLAG], [Type.START_DOC_COMMENT],
7932da489cd246702bee5938545b18a6f710ed214bcJamie Gennis                                None, True)
7942da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      if f and f.attached_object.type_start_token is not None:
7952da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        return (tokenutil.Compare(t, f.attached_object.type_start_token) > 0 and
7962da489cd246702bee5938545b18a6f710ed214bcJamie Gennis                tokenutil.Compare(t, f.attached_object.type_end_token) < 0)
7972da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    return False
7982da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
7992da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  def GetFunction(self):
8002da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    """Return the function the current code block is a part of.
8012da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
8022da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    Returns:
8032da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      The current Function object.
8042da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    """
8052da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    if self._functions:
8062da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      return self._functions[-1]
8072da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
8082da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  def GetBlockDepth(self):
8092da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    """Return the block depth.
8102da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
8112da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    Returns:
8122da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      The current block depth.
8132da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    """
8142da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    return self._block_depth
8152da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
8162da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  def GetLastNonSpaceToken(self):
8172da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    """Return the last non whitespace token."""
8182da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    return self._last_non_space_token
8192da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
8202da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  def GetLastLine(self):
8212da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    """Return the last line."""
8222da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    return self._last_line
8232da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
8242da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  def GetFirstToken(self):
8252da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    """Return the very first token in the file."""
8262da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    return self._first_token
8272da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
8282da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  def HandleToken(self, token, last_non_space_token):
8292da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    """Handles the given token and updates state.
8302da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
8312da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    Args:
8322da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      token: The token to handle.
8332da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      last_non_space_token:
8342da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    """
8352da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    self._is_block_close = False
8362da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
8372da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    if not self._first_token:
8382da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      self._first_token = token
8392da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
8402da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    # Track block depth.
8412da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    type = token.type
8422da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    if type == Type.START_BLOCK:
8432da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      self._block_depth += 1
8442da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
8452da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      # Subclasses need to handle block start very differently because
8462da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      # whether a block is a CODE or OBJECT_LITERAL block varies significantly
8472da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      # by language.
8482da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      self._block_types.append(self.GetBlockType(token))
8492da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
8502da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    # Track block depth.
8512da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    elif type == Type.END_BLOCK:
8522da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      self._is_block_close = not self.InObjectLiteral()
8532da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      self._block_depth -= 1
8542da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      self._block_types.pop()
8552da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
8562da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    # Track parentheses depth.
8572da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    elif type == Type.START_PAREN:
8582da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      self._paren_depth += 1
8592da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
8602da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    # Track parentheses depth.
8612da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    elif type == Type.END_PAREN:
8622da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      self._paren_depth -= 1
8632da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
8642da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    elif type == Type.COMMENT:
8652da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      self._last_comment = token.string
8662da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
8672da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    elif type == Type.START_DOC_COMMENT:
8682da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      self._last_comment = None
8692da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      self._doc_comment = DocComment(token)
8702da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
8712da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    elif type == Type.END_DOC_COMMENT:
8722da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      self._doc_comment.end_token = token
8732da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
8742da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    elif type in (Type.DOC_FLAG, Type.DOC_INLINE_FLAG):
8752da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      flag = self._doc_flag(token)
8762da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      token.attached_object = flag
8772da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      self._doc_comment.AddFlag(flag)
8782da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
8792da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      if flag.flag_type == 'param' and flag.name:
8802da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        self._doc_comment.AddParam(flag.name, flag.type)
8812da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      elif flag.flag_type == 'suppress':
8822da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        self._doc_comment.AddSuppression(token)
8832da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
8842da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    elif type == Type.FUNCTION_DECLARATION:
8852da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      last_code = tokenutil.SearchExcept(token, Type.NON_CODE_TYPES, None,
8862da489cd246702bee5938545b18a6f710ed214bcJamie Gennis                                         True)
8872da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      doc = None
8882da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      # Only functions outside of parens are eligible for documentation.
8892da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      if not self._paren_depth:
8902da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        doc = self._doc_comment
8912da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
8922da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      name = ''
8932da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      is_assigned = last_code and (last_code.IsOperator('=') or
8942da489cd246702bee5938545b18a6f710ed214bcJamie Gennis          last_code.IsOperator('||') or last_code.IsOperator('&&') or
8952da489cd246702bee5938545b18a6f710ed214bcJamie Gennis          (last_code.IsOperator(':') and not self.InObjectLiteral()))
8962da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      if is_assigned:
8972da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        # TODO(robbyw): This breaks for x[2] = ...
8982da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        # Must use loop to find full function name in the case of line-wrapped
8992da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        # declarations (bug 1220601) like:
9002da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        # my.function.foo.
9012da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        #   bar = function() ...
9022da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        identifier = tokenutil.Search(last_code, Type.SIMPLE_LVALUE, None, True)
9032da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        while identifier and identifier.type in (
9042da489cd246702bee5938545b18a6f710ed214bcJamie Gennis            Type.IDENTIFIER, Type.SIMPLE_LVALUE):
9052da489cd246702bee5938545b18a6f710ed214bcJamie Gennis          name = identifier.string + name
9062da489cd246702bee5938545b18a6f710ed214bcJamie Gennis          # Traverse behind us, skipping whitespace and comments.
9072da489cd246702bee5938545b18a6f710ed214bcJamie Gennis          while True:
9082da489cd246702bee5938545b18a6f710ed214bcJamie Gennis            identifier = identifier.previous
9092da489cd246702bee5938545b18a6f710ed214bcJamie Gennis            if not identifier or not identifier.type in Type.NON_CODE_TYPES:
9102da489cd246702bee5938545b18a6f710ed214bcJamie Gennis              break
9112da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
9122da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      else:
9132da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        next_token = tokenutil.SearchExcept(token, Type.NON_CODE_TYPES)
9142da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        while next_token and next_token.IsType(Type.FUNCTION_NAME):
9152da489cd246702bee5938545b18a6f710ed214bcJamie Gennis          name += next_token.string
9162da489cd246702bee5938545b18a6f710ed214bcJamie Gennis          next_token = tokenutil.Search(next_token, Type.FUNCTION_NAME, 2)
9172da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
9182da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      function = Function(self._block_depth, is_assigned, doc, name)
9192da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      self._functions.append(function)
9202da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      self._functions_by_name[name] = function
9212da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
9222da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    elif type == Type.START_PARAMETERS:
9232da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      self._cumulative_params = ''
9242da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
9252da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    elif type == Type.PARAMETERS:
9262da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      self._cumulative_params += token.string
9272da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
9282da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    elif type == Type.KEYWORD and token.string == 'return':
9292da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      next_token = tokenutil.SearchExcept(token, Type.NON_CODE_TYPES)
9302da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      if not next_token.IsType(Type.SEMICOLON):
9312da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        function = self.GetFunction()
9322da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        if function:
9332da489cd246702bee5938545b18a6f710ed214bcJamie Gennis          function.has_return = True
9342da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
9352da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    elif type == Type.KEYWORD and token.string == 'throw':
9362da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      function = self.GetFunction()
9372da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      if function:
9382da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        function.has_throw = True
9392da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
9402da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    elif type == Type.SIMPLE_LVALUE:
9412da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      identifier = token.values['identifier']
9422da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      jsdoc = self.GetDocComment()
9432da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      if jsdoc:
9442da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        self._documented_identifiers.add(identifier)
9452da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
9462da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      self._HandleIdentifier(identifier, True)
9472da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
9482da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    elif type == Type.IDENTIFIER:
9492da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      self._HandleIdentifier(token.string, False)
9502da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
9512da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      # Detect documented non-assignments.
9522da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      next_token = tokenutil.SearchExcept(token, Type.NON_CODE_TYPES)
9532da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      if next_token.IsType(Type.SEMICOLON):
9542da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        if (self._last_non_space_token and
9552da489cd246702bee5938545b18a6f710ed214bcJamie Gennis            self._last_non_space_token.IsType(Type.END_DOC_COMMENT)):
9562da489cd246702bee5938545b18a6f710ed214bcJamie Gennis          self._documented_identifiers.add(token.string)
9572da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
9582da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  def _HandleIdentifier(self, identifier, is_assignment):
9592da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    """Process the given identifier.
9602da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
9612da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    Currently checks if it references 'this' and annotates the function
9622da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    accordingly.
9632da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
9642da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    Args:
9652da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      identifier: The identifer to process.
9662da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      is_assignment: Whether the identifer is being written to.
9672da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    """
9682da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    if identifier == 'this' or identifier.startswith('this.'):
9692da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      function = self.GetFunction()
9702da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      if function:
9712da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        function.has_this = True
9722da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
9732da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
9742da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  def HandleAfterToken(self, token):
9752da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    """Handle updating state after a token has been checked.
9762da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
9772da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    This function should be used for destructive state changes such as
9782da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    deleting a tracked object.
9792da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
9802da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    Args:
9812da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      token: The token to handle.
9822da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    """
9832da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    type = token.type
9842da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    if type == Type.SEMICOLON or type == Type.END_PAREN or (
9852da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        type == Type.END_BRACKET and
9862da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        self._last_non_space_token.type not in (
9872da489cd246702bee5938545b18a6f710ed214bcJamie Gennis            Type.SINGLE_QUOTE_STRING_END, Type.DOUBLE_QUOTE_STRING_END)):
9882da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      # We end on any numeric array index, but keep going for string based
9892da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      # array indices so that we pick up manually exported identifiers.
9902da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      self._doc_comment = None
9912da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      self._last_comment = None
9922da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
9932da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    elif type == Type.END_BLOCK:
9942da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      self._doc_comment = None
9952da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      self._last_comment = None
9962da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
9972da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      if self.InFunction() and self.IsFunctionClose():
9982da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        # TODO(robbyw): Detect the function's name for better errors.
9992da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        self._functions.pop()
10002da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
10012da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    elif type == Type.END_PARAMETERS and self._doc_comment:
10022da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      self._doc_comment = None
10032da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      self._last_comment = None
10042da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
10052da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    if not token.IsAnyType(Type.WHITESPACE, Type.BLANK_LINE):
10062da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      self._last_non_space_token = token
10072da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
10082da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    self._last_line = token.line
1009