Index: third_party/closure_linter/closure_linter/typeannotation.py |
diff --git a/third_party/closure_linter/closure_linter/typeannotation.py b/third_party/closure_linter/closure_linter/typeannotation.py |
deleted file mode 100644 |
index e8e270de241b968c5eabed5c1394499d21fe3400..0000000000000000000000000000000000000000 |
--- a/third_party/closure_linter/closure_linter/typeannotation.py |
+++ /dev/null |
@@ -1,410 +0,0 @@ |
-#!/usr/bin/env python |
-#*-* coding: utf-8 |
-"""Closure typeannotation parsing and utilities.""" |
- |
- |
- |
-from closure_linter import errors |
-from closure_linter import javascripttokens |
-from closure_linter.common import error |
- |
-# Shorthand |
-TYPE = javascripttokens.JavaScriptTokenType |
- |
- |
-class TypeAnnotation(object): |
- """Represents a structured view of a closure type annotation. |
- |
- Attribute: |
- identifier: The name of the type. |
- key_type: The name part before a colon. |
- sub_types: The list of sub_types used e.g. for Array.<…> |
- or_null: The '?' annotation |
- not_null: The '!' annotation |
- type_group: If this a a grouping (a|b), but does not include function(a). |
- return_type: The return type of a function definition. |
- alias: The actual type set by closurizednamespaceinfo if the identifier uses |
- an alias to shorten the name. |
- tokens: An ordered list of tokens used for this type. May contain |
- TypeAnnotation instances for sub_types, key_type or return_type. |
- """ |
- |
- IMPLICIT_TYPE_GROUP = 2 |
- |
- NULLABILITY_UNKNOWN = 2 |
- |
- FUNCTION_TYPE = 'function' |
- NULL_TYPE = 'null' |
- VAR_ARGS_TYPE = '...' |
- |
- # Frequently used known non-nullable types. |
- NON_NULLABLE = frozenset([ |
- 'boolean', FUNCTION_TYPE, 'number', 'string', 'undefined']) |
- # Frequently used known nullable types. |
- NULLABLE_TYPE_WHITELIST = frozenset([ |
- 'Array', 'Document', 'Element', 'Function', 'Node', 'NodeList', |
- 'Object']) |
- |
- def __init__(self): |
- self.identifier = '' |
- self.sub_types = [] |
- self.or_null = False |
- self.not_null = False |
- self.type_group = False |
- self.alias = None |
- self.key_type = None |
- self.record_type = False |
- self.opt_arg = False |
- self.return_type = None |
- self.tokens = [] |
- |
- def IsFunction(self): |
- """Determines whether this is a function definition.""" |
- return self.identifier == TypeAnnotation.FUNCTION_TYPE |
- |
- def IsConstructor(self): |
- """Determines whether this is a function definition for a constructor.""" |
- key_type = self.sub_types and self.sub_types[0].key_type |
- return self.IsFunction() and key_type.identifier == 'new' |
- |
- def IsRecordType(self): |
- """Returns True if this type is a record type.""" |
- return (self.record_type or |
- any(t.IsRecordType() for t in self.sub_types)) |
- |
- def IsVarArgsType(self): |
- """Determines if the type is a var_args type, i.e. starts with '...'.""" |
- return self.identifier.startswith(TypeAnnotation.VAR_ARGS_TYPE) or ( |
- self.type_group == TypeAnnotation.IMPLICIT_TYPE_GROUP and |
- self.sub_types[0].identifier.startswith(TypeAnnotation.VAR_ARGS_TYPE)) |
- |
- def IsEmpty(self): |
- """Returns True if the type is empty.""" |
- return not self.tokens |
- |
- def IsUnknownType(self): |
- """Returns True if this is the unknown type {?}.""" |
- return (self.or_null |
- and not self.identifier |
- and not self.sub_types |
- and not self.return_type) |
- |
- def Append(self, item): |
- """Adds a sub_type to this type and finalizes it. |
- |
- Args: |
- item: The TypeAnnotation item to append. |
- """ |
- # item is a TypeAnnotation instance, so pylint: disable=protected-access |
- self.sub_types.append(item._Finalize(self)) |
- |
- def __repr__(self): |
- """Reconstructs the type definition.""" |
- append = '' |
- if self.sub_types: |
- separator = (',' if not self.type_group else '|') |
- if self.IsFunction(): |
- surround = '(%s)' |
- else: |
- surround = {False: '{%s}' if self.record_type else '<%s>', |
- True: '(%s)', |
- TypeAnnotation.IMPLICIT_TYPE_GROUP: '%s'}[self.type_group] |
- append = surround % separator.join(repr(t) for t in self.sub_types) |
- if self.return_type: |
- append += ':%s' % repr(self.return_type) |
- append += '=' if self.opt_arg else '' |
- prefix = '' + ('?' if self.or_null else '') + ('!' if self.not_null else '') |
- keyword = '%s:' % repr(self.key_type) if self.key_type else '' |
- return keyword + prefix + '%s' % (self.alias or self.identifier) + append |
- |
- def ToString(self): |
- """Concats the type's tokens to form a string again.""" |
- ret = [] |
- for token in self.tokens: |
- if not isinstance(token, TypeAnnotation): |
- ret.append(token.string) |
- else: |
- ret.append(token.ToString()) |
- return ''.join(ret) |
- |
- def Dump(self, indent=''): |
- """Dumps this type's structure for debugging purposes.""" |
- result = [] |
- for t in self.tokens: |
- if isinstance(t, TypeAnnotation): |
- result.append(indent + str(t) + ' =>\n' + t.Dump(indent + ' ')) |
- else: |
- result.append(indent + str(t)) |
- return '\n'.join(result) |
- |
- def IterIdentifiers(self): |
- """Iterates over all identifiers in this type and its subtypes.""" |
- if self.identifier: |
- yield self.identifier |
- for subtype in self.IterTypes(): |
- for identifier in subtype.IterIdentifiers(): |
- yield identifier |
- |
- def IterTypeGroup(self): |
- """Iterates over all types in the type group including self. |
- |
- Yields: |
- If this is a implicit or manual type-group: all sub_types. |
- Otherwise: self |
- E.g. for @type {Foo.<Bar>} this will yield only Foo.<Bar>, |
- for @type {Foo|(Bar|Sample)} this will yield Foo, Bar and Sample. |
- |
- """ |
- if self.type_group: |
- for sub_type in self.sub_types: |
- for sub_type in sub_type.IterTypeGroup(): |
- yield sub_type |
- else: |
- yield self |
- |
- def IterTypes(self): |
- """Iterates over each subtype as well as return and key types.""" |
- if self.return_type: |
- yield self.return_type |
- |
- if self.key_type: |
- yield self.key_type |
- |
- for sub_type in self.sub_types: |
- yield sub_type |
- |
- def GetNullability(self, modifiers=True): |
- """Computes whether the type may be null. |
- |
- Args: |
- modifiers: Whether the modifiers ? and ! should be considered in the |
- evaluation. |
- Returns: |
- True if the type allows null, False if the type is strictly non nullable |
- and NULLABILITY_UNKNOWN if the nullability cannot be determined. |
- """ |
- |
- # Explicitly marked nullable types or 'null' are nullable. |
- if ((modifiers and self.or_null) or |
- self.identifier == TypeAnnotation.NULL_TYPE): |
- return True |
- |
- # Explicitly marked non-nullable types or non-nullable base types: |
- if ((modifiers and self.not_null) or self.record_type |
- or self.identifier in TypeAnnotation.NON_NULLABLE): |
- return False |
- |
- # A type group is nullable if any of its elements are nullable. |
- if self.type_group: |
- maybe_nullable = False |
- for sub_type in self.sub_types: |
- nullability = sub_type.GetNullability() |
- if nullability == self.NULLABILITY_UNKNOWN: |
- maybe_nullable = nullability |
- elif nullability: |
- return True |
- return maybe_nullable |
- |
- # Whitelisted types are nullable. |
- if self.identifier.rstrip('.') in TypeAnnotation.NULLABLE_TYPE_WHITELIST: |
- return True |
- |
- # All other types are unknown (most should be nullable, but |
- # enums are not and typedefs might not be). |
- return TypeAnnotation.NULLABILITY_UNKNOWN |
- |
- def WillAlwaysBeNullable(self): |
- """Computes whether the ! flag is illegal for this type. |
- |
- This is the case if this type or any of the subtypes is marked as |
- explicitly nullable. |
- |
- Returns: |
- True if the ! flag would be illegal. |
- """ |
- if self.or_null or self.identifier == TypeAnnotation.NULL_TYPE: |
- return True |
- |
- if self.type_group: |
- return any(t.WillAlwaysBeNullable() for t in self.sub_types) |
- |
- return False |
- |
- def _Finalize(self, parent): |
- """Fixes some parsing issues once the TypeAnnotation is complete.""" |
- |
- # Normalize functions whose definition ended up in the key type because |
- # they defined a return type after a colon. |
- if (self.key_type and |
- self.key_type.identifier == TypeAnnotation.FUNCTION_TYPE): |
- current = self.key_type |
- current.return_type = self |
- self.key_type = None |
- # opt_arg never refers to the return type but to the function itself. |
- current.opt_arg = self.opt_arg |
- self.opt_arg = False |
- return current |
- |
- # If a typedef just specified the key, it will not end up in the key type. |
- if parent.record_type and not self.key_type: |
- current = TypeAnnotation() |
- current.key_type = self |
- current.tokens.append(self) |
- return current |
- return self |
- |
- def FirstToken(self): |
- """Returns the first token used in this type or any of its subtypes.""" |
- first = self.tokens[0] |
- return first.FirstToken() if isinstance(first, TypeAnnotation) else first |
- |
- |
-def Parse(token, token_end, error_handler): |
- """Parses a type annotation and returns a TypeAnnotation object.""" |
- return TypeAnnotationParser(error_handler).Parse(token.next, token_end) |
- |
- |
-class TypeAnnotationParser(object): |
- """A parser for type annotations constructing the TypeAnnotation object.""" |
- |
- def __init__(self, error_handler): |
- self._stack = [] |
- self._error_handler = error_handler |
- self._closing_error = False |
- |
- def Parse(self, token, token_end): |
- """Parses a type annotation and returns a TypeAnnotation object.""" |
- root = TypeAnnotation() |
- self._stack.append(root) |
- current = TypeAnnotation() |
- root.tokens.append(current) |
- |
- while token and token != token_end: |
- if token.type in (TYPE.DOC_TYPE_START_BLOCK, TYPE.DOC_START_BRACE): |
- if token.string == '(': |
- if current.identifier and current.identifier not in [ |
- TypeAnnotation.FUNCTION_TYPE, TypeAnnotation.VAR_ARGS_TYPE]: |
- self.Error(token, |
- 'Invalid identifier for (): "%s"' % current.identifier) |
- current.type_group = ( |
- current.identifier != TypeAnnotation.FUNCTION_TYPE) |
- elif token.string == '{': |
- current.record_type = True |
- current.tokens.append(token) |
- self._stack.append(current) |
- current = TypeAnnotation() |
- self._stack[-1].tokens.append(current) |
- |
- elif token.type in (TYPE.DOC_TYPE_END_BLOCK, TYPE.DOC_END_BRACE): |
- prev = self._stack.pop() |
- prev.Append(current) |
- current = prev |
- |
- # If an implicit type group was created, close it as well. |
- if prev.type_group == TypeAnnotation.IMPLICIT_TYPE_GROUP: |
- prev = self._stack.pop() |
- prev.Append(current) |
- current = prev |
- current.tokens.append(token) |
- |
- elif token.type == TYPE.DOC_TYPE_MODIFIER: |
- if token.string == '!': |
- current.tokens.append(token) |
- current.not_null = True |
- elif token.string == '?': |
- current.tokens.append(token) |
- current.or_null = True |
- elif token.string == ':': |
- current.tokens.append(token) |
- prev = current |
- current = TypeAnnotation() |
- prev.tokens.append(current) |
- current.key_type = prev |
- elif token.string == '=': |
- # For implicit type groups the '=' refers to the parent. |
- try: |
- if self._stack[-1].type_group == TypeAnnotation.IMPLICIT_TYPE_GROUP: |
- self._stack[-1].tokens.append(token) |
- self._stack[-1].opt_arg = True |
- else: |
- current.tokens.append(token) |
- current.opt_arg = True |
- except IndexError: |
- self.ClosingError(token) |
- elif token.string == '|': |
- # If a type group has explicitly been opened, do a normal append. |
- # Otherwise we have to open the type group and move the current |
- # type into it, before appending |
- if not self._stack[-1].type_group: |
- type_group = TypeAnnotation() |
- if (current.key_type and |
- current.key_type.identifier != TypeAnnotation.FUNCTION_TYPE): |
- type_group.key_type = current.key_type |
- current.key_type = None |
- type_group.type_group = TypeAnnotation.IMPLICIT_TYPE_GROUP |
- # Fix the token order |
- prev = self._stack[-1].tokens.pop() |
- self._stack[-1].tokens.append(type_group) |
- type_group.tokens.append(prev) |
- self._stack.append(type_group) |
- self._stack[-1].tokens.append(token) |
- self.Append(current, error_token=token) |
- current = TypeAnnotation() |
- self._stack[-1].tokens.append(current) |
- elif token.string == ',': |
- self.Append(current, error_token=token) |
- current = TypeAnnotation() |
- self._stack[-1].tokens.append(token) |
- self._stack[-1].tokens.append(current) |
- else: |
- current.tokens.append(token) |
- self.Error(token, 'Invalid token') |
- |
- elif token.type == TYPE.COMMENT: |
- current.tokens.append(token) |
- current.identifier += token.string.strip() |
- |
- elif token.type in [TYPE.DOC_PREFIX, TYPE.WHITESPACE]: |
- current.tokens.append(token) |
- |
- else: |
- current.tokens.append(token) |
- self.Error(token, 'Unexpected token') |
- |
- token = token.next |
- |
- self.Append(current, error_token=token) |
- try: |
- ret = self._stack.pop() |
- except IndexError: |
- self.ClosingError(token) |
- # The type is screwed up, but let's return something. |
- return current |
- |
- if self._stack and (len(self._stack) != 1 or |
- ret.type_group != TypeAnnotation.IMPLICIT_TYPE_GROUP): |
- self.Error(token, 'Too many opening items.') |
- |
- return ret if len(ret.sub_types) > 1 else ret.sub_types[0] |
- |
- def Append(self, type_obj, error_token): |
- """Appends a new TypeAnnotation object to the current parent.""" |
- if self._stack: |
- self._stack[-1].Append(type_obj) |
- else: |
- self.ClosingError(error_token) |
- |
- def ClosingError(self, token): |
- """Reports an error about too many closing items, but only once.""" |
- if not self._closing_error: |
- self._closing_error = True |
- self.Error(token, 'Too many closing items.') |
- |
- def Error(self, token, message): |
- """Calls the error_handler to post an error message.""" |
- if self._error_handler: |
- self._error_handler.HandleError(error.Error( |
- errors.JSDOC_DOES_NOT_PARSE, |
- 'Error parsing jsdoc type at token "%s" (column: %d): %s' % |
- (token.string, token.start_index, message), token)) |
- |