aboutsummaryrefslogtreecommitdiff
path: root/tools/closure_linter-2.3.4/closure_linter/closurizednamespacesinfo.py
diff options
context:
space:
mode:
Diffstat (limited to 'tools/closure_linter-2.3.4/closure_linter/closurizednamespacesinfo.py')
-rwxr-xr-xtools/closure_linter-2.3.4/closure_linter/closurizednamespacesinfo.py498
1 files changed, 0 insertions, 498 deletions
diff --git a/tools/closure_linter-2.3.4/closure_linter/closurizednamespacesinfo.py b/tools/closure_linter-2.3.4/closure_linter/closurizednamespacesinfo.py
deleted file mode 100755
index 065b5cc..0000000
--- a/tools/closure_linter-2.3.4/closure_linter/closurizednamespacesinfo.py
+++ /dev/null
@@ -1,498 +0,0 @@
-#!/usr/bin/env python
-#
-# Copyright 2008 The Closure Linter Authors. All Rights Reserved.
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS-IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-"""Logic for computing dependency information for closurized JavaScript files.
-
-Closurized JavaScript files express dependencies using goog.require and
-goog.provide statements. In order for the linter to detect when a statement is
-missing or unnecessary, all identifiers in the JavaScript file must first be
-processed to determine if they constitute the creation or usage of a dependency.
-"""
-
-
-
-from closure_linter import javascripttokens
-from closure_linter import tokenutil
-
-# pylint: disable-msg=C6409
-TokenType = javascripttokens.JavaScriptTokenType
-
-DEFAULT_EXTRA_NAMESPACES = [
- 'goog.testing.asserts',
- 'goog.testing.jsunit',
-]
-
-class ClosurizedNamespacesInfo(object):
- """Dependency information for closurized JavaScript files.
-
- Processes token streams for dependency creation or usage and provides logic
- for determining if a given require or provide statement is unnecessary or if
- there are missing require or provide statements.
- """
-
- def __init__(self, closurized_namespaces, ignored_extra_namespaces):
- """Initializes an instance the ClosurizedNamespacesInfo class.
-
- Args:
- closurized_namespaces: A list of namespace prefixes that should be
- processed for dependency information. Non-matching namespaces are
- ignored.
- ignored_extra_namespaces: A list of namespaces that should not be reported
- as extra regardless of whether they are actually used.
- """
- self._closurized_namespaces = closurized_namespaces
- self._ignored_extra_namespaces = (ignored_extra_namespaces +
- DEFAULT_EXTRA_NAMESPACES)
- self.Reset()
-
- def Reset(self):
- """Resets the internal state to prepare for processing a new file."""
-
- # A list of goog.provide tokens in the order they appeared in the file.
- self._provide_tokens = []
-
- # A list of goog.require tokens in the order they appeared in the file.
- self._require_tokens = []
-
- # Namespaces that are already goog.provided.
- self._provided_namespaces = []
-
- # Namespaces that are already goog.required.
- self._required_namespaces = []
-
- # Note that created_namespaces and used_namespaces contain both namespaces
- # and identifiers because there are many existing cases where a method or
- # constant is provided directly instead of its namespace. Ideally, these
- # two lists would only have to contain namespaces.
-
- # A list of tuples where the first element is the namespace of an identifier
- # created in the file and the second is the identifier itself.
- self._created_namespaces = []
-
- # A list of tuples where the first element is the namespace of an identifier
- # used in the file and the second is the identifier itself.
- self._used_namespaces = []
-
- # A list of seemingly-unnecessary namespaces that are goog.required() and
- # annotated with @suppress {extraRequire}.
- self._suppressed_requires = []
-
- # A list of goog.provide tokens which are duplicates.
- self._duplicate_provide_tokens = []
-
- # A list of goog.require tokens which are duplicates.
- self._duplicate_require_tokens = []
-
- # Whether this file is in a goog.scope. Someday, we may add support
- # for checking scopified namespaces, but for now let's just fail
- # in a more reasonable way.
- self._scopified_file = False
-
- # TODO(user): Handle the case where there are 2 different requires
- # that can satisfy the same dependency, but only one is necessary.
-
- def GetProvidedNamespaces(self):
- """Returns the namespaces which are already provided by this file.
-
- Returns:
- A list of strings where each string is a 'namespace' corresponding to an
- existing goog.provide statement in the file being checked.
- """
- return list(self._provided_namespaces)
-
- def GetRequiredNamespaces(self):
- """Returns the namespaces which are already required by this file.
-
- Returns:
- A list of strings where each string is a 'namespace' corresponding to an
- existing goog.require statement in the file being checked.
- """
- return list(self._required_namespaces)
-
- def IsExtraProvide(self, token):
- """Returns whether the given goog.provide token is unnecessary.
-
- Args:
- token: A goog.provide token.
-
- Returns:
- True if the given token corresponds to an unnecessary goog.provide
- statement, otherwise False.
- """
- if self._scopified_file:
- return False
-
- namespace = tokenutil.Search(token, TokenType.STRING_TEXT).string
-
- base_namespace = namespace.split('.', 1)[0]
- if base_namespace not in self._closurized_namespaces:
- return False
-
- if token in self._duplicate_provide_tokens:
- return True
-
- # TODO(user): There's probably a faster way to compute this.
- for created_namespace, created_identifier in self._created_namespaces:
- if namespace == created_namespace or namespace == created_identifier:
- return False
-
- return True
-
- def IsExtraRequire(self, token):
- """Returns whether the given goog.require token is unnecessary.
-
- Args:
- token: A goog.require token.
-
- Returns:
- True if the given token corresponds to an unnecessary goog.require
- statement, otherwise False.
- """
- if self._scopified_file:
- return False
-
- namespace = tokenutil.Search(token, TokenType.STRING_TEXT).string
-
- base_namespace = namespace.split('.', 1)[0]
- if base_namespace not in self._closurized_namespaces:
- return False
-
- if namespace in self._ignored_extra_namespaces:
- return False
-
- if token in self._duplicate_require_tokens:
- return True
-
- if namespace in self._suppressed_requires:
- return False
-
- # If the namespace contains a component that is initial caps, then that
- # must be the last component of the namespace.
- parts = namespace.split('.')
- if len(parts) > 1 and parts[-2][0].isupper():
- return True
-
- # TODO(user): There's probably a faster way to compute this.
- for used_namespace, used_identifier in self._used_namespaces:
- if namespace == used_namespace or namespace == used_identifier:
- return False
-
- return True
-
- def GetMissingProvides(self):
- """Returns the set of missing provided namespaces for the current file.
-
- Returns:
- Returns a set of strings where each string is a namespace that should be
- provided by this file, but is not.
- """
- if self._scopified_file:
- return set()
-
- missing_provides = set()
- for namespace, identifier in self._created_namespaces:
- if (not self._IsPrivateIdentifier(identifier) and
- namespace not in self._provided_namespaces and
- identifier not in self._provided_namespaces and
- namespace not in self._required_namespaces):
- missing_provides.add(namespace)
-
- return missing_provides
-
- def GetMissingRequires(self):
- """Returns the set of missing required namespaces for the current file.
-
- For each non-private identifier used in the file, find either a
- goog.require, goog.provide or a created identifier that satisfies it.
- goog.require statements can satisfy the identifier by requiring either the
- namespace of the identifier or the identifier itself. goog.provide
- statements can satisfy the identifier by providing the namespace of the
- identifier. A created identifier can only satisfy the used identifier if
- it matches it exactly (necessary since things can be defined on a
- namespace in more than one file). Note that provided namespaces should be
- a subset of created namespaces, but we check both because in some cases we
- can't always detect the creation of the namespace.
-
- Returns:
- Returns a set of strings where each string is a namespace that should be
- required by this file, but is not.
- """
- if self._scopified_file:
- return set()
-
- external_dependencies = set(self._required_namespaces)
-
- # Assume goog namespace is always available.
- external_dependencies.add('goog')
-
- created_identifiers = set()
- for namespace, identifier in self._created_namespaces:
- created_identifiers.add(identifier)
-
- missing_requires = set()
- for namespace, identifier in self._used_namespaces:
- if (not self._IsPrivateIdentifier(identifier) and
- namespace not in external_dependencies and
- namespace not in self._provided_namespaces and
- identifier not in external_dependencies and
- identifier not in created_identifiers):
- missing_requires.add(namespace)
-
- return missing_requires
-
- def _IsPrivateIdentifier(self, identifier):
- """Returns whether the given identifer is private."""
- pieces = identifier.split('.')
- for piece in pieces:
- if piece.endswith('_'):
- return True
- return False
-
- def IsFirstProvide(self, token):
- """Returns whether token is the first provide token."""
- return self._provide_tokens and token == self._provide_tokens[0]
-
- def IsFirstRequire(self, token):
- """Returns whether token is the first require token."""
- return self._require_tokens and token == self._require_tokens[0]
-
- def IsLastProvide(self, token):
- """Returns whether token is the last provide token."""
- return self._provide_tokens and token == self._provide_tokens[-1]
-
- def IsLastRequire(self, token):
- """Returns whether token is the last require token."""
- return self._require_tokens and token == self._require_tokens[-1]
-
- def ProcessToken(self, token, state_tracker):
- """Processes the given token for dependency information.
-
- Args:
- token: The token to process.
- state_tracker: The JavaScript state tracker.
- """
-
- # Note that this method is in the critical path for the linter and has been
- # optimized for performance in the following ways:
- # - Tokens are checked by type first to minimize the number of function
- # calls necessary to determine if action needs to be taken for the token.
- # - The most common tokens types are checked for first.
- # - The number of function calls has been minimized (thus the length of this
- # function.
-
- if token.type == TokenType.IDENTIFIER:
- # TODO(user): Consider saving the whole identifier in metadata.
- whole_identifier_string = self._GetWholeIdentifierString(token)
- if whole_identifier_string is None:
- # We only want to process the identifier one time. If the whole string
- # identifier is None, that means this token was part of a multi-token
- # identifier, but it was not the first token of the identifier.
- return
-
- # In the odd case that a goog.require is encountered inside a function,
- # just ignore it (e.g. dynamic loading in test runners).
- if token.string == 'goog.require' and not state_tracker.InFunction():
- self._require_tokens.append(token)
- namespace = tokenutil.Search(token, TokenType.STRING_TEXT).string
- if namespace in self._required_namespaces:
- self._duplicate_require_tokens.append(token)
- else:
- self._required_namespaces.append(namespace)
-
- # If there is a suppression for the require, add a usage for it so it
- # gets treated as a regular goog.require (i.e. still gets sorted).
- jsdoc = state_tracker.GetDocComment()
- if jsdoc and ('extraRequire' in jsdoc.suppressions):
- self._suppressed_requires.append(namespace)
- self._AddUsedNamespace(state_tracker, namespace)
-
- elif token.string == 'goog.provide':
- self._provide_tokens.append(token)
- namespace = tokenutil.Search(token, TokenType.STRING_TEXT).string
- if namespace in self._provided_namespaces:
- self._duplicate_provide_tokens.append(token)
- else:
- self._provided_namespaces.append(namespace)
-
- # If there is a suppression for the provide, add a creation for it so it
- # gets treated as a regular goog.provide (i.e. still gets sorted).
- jsdoc = state_tracker.GetDocComment()
- if jsdoc and ('extraProvide' in jsdoc.suppressions):
- self._AddCreatedNamespace(state_tracker, namespace)
-
- elif token.string == 'goog.scope':
- self._scopified_file = True
-
- else:
- jsdoc = state_tracker.GetDocComment()
- if jsdoc and jsdoc.HasFlag('typedef'):
- self._AddCreatedNamespace(state_tracker, whole_identifier_string)
- else:
- self._AddUsedNamespace(state_tracker, whole_identifier_string)
-
- elif token.type == TokenType.SIMPLE_LVALUE:
- identifier = token.values['identifier']
- namespace = self._GetClosurizedNamespace(identifier)
- if state_tracker.InFunction():
- self._AddUsedNamespace(state_tracker, identifier)
- elif namespace and namespace != 'goog':
- self._AddCreatedNamespace(state_tracker, identifier, namespace)
-
- elif token.type == TokenType.DOC_FLAG:
- flag_type = token.attached_object.flag_type
- is_interface = state_tracker.GetDocComment().HasFlag('interface')
- if flag_type == 'implements' or (flag_type == 'extends' and is_interface):
- # Interfaces should be goog.require'd.
- doc_start = tokenutil.Search(token, TokenType.DOC_START_BRACE)
- interface = tokenutil.Search(doc_start, TokenType.COMMENT)
- self._AddUsedNamespace(state_tracker, interface.string)
-
-
- def _GetWholeIdentifierString(self, token):
- """Returns the whole identifier string for the given token.
-
- Checks the tokens after the current one to see if the token is one in a
- sequence of tokens which are actually just one identifier (i.e. a line was
- wrapped in the middle of an identifier).
-
- Args:
- token: The token to check.
-
- Returns:
- The whole identifier string or None if this token is not the first token
- in a multi-token identifier.
- """
- result = ''
-
- # Search backward to determine if this token is the first token of the
- # identifier. If it is not the first token, return None to signal that this
- # token should be ignored.
- prev_token = token.previous
- while prev_token:
- if (prev_token.IsType(TokenType.IDENTIFIER) or
- prev_token.IsType(TokenType.NORMAL) and prev_token.string == '.'):
- return None
- elif (not prev_token.IsType(TokenType.WHITESPACE) and
- not prev_token.IsAnyType(TokenType.COMMENT_TYPES)):
- break
- prev_token = prev_token.previous
-
- # Search forward to find other parts of this identifier separated by white
- # space.
- next_token = token
- while next_token:
- if (next_token.IsType(TokenType.IDENTIFIER) or
- next_token.IsType(TokenType.NORMAL) and next_token.string == '.'):
- result += next_token.string
- elif (not next_token.IsType(TokenType.WHITESPACE) and
- not next_token.IsAnyType(TokenType.COMMENT_TYPES)):
- break
- next_token = next_token.next
-
- return result
-
- def _AddCreatedNamespace(self, state_tracker, identifier, namespace=None):
- """Adds the namespace of an identifier to the list of created namespaces.
-
- If the identifier is annotated with a 'missingProvide' suppression, it is
- not added.
-
- Args:
- state_tracker: The JavaScriptStateTracker instance.
- identifier: The identifier to add.
- namespace: The namespace of the identifier or None if the identifier is
- also the namespace.
- """
- if not namespace:
- namespace = identifier
-
- jsdoc = state_tracker.GetDocComment()
- if jsdoc and 'missingProvide' in jsdoc.suppressions:
- return
-
- self._created_namespaces.append([namespace, identifier])
-
- def _AddUsedNamespace(self, state_tracker, identifier):
- """Adds the namespace of an identifier to the list of used namespaces.
-
- If the identifier is annotated with a 'missingRequire' suppression, it is
- not added.
-
- Args:
- state_tracker: The JavaScriptStateTracker instance.
- identifier: An identifier which has been used.
- """
- jsdoc = state_tracker.GetDocComment()
- if jsdoc and 'missingRequire' in jsdoc.suppressions:
- return
-
- namespace = self._GetClosurizedNamespace(identifier)
- if namespace:
- self._used_namespaces.append([namespace, identifier])
-
- def _GetClosurizedNamespace(self, identifier):
- """Given an identifier, returns the namespace that identifier is from.
-
- Args:
- identifier: The identifier to extract a namespace from.
-
- Returns:
- The namespace the given identifier resides in, or None if one could not
- be found.
- """
- if identifier.startswith('goog.global'):
- # Ignore goog.global, since it is, by definition, global.
- return None
-
- parts = identifier.split('.')
- for namespace in self._closurized_namespaces:
- if not identifier.startswith(namespace + '.'):
- continue
-
- last_part = parts[-1]
- if not last_part:
- # TODO(robbyw): Handle this: it's a multi-line identifier.
- return None
-
- # The namespace for a class is the shortest prefix ending in a class
- # name, which starts with a capital letter but is not a capitalized word.
- #
- # We ultimately do not want to allow requiring or providing of inner
- # classes/enums. Instead, a file should provide only the top-level class
- # and users should require only that.
- namespace = []
- for part in parts:
- if part == 'prototype' or part.isupper():
- return '.'.join(namespace)
- namespace.append(part)
- if part[0].isupper():
- return '.'.join(namespace)
-
- # At this point, we know there's no class or enum, so the namespace is
- # just the identifier with the last part removed. With the exception of
- # apply, inherits, and call, which should also be stripped.
- if parts[-1] in ('apply', 'inherits', 'call'):
- parts.pop()
- parts.pop()
-
- # If the last part ends with an underscore, it is a private variable,
- # method, or enum. The namespace is whatever is before it.
- if parts and parts[-1].endswith('_'):
- parts.pop()
-
- return '.'.join(parts)
-
- return None