| Index: third_party/closure_linter/closure_linter/statetracker.py
|
| diff --git a/third_party/closure_linter/closure_linter/statetracker.py b/third_party/closure_linter/closure_linter/statetracker.py
|
| index ba25dfc5cf916de33d99ca14cc4c86b75bf088f3..5730facb87dc7ea2d89b423167e5f1a799884b02 100755
|
| --- a/third_party/closure_linter/closure_linter/statetracker.py
|
| +++ b/third_party/closure_linter/closure_linter/statetracker.py
|
| @@ -24,6 +24,7 @@ import re
|
| from closure_linter import javascripttokenizer
|
| from closure_linter import javascripttokens
|
| from closure_linter import tokenutil
|
| +from closure_linter import typeannotation
|
|
|
| # Shorthand
|
| Type = javascripttokens.JavaScriptTokenType
|
| @@ -39,7 +40,8 @@ class DocFlag(object):
|
| including braces.
|
| type_end_token: The last token specifying the flag type,
|
| including braces.
|
| - type: The type spec.
|
| + type: The type spec string.
|
| + jstype: The type spec, a TypeAnnotation instance.
|
| name_token: The token specifying the flag name.
|
| name: The flag name
|
| description_start_token: The first token in the description.
|
| @@ -78,8 +80,10 @@ class DocFlag(object):
|
| 'nosideeffects',
|
| 'override',
|
| 'owner',
|
| + 'nocollapse',
|
| 'package',
|
| 'param',
|
| + 'polymerBehavior', # This annotation is specific to Polymer.
|
| 'preserve',
|
| 'private',
|
| 'protected',
|
| @@ -111,6 +115,7 @@ class DocFlag(object):
|
| SUPPRESS_TYPES = frozenset([
|
| 'accessControls',
|
| 'ambiguousFunctionDecl',
|
| + 'checkDebuggerStatement',
|
| 'checkRegExp',
|
| 'checkStructDictInheritance',
|
| 'checkTypes',
|
| @@ -132,6 +137,7 @@ class DocFlag(object):
|
| 'missingRequire',
|
| 'missingReturn',
|
| 'nonStandardJsDocs',
|
| + 'reportUnknownTypes',
|
| 'strictModuleDepCheck',
|
| 'suspiciousCode',
|
| 'tweakValidation',
|
| @@ -144,38 +150,86 @@ class DocFlag(object):
|
| 'unusedPrivateMembers',
|
| 'uselessCode',
|
| 'visibility',
|
| - 'with'])
|
| + 'with',
|
| + ])
|
|
|
| HAS_DESCRIPTION = frozenset([
|
| - 'define', 'deprecated', 'desc', 'fileoverview', 'license', 'param',
|
| - 'preserve', 'return', 'supported'])
|
| + 'define',
|
| + 'deprecated',
|
| + 'desc',
|
| + 'fileoverview',
|
| + 'license',
|
| + 'param',
|
| + 'preserve',
|
| + 'return',
|
| + 'supported',
|
| + ])
|
|
|
| + # Docflags whose argument should be parsed using the typeannotation parser.
|
| HAS_TYPE = frozenset([
|
| - 'define', 'enum', 'extends', 'implements', 'param', 'return', 'type',
|
| - 'suppress', 'const', 'package', 'private', 'protected', 'public'])
|
| + 'const',
|
| + 'define',
|
| + 'enum',
|
| + 'export',
|
| + 'extends',
|
| + 'final',
|
| + 'implements',
|
| + 'mods',
|
| + 'package',
|
| + 'param',
|
| + 'private',
|
| + 'protected',
|
| + 'public',
|
| + 'return',
|
| + 'suppress',
|
| + 'type',
|
| + 'typedef',
|
| + ])
|
|
|
| - CAN_OMIT_TYPE = frozenset(['enum', 'const', 'package', 'private',
|
| - 'protected', 'public'])
|
| + # Docflags for which it's ok to omit the type (flag without an argument).
|
| + CAN_OMIT_TYPE = frozenset([
|
| + 'const',
|
| + 'enum',
|
| + 'export',
|
| + 'final',
|
| + 'package',
|
| + 'private',
|
| + 'protected',
|
| + 'public',
|
| + 'suppress', # We'll raise a separate INCORRECT_SUPPRESS_SYNTAX instead.
|
| + ])
|
|
|
| - TYPE_ONLY = frozenset(['enum', 'extends', 'implements', 'suppress', 'type',
|
| - 'const', 'package', 'private', 'protected', 'public'])
|
| + # Docflags that only take a type as an argument and should not parse a
|
| + # following description.
|
| + TYPE_ONLY = frozenset([
|
| + 'const',
|
| + 'enum',
|
| + 'extends',
|
| + 'implements',
|
| + 'package',
|
| + 'suppress',
|
| + 'type',
|
| + ])
|
|
|
| HAS_NAME = frozenset(['param'])
|
|
|
| EMPTY_COMMENT_LINE = re.compile(r'^\s*\*?\s*$')
|
| EMPTY_STRING = re.compile(r'^\s*$')
|
|
|
| - def __init__(self, flag_token):
|
| + def __init__(self, flag_token, error_handler=None):
|
| """Creates the DocFlag object and attaches it to the given start token.
|
|
|
| Args:
|
| flag_token: The starting token of the flag.
|
| + error_handler: An optional error handler for errors occurring while
|
| + parsing the doctype.
|
| """
|
| self.flag_token = flag_token
|
| self.flag_type = flag_token.string.strip().lstrip('@')
|
|
|
| # Extract type, if applicable.
|
| self.type = None
|
| + self.jstype = None
|
| self.type_start_token = None
|
| self.type_end_token = None
|
| if self.flag_type in self.HAS_TYPE:
|
| @@ -184,6 +238,8 @@ class DocFlag(object):
|
| if brace:
|
| end_token, contents = _GetMatchingEndBraceAndContents(brace)
|
| self.type = contents
|
| + self.jstype = typeannotation.Parse(brace, end_token,
|
| + error_handler)
|
| self.type_start_token = brace
|
| self.type_end_token = end_token
|
| elif (self.flag_type in self.TYPE_ONLY and
|
| @@ -197,6 +253,8 @@ class DocFlag(object):
|
| self.type_start_token)
|
| if self.type is not None:
|
| self.type = self.type.strip()
|
| + self.jstype = typeannotation.Parse(flag_token, self.type_end_token,
|
| + error_handler)
|
|
|
| # Extract name, if applicable.
|
| self.name_token = None
|
| @@ -238,6 +296,13 @@ class DocFlag(object):
|
| self.description_end_token, self.description = (
|
| _GetEndTokenAndContents(interesting_token))
|
|
|
| + def HasType(self):
|
| + """Returns whether this flag should have a type annotation."""
|
| + return self.flag_type in self.HAS_TYPE
|
| +
|
| + def __repr__(self):
|
| + return '<Flag: %s, type:%s>' % (self.flag_type, repr(self.jstype))
|
| +
|
|
|
| class DocComment(object):
|
| """JavaScript doc comment object.
|
| @@ -288,12 +353,9 @@ class DocComment(object):
|
| Args:
|
| token: The suppression flag token.
|
| """
|
| - #TODO(user): Error if no braces
|
| - brace = tokenutil.SearchUntil(token, [Type.DOC_START_BRACE],
|
| - [Type.DOC_FLAG])
|
| - if brace:
|
| - end_token, contents = _GetMatchingEndBraceAndContents(brace)
|
| - for suppression in contents.split('|'):
|
| + flag = token and token.attached_object
|
| + if flag and flag.jstype:
|
| + for suppression in flag.jstype.IterIdentifiers():
|
| self.suppressions[suppression] = token
|
|
|
| def SuppressionOnly(self):
|
| @@ -717,6 +779,23 @@ class StateTracker(object):
|
| self._documented_identifiers = set()
|
| self._variables_in_scope = []
|
|
|
| + def DocFlagPass(self, start_token, error_handler):
|
| + """Parses doc flags.
|
| +
|
| + This pass needs to be executed before the aliaspass and we don't want to do
|
| + a full-blown statetracker dry run for these.
|
| +
|
| + Args:
|
| + start_token: The token at which to start iterating
|
| + error_handler: An error handler for error reporting.
|
| + """
|
| + if not start_token:
|
| + return
|
| + doc_flag_types = (Type.DOC_FLAG, Type.DOC_INLINE_FLAG)
|
| + for token in start_token:
|
| + if token.type in doc_flag_types:
|
| + token.attached_object = self._doc_flag(token, error_handler)
|
| +
|
| def InFunction(self):
|
| """Returns true if the current token is within a function.
|
|
|
| @@ -1055,8 +1134,12 @@ class StateTracker(object):
|
| self._doc_comment.end_token = token
|
|
|
| elif type in (Type.DOC_FLAG, Type.DOC_INLINE_FLAG):
|
| - flag = self._doc_flag(token)
|
| - token.attached_object = flag
|
| + # Don't overwrite flags if they were already parsed in a previous pass.
|
| + if token.attached_object is None:
|
| + flag = self._doc_flag(token)
|
| + token.attached_object = flag
|
| + else:
|
| + flag = token.attached_object
|
| self._doc_comment.AddFlag(flag)
|
|
|
| if flag.flag_type == 'suppress':
|
| @@ -1066,8 +1149,8 @@ class StateTracker(object):
|
| last_code = tokenutil.SearchExcept(token, Type.NON_CODE_TYPES, None,
|
| True)
|
| doc = None
|
| - # Only functions outside of parens are eligible for documentation.
|
| - if not self._paren_depth:
|
| + # Only top-level functions are eligible for documentation.
|
| + if self.InTopLevel():
|
| doc = self._doc_comment
|
|
|
| name = ''
|
| @@ -1081,8 +1164,7 @@ class StateTracker(object):
|
| # my.function.foo.
|
| # bar = function() ...
|
| identifier = tokenutil.Search(last_code, Type.SIMPLE_LVALUE, None, True)
|
| - while identifier and identifier.type in (
|
| - Type.IDENTIFIER, Type.SIMPLE_LVALUE):
|
| + while identifier and tokenutil.IsIdentifierOrDot(identifier):
|
| name = identifier.string + name
|
| # Traverse behind us, skipping whitespace and comments.
|
| while True:
|
| @@ -1183,7 +1265,8 @@ class StateTracker(object):
|
| if type == Type.SEMICOLON or type == Type.END_PAREN or (
|
| type == Type.END_BRACKET and
|
| self._last_non_space_token.type not in (
|
| - Type.SINGLE_QUOTE_STRING_END, Type.DOUBLE_QUOTE_STRING_END)):
|
| + Type.SINGLE_QUOTE_STRING_END, Type.DOUBLE_QUOTE_STRING_END,
|
| + Type.TEMPLATE_STRING_END)):
|
| # We end on any numeric array index, but keep going for string based
|
| # array indices so that we pick up manually exported identifiers.
|
| self._doc_comment = None
|
|
|