Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(284)

Unified Diff: third_party/closure_linter/closure_linter/aliaspass.py

Issue 411243002: closure_linter: 2.3.4 => 2.3.14 (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: remove checker Created 6 years, 5 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: third_party/closure_linter/closure_linter/aliaspass.py
diff --git a/third_party/closure_linter/closure_linter/aliaspass.py b/third_party/closure_linter/closure_linter/aliaspass.py
new file mode 100644
index 0000000000000000000000000000000000000000..8854380321ef080c459abb70a3ed37d8b509594f
--- /dev/null
+++ b/third_party/closure_linter/closure_linter/aliaspass.py
@@ -0,0 +1,222 @@
+#!/usr/bin/env python
+#
+# Copyright 2012 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.
+
+"""Pass that scans for goog.scope aliases and lint/usage errors."""
+
+# Allow non-Google copyright
+# pylint: disable=g-bad-file-header
+
+__author__ = ('nnaze@google.com (Nathan Naze)')
+
+import itertools
+
+from closure_linter import ecmametadatapass
+from closure_linter import errors
+from closure_linter import javascripttokens
+from closure_linter import scopeutil
+from closure_linter import tokenutil
+from closure_linter.common import error
+
+
+# TODO(nnaze): Create a Pass interface and move this class, EcmaMetaDataPass,
+# and related classes onto it.
+
+
+def _GetAliasForIdentifier(identifier, alias_map):
+ """Returns the aliased_symbol name for an identifier.
+
+ Example usage:
+ >>> alias_map = {'MyClass': 'goog.foo.MyClass'}
+ >>> _GetAliasForIdentifier('MyClass.prototype.action', alias_map)
+ 'goog.foo.MyClass.prototype.action'
+
+ >>> _GetAliasForIdentifier('MyClass.prototype.action', {})
+ None
+
+ Args:
+ identifier: The identifier.
+ alias_map: A dictionary mapping a symbol to an alias.
+
+ Returns:
+ The aliased symbol name or None if not found.
+ """
+ ns = identifier.split('.', 1)[0]
+ aliased_symbol = alias_map.get(ns)
+ if aliased_symbol:
+ return aliased_symbol + identifier[len(ns):]
+
+
+class AliasPass(object):
+ """Pass to identify goog.scope() usages.
+
+ Identifies goog.scope() usages and finds lint/usage errors. Notes any
+ aliases of symbols in Closurized namespaces (that is, reassignments
+ such as "var MyClass = goog.foo.MyClass;") and annotates identifiers
+ when they're using an alias (so they may be expanded to the full symbol
+ later -- that "MyClass.prototype.action" refers to
+ "goog.foo.MyClass.prototype.action" when expanded.).
+ """
+
+ def __init__(self, closurized_namespaces=None, error_handler=None):
+ """Creates a new pass.
+
+ Args:
+ closurized_namespaces: A set of Closurized namespaces (e.g. 'goog').
+ error_handler: An error handler to report lint errors to.
+ """
+
+ self._error_handler = error_handler
+
+ # If we have namespaces, freeze the set.
+ if closurized_namespaces:
+ closurized_namespaces = frozenset(closurized_namespaces)
+
+ self._closurized_namespaces = closurized_namespaces
+
+ def Process(self, start_token):
+ """Runs the pass on a token stream.
+
+ Args:
+ start_token: The first token in the stream.
+ """
+
+ if start_token is None:
+ return
+
+ # TODO(nnaze): Add more goog.scope usage checks.
+ self._CheckGoogScopeCalls(start_token)
+
+ # If we have closurized namespaces, identify aliased identifiers.
+ if self._closurized_namespaces:
+ context = start_token.metadata.context
+ root_context = context.GetRoot()
+ self._ProcessRootContext(root_context)
+
+ def _CheckGoogScopeCalls(self, start_token):
+ """Check goog.scope calls for lint/usage errors."""
+
+ def IsScopeToken(token):
+ return (token.type is javascripttokens.JavaScriptTokenType.IDENTIFIER and
+ token.string == 'goog.scope')
+
+ # Find all the goog.scope tokens in the file
+ scope_tokens = [t for t in start_token if IsScopeToken(t)]
+
+ for token in scope_tokens:
+ scope_context = token.metadata.context
+
+ if not (scope_context.type == ecmametadatapass.EcmaContext.STATEMENT and
+ scope_context.parent.type == ecmametadatapass.EcmaContext.ROOT):
+ self._MaybeReportError(
+ error.Error(errors.INVALID_USE_OF_GOOG_SCOPE,
+ 'goog.scope call not in global scope', token))
+
+ # There should be only one goog.scope reference. Register errors for
+ # every instance after the first.
+ for token in scope_tokens[1:]:
+ self._MaybeReportError(
+ error.Error(errors.EXTRA_GOOG_SCOPE_USAGE,
+ 'More than one goog.scope call in file.', token))
+
+ def _MaybeReportError(self, err):
+ """Report an error to the handler (if registered)."""
+ if self._error_handler:
+ self._error_handler.HandleError(err)
+
+ @classmethod
+ def _YieldAllContexts(cls, context):
+ """Yields all contexts that are contained by the given context."""
+ yield context
+ for child_context in context.children:
+ for descendent_child in cls._YieldAllContexts(child_context):
+ yield descendent_child
+
+ @staticmethod
+ def _IsTokenInParentBlock(token, parent_block):
+ """Determines whether the given token is contained by the given block.
+
+ Args:
+ token: A token
+ parent_block: An EcmaContext.
+
+ Returns:
+ Whether the token is in a context that is or is a child of the given
+ parent_block context.
+ """
+ context = token.metadata.context
+
+ while context:
+ if context is parent_block:
+ return True
+ context = context.parent
+
+ return False
+
+ def _ProcessRootContext(self, root_context):
+ """Processes all goog.scope blocks under the root context."""
+
+ assert root_context.type is ecmametadatapass.EcmaContext.ROOT
+
+ # Identify all goog.scope blocks.
+ goog_scope_blocks = itertools.ifilter(
+ scopeutil.IsGoogScopeBlock,
+ self._YieldAllContexts(root_context))
+
+ # Process each block to find aliases.
+ for scope_block in goog_scope_blocks:
+ self._ProcessGoogScopeBlock(scope_block)
+
+ def _ProcessGoogScopeBlock(self, scope_block):
+ """Scans a goog.scope block to find aliases and mark alias tokens."""
+
+ alias_map = dict()
+
+ # Iterate over every token in the scope_block. Each token points to one
+ # context, but multiple tokens may point to the same context. We only want
+ # to check each context once, so keep track of those we've seen.
+ seen_contexts = set()
+ token = scope_block.start_token
+ while token and self._IsTokenInParentBlock(token, scope_block):
+
+ token_context = token.metadata.context
+
+ # Check to see if this token is an alias.
+ if token_context not in seen_contexts:
+ seen_contexts.add(token_context)
+
+ # If this is a alias statement in the goog.scope block.
+ if (token_context.type == ecmametadatapass.EcmaContext.VAR and
+ token_context.parent.parent is scope_block):
+ match = scopeutil.MatchAlias(token_context)
+
+ # If this is an alias, remember it in the map.
+ if match:
+ alias, symbol = match
+ symbol = _GetAliasForIdentifier(symbol, alias_map) or symbol
+ if scopeutil.IsInClosurizedNamespace(symbol,
+ self._closurized_namespaces):
+ alias_map[alias] = symbol
+
+ # If this token is an identifier that matches an alias,
+ # mark the token as an alias to the original symbol.
+ if (token.type is javascripttokens.JavaScriptTokenType.SIMPLE_LVALUE or
+ token.type is javascripttokens.JavaScriptTokenType.IDENTIFIER):
+ identifier = tokenutil.GetIdentifierForToken(token)
+ if identifier:
+ aliased_symbol = _GetAliasForIdentifier(identifier, alias_map)
+ if aliased_symbol:
+ token.metadata.aliased_symbol = aliased_symbol
+
+ token = token.next # Get next token
« no previous file with comments | « third_party/closure_linter/README.chromium ('k') | third_party/closure_linter/closure_linter/aliaspass_test.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698