Index: third_party/closure_linter/closure_linter/javascriptlintrules.py |
diff --git a/third_party/closure_linter/closure_linter/javascriptlintrules.py b/third_party/closure_linter/closure_linter/javascriptlintrules.py |
index d72fd56a54d56cbbdcf4a61ec134748775cc6b04..6406ff799b63b81cf36a31d820aec740db041445 100755 |
--- a/third_party/closure_linter/closure_linter/javascriptlintrules.py |
+++ b/third_party/closure_linter/closure_linter/javascriptlintrules.py |
@@ -1,5 +1,4 @@ |
#!/usr/bin/env python |
-# |
# Copyright 2011 The Closure Linter Authors. All Rights Reserved. |
# |
# Licensed under the Apache License, Version 2.0 (the "License"); |
@@ -25,7 +24,7 @@ __author__ = ('robbyw@google.com (Robert Walker)', |
'jacobr@google.com (Jacob Richman)') |
import re |
-from sets import Set |
+ |
from closure_linter import ecmalintrules |
from closure_linter import error_check |
from closure_linter import errors |
@@ -51,8 +50,12 @@ class JavaScriptLintRules(ecmalintrules.EcmaScriptLintRules): |
ecmalintrules.EcmaScriptLintRules.__init__(self) |
self._namespaces_info = namespaces_info |
self._declared_private_member_tokens = {} |
- self._declared_private_members = Set() |
- self._used_private_members = Set() |
+ self._declared_private_members = set() |
+ self._used_private_members = set() |
+ # A stack of dictionaries, one for each function scope entered. Each |
+ # dictionary is keyed by an identifier that defines a local variable and has |
+ # a token as its value. |
+ self._unused_local_variables_by_scope = [] |
def HandleMissingParameterDoc(self, token, param_name): |
"""Handle errors associated with a parameter missing a param tag.""" |
@@ -75,6 +78,7 @@ class JavaScriptLintRules(ecmalintrules.EcmaScriptLintRules): |
token.attached_object.type is not None and |
token.attached_object.type.find('{') != token.string.rfind('{')) |
+ # pylint: disable=too-many-statements |
def CheckToken(self, token, state): |
"""Checks a token, given the current parser_state, for warnings and errors. |
@@ -82,7 +86,10 @@ class JavaScriptLintRules(ecmalintrules.EcmaScriptLintRules): |
token: The current token under consideration |
state: parser_state object that indicates the current state in the page |
""" |
- if self.__ContainsRecordType(token): |
+ |
+ # For @param don't ignore record type. |
+ if (self.__ContainsRecordType(token) and |
+ token.attached_object.flag_type != 'param'): |
# We should bail out and not emit any warnings for this annotation. |
# TODO(nicksantos): Support record types for real. |
state.GetDocComment().Invalidate() |
@@ -94,6 +101,9 @@ class JavaScriptLintRules(ecmalintrules.EcmaScriptLintRules): |
# Store some convenience variables |
namespaces_info = self._namespaces_info |
+ if error_check.ShouldCheck(Rule.UNUSED_LOCAL_VARIABLES): |
+ self._CheckUnusedLocalVariables(token, state) |
+ |
if error_check.ShouldCheck(Rule.UNUSED_PRIVATE_MEMBERS): |
# Find all assignments to private members. |
if token.type == Type.SIMPLE_LVALUE: |
@@ -101,14 +111,20 @@ class JavaScriptLintRules(ecmalintrules.EcmaScriptLintRules): |
if identifier.endswith('_') and not identifier.endswith('__'): |
doc_comment = state.GetDocComment() |
suppressed = (doc_comment and doc_comment.HasFlag('suppress') and |
- doc_comment.GetFlag('suppress').type == 'underscore') |
+ (doc_comment.GetFlag('suppress').type == 'underscore' or |
+ doc_comment.GetFlag('suppress').type == |
+ 'unusedPrivateMembers')) |
if not suppressed: |
# Look for static members defined on a provided namespace. |
- namespace = namespaces_info.GetClosurizedNamespace(identifier) |
- provided_namespaces = namespaces_info.GetProvidedNamespaces() |
+ if namespaces_info: |
+ namespace = namespaces_info.GetClosurizedNamespace(identifier) |
+ provided_namespaces = namespaces_info.GetProvidedNamespaces() |
+ else: |
+ namespace = None |
+ provided_namespaces = set() |
# Skip cases of this.something_.somethingElse_. |
- regex = re.compile('^this\.[a-zA-Z_]+$') |
+ regex = re.compile(r'^this\.[a-zA-Z_]+$') |
if namespace in provided_namespaces or regex.match(identifier): |
variable = identifier.split('.')[-1] |
self._declared_private_member_tokens[variable] = token |
@@ -132,27 +148,45 @@ class JavaScriptLintRules(ecmalintrules.EcmaScriptLintRules): |
self._CheckForMissingSpaceBeforeToken( |
token.attached_object.name_token) |
- if (error_check.ShouldCheck(Rule.OPTIONAL_TYPE_MARKER) and |
- flag.type is not None and flag.name is not None): |
- # Check for optional marker in type. |
- if (flag.type.endswith('=') and |
- not flag.name.startswith('opt_')): |
- self._HandleError(errors.JSDOC_MISSING_OPTIONAL_PREFIX, |
- 'Optional parameter name %s must be prefixed ' |
- 'with opt_.' % flag.name, |
- token) |
- elif (not flag.type.endswith('=') and |
- flag.name.startswith('opt_')): |
- self._HandleError(errors.JSDOC_MISSING_OPTIONAL_TYPE, |
- 'Optional parameter %s type must end with =.' % |
- flag.name, |
- token) |
+ if flag.type is not None and flag.name is not None: |
+ if error_check.ShouldCheck(Rule.VARIABLE_ARG_MARKER): |
+ # Check for variable arguments marker in type. |
+ if (flag.type.startswith('...') and |
+ flag.name != 'var_args'): |
+ self._HandleError(errors.JSDOC_MISSING_VAR_ARGS_NAME, |
+ 'Variable length argument %s must be renamed ' |
+ 'to var_args.' % flag.name, |
+ token) |
+ elif (not flag.type.startswith('...') and |
+ flag.name == 'var_args'): |
+ self._HandleError(errors.JSDOC_MISSING_VAR_ARGS_TYPE, |
+ 'Variable length argument %s type must start ' |
+ 'with \'...\'.' % flag.name, |
+ token) |
+ |
+ if error_check.ShouldCheck(Rule.OPTIONAL_TYPE_MARKER): |
+ # Check for optional marker in type. |
+ if (flag.type.endswith('=') and |
+ not flag.name.startswith('opt_')): |
+ self._HandleError(errors.JSDOC_MISSING_OPTIONAL_PREFIX, |
+ 'Optional parameter name %s must be prefixed ' |
+ 'with opt_.' % flag.name, |
+ token) |
+ elif (not flag.type.endswith('=') and |
+ flag.name.startswith('opt_')): |
+ self._HandleError(errors.JSDOC_MISSING_OPTIONAL_TYPE, |
+ 'Optional parameter %s type must end with =.' % |
+ flag.name, |
+ token) |
if flag.flag_type in state.GetDocFlag().HAS_TYPE: |
# Check for both missing type token and empty type braces '{}' |
- # Missing suppress types are reported separately and we allow enums |
- # without types. |
- if (flag.flag_type not in ('suppress', 'enum') and |
+ # Missing suppress types are reported separately and we allow enums, |
+ # const, private, public and protected without types. |
+ allowed_flags = set(['suppress']).union( |
+ state.GetDocFlag().CAN_OMIT_TYPE) |
+ |
+ if (flag.flag_type not in allowed_flags and |
(not flag.type or flag.type.isspace())): |
self._HandleError(errors.MISSING_JSDOC_TAG_TYPE, |
'Missing type in %s tag' % token.string, token) |
@@ -176,7 +210,7 @@ class JavaScriptLintRules(ecmalintrules.EcmaScriptLintRules): |
errors.UNNECESSARY_DOUBLE_QUOTED_STRING, |
'Single-quoted string preferred over double-quoted string.', |
token, |
- Position.All(token.string)) |
+ position=Position.All(token.string)) |
elif token.type == Type.END_DOC_COMMENT: |
doc_comment = state.GetDocComment() |
@@ -187,13 +221,19 @@ class JavaScriptLintRules(ecmalintrules.EcmaScriptLintRules): |
self._SetLimitedDocChecks(True) |
if (error_check.ShouldCheck(Rule.BLANK_LINES_AT_TOP_LEVEL) and |
- not self._is_html and state.InTopLevel() and not state.InBlock()): |
+ not self._is_html and |
+ state.InTopLevel() and |
+ not state.InNonScopeBlock()): |
# Check if we're in a fileoverview or constructor JsDoc. |
is_constructor = ( |
doc_comment.HasFlag('constructor') or |
doc_comment.HasFlag('interface')) |
- is_file_overview = doc_comment.HasFlag('fileoverview') |
+ # @fileoverview is an optional tag so if the dosctring is the first |
+ # token in the file treat it as a file level docstring. |
+ is_file_level_comment = ( |
+ doc_comment.HasFlag('fileoverview') or |
+ not doc_comment.start_token.previous) |
# If the comment is not a file overview, and it does not immediately |
# precede some code, skip it. |
@@ -201,7 +241,8 @@ class JavaScriptLintRules(ecmalintrules.EcmaScriptLintRules): |
# behavior at the top of a file. |
next_token = token.next |
if (not next_token or |
- (not is_file_overview and next_token.type in Type.NON_CODE_TYPES)): |
+ (not is_file_level_comment and |
+ next_token.type in Type.NON_CODE_TYPES)): |
return |
# Don't require extra blank lines around suppression of extra |
@@ -214,7 +255,7 @@ class JavaScriptLintRules(ecmalintrules.EcmaScriptLintRules): |
# Find the start of this block (include comments above the block, unless |
# this is a file overview). |
block_start = doc_comment.start_token |
- if not is_file_overview: |
+ if not is_file_level_comment: |
token = block_start.previous |
while token and token.type in Type.COMMENT_TYPES: |
block_start = token |
@@ -236,22 +277,25 @@ class JavaScriptLintRules(ecmalintrules.EcmaScriptLintRules): |
error_message = False |
expected_blank_lines = 0 |
- if is_file_overview and blank_lines == 0: |
+ # Only need blank line before file overview if it is not the beginning |
+ # of the file, e.g. copyright is first. |
+ if is_file_level_comment and blank_lines == 0 and block_start.previous: |
error_message = 'Should have a blank line before a file overview.' |
expected_blank_lines = 1 |
elif is_constructor and blank_lines != 3: |
error_message = ( |
'Should have 3 blank lines before a constructor/interface.') |
expected_blank_lines = 3 |
- elif not is_file_overview and not is_constructor and blank_lines != 2: |
+ elif (not is_file_level_comment and not is_constructor and |
+ blank_lines != 2): |
error_message = 'Should have 2 blank lines between top-level blocks.' |
expected_blank_lines = 2 |
if error_message: |
self._HandleError( |
errors.WRONG_BLANK_LINE_COUNT, error_message, |
- block_start, Position.AtBeginning(), |
- expected_blank_lines - blank_lines) |
+ block_start, position=Position.AtBeginning(), |
+ fix_data=expected_blank_lines - blank_lines) |
elif token.type == Type.END_BLOCK: |
if state.InFunction() and state.IsFunctionClose(): |
@@ -269,7 +313,7 @@ class JavaScriptLintRules(ecmalintrules.EcmaScriptLintRules): |
self._HandleError( |
errors.MISSING_RETURN_DOCUMENTATION, |
'Missing @return JsDoc in function with non-trivial return', |
- function.doc.end_token, Position.AtBeginning()) |
+ function.doc.end_token, position=Position.AtBeginning()) |
elif (not function.has_return and |
not function.has_throw and |
function.doc and |
@@ -283,23 +327,63 @@ class JavaScriptLintRules(ecmalintrules.EcmaScriptLintRules): |
self._HandleError( |
errors.UNNECESSARY_RETURN_DOCUMENTATION, |
'Found @return JsDoc on function that returns nothing', |
- return_flag.flag_token, Position.AtBeginning()) |
+ return_flag.flag_token, position=Position.AtBeginning()) |
+ |
+ # b/4073735. Method in object literal definition of prototype can |
+ # safely reference 'this'. |
+ prototype_object_literal = False |
+ block_start = None |
+ previous_code = None |
+ previous_previous_code = None |
+ |
+ # Search for cases where prototype is defined as object literal. |
+ # previous_previous_code |
+ # | previous_code |
+ # | | block_start |
+ # | | | |
+ # a.b.prototype = { |
+ # c : function() { |
+ # this.d = 1; |
+ # } |
+ # } |
+ |
+ # If in object literal, find first token of block so to find previous |
+ # tokens to check above condition. |
+ if state.InObjectLiteral(): |
+ block_start = state.GetCurrentBlockStart() |
+ |
+ # If an object literal then get previous token (code type). For above |
+ # case it should be '='. |
+ if block_start: |
+ previous_code = tokenutil.SearchExcept(block_start, |
+ Type.NON_CODE_TYPES, |
+ reverse=True) |
+ |
+ # If previous token to block is '=' then get its previous token. |
+ if previous_code and previous_code.IsOperator('='): |
+ previous_previous_code = tokenutil.SearchExcept(previous_code, |
+ Type.NON_CODE_TYPES, |
+ reverse=True) |
+ |
+ # If variable/token before '=' ends with '.prototype' then its above |
+ # case of prototype defined with object literal. |
+ prototype_object_literal = (previous_previous_code and |
+ previous_previous_code.string.endswith( |
+ '.prototype')) |
- if state.InFunction() and state.IsFunctionClose(): |
- is_immediately_called = (token.next and |
- token.next.type == Type.START_PAREN) |
if (function.has_this and function.doc and |
not function.doc.HasFlag('this') and |
not function.is_constructor and |
not function.is_interface and |
- '.prototype.' not in function.name): |
+ '.prototype.' not in function.name and |
+ not prototype_object_literal): |
self._HandleError( |
errors.MISSING_JSDOC_TAG_THIS, |
'Missing @this JsDoc in function referencing "this". (' |
'this usually means you are trying to reference "this" in ' |
'a static function, or you have forgotten to mark a ' |
'constructor with @constructor)', |
- function.doc.end_token, Position.AtBeginning()) |
+ function.doc.end_token, position=Position.AtBeginning()) |
elif token.type == Type.IDENTIFIER: |
if token.string == 'goog.inherits' and not state.InFunction(): |
@@ -308,7 +392,7 @@ class JavaScriptLintRules(ecmalintrules.EcmaScriptLintRules): |
errors.MISSING_LINE, |
'Missing newline between constructor and goog.inherits', |
token, |
- Position.AtBeginning()) |
+ position=Position.AtBeginning()) |
extra_space = state.GetLastNonSpaceToken().next |
while extra_space != token: |
@@ -325,13 +409,23 @@ class JavaScriptLintRules(ecmalintrules.EcmaScriptLintRules): |
elif (token.string == 'goog.provide' and |
not state.InFunction() and |
namespaces_info is not None): |
- namespace = tokenutil.Search(token, Type.STRING_TEXT).string |
+ namespace = tokenutil.GetStringAfterToken(token) |
# Report extra goog.provide statement. |
- if namespaces_info.IsExtraProvide(token): |
+ if not namespace or namespaces_info.IsExtraProvide(token): |
+ if not namespace: |
+ msg = 'Empty namespace in goog.provide' |
+ else: |
+ msg = 'Unnecessary goog.provide: ' + namespace |
+ |
+ # Hint to user if this is a Test namespace. |
+ if namespace.endswith('Test'): |
+ msg += (' *Test namespaces must be mentioned in the ' |
+ 'goog.setTestOnly() call') |
+ |
self._HandleError( |
errors.EXTRA_GOOG_PROVIDE, |
- 'Unnecessary goog.provide: ' + namespace, |
+ msg, |
token, position=Position.AtBeginning()) |
if namespaces_info.IsLastProvide(token): |
@@ -356,7 +450,7 @@ class JavaScriptLintRules(ecmalintrules.EcmaScriptLintRules): |
elif (token.string == 'goog.require' and |
not state.InFunction() and |
namespaces_info is not None): |
- namespace = tokenutil.Search(token, Type.STRING_TEXT).string |
+ namespace = tokenutil.GetStringAfterToken(token) |
# If there are no provide statements, missing provides should be |
# reported before the first require. |
@@ -370,10 +464,15 @@ class JavaScriptLintRules(ecmalintrules.EcmaScriptLintRules): |
True) |
# Report extra goog.require statement. |
- if namespaces_info.IsExtraRequire(token): |
+ if not namespace or namespaces_info.IsExtraRequire(token): |
+ if not namespace: |
+ msg = 'Empty namespace in goog.require' |
+ else: |
+ msg = 'Unnecessary goog.require: ' + namespace |
+ |
self._HandleError( |
errors.EXTRA_GOOG_REQUIRE, |
- 'Unnecessary goog.require: ' + namespace, |
+ msg, |
token, position=Position.AtBeginning()) |
# Report missing goog.require statements. |
@@ -395,14 +494,14 @@ class JavaScriptLintRules(ecmalintrules.EcmaScriptLintRules): |
if (not token.metadata.IsUnaryOperator() and not last_in_line |
and not token.next.IsComment() |
and not token.next.IsOperator(',') |
- and not token.next.type in (Type.WHITESPACE, Type.END_PAREN, |
+ and token.next.type not in (Type.WHITESPACE, Type.END_PAREN, |
Type.END_BRACKET, Type.SEMICOLON, |
Type.START_BRACKET)): |
self._HandleError( |
errors.MISSING_SPACE, |
'Missing space after "%s"' % token.string, |
token, |
- Position.AtEnd(token.string)) |
+ position=Position.AtEnd(token.string)) |
elif token.type == Type.WHITESPACE: |
first_in_line = token.IsFirstInLine() |
last_in_line = token.IsLastInLine() |
@@ -417,52 +516,152 @@ class JavaScriptLintRules(ecmalintrules.EcmaScriptLintRules): |
errors.EXTRA_SPACE, |
'Extra space after "%s"' % token.previous.string, |
token, |
- Position.All(token.string)) |
+ position=Position.All(token.string)) |
+ elif token.type == Type.SEMICOLON: |
+ previous_token = tokenutil.SearchExcept(token, Type.NON_CODE_TYPES, |
+ reverse=True) |
+ if not previous_token: |
+ self._HandleError( |
+ errors.REDUNDANT_SEMICOLON, |
+ 'Semicolon without any statement', |
+ token, |
+ position=Position.AtEnd(token.string)) |
+ elif (previous_token.type == Type.KEYWORD and |
+ previous_token.string not in ['break', 'continue', 'return']): |
+ self._HandleError( |
+ errors.REDUNDANT_SEMICOLON, |
+ ('Semicolon after \'%s\' without any statement.' |
+ ' Looks like an error.' % previous_token.string), |
+ token, |
+ position=Position.AtEnd(token.string)) |
+ |
+ def _CheckUnusedLocalVariables(self, token, state): |
+ """Checks for unused local variables in function blocks. |
+ |
+ Args: |
+ token: The token to check. |
+ state: The state tracker. |
+ """ |
+ # We don't use state.InFunction because that disregards scope functions. |
+ in_function = state.FunctionDepth() > 0 |
+ if token.type == Type.SIMPLE_LVALUE or token.type == Type.IDENTIFIER: |
+ if in_function: |
+ identifier = token.string |
+ # Check whether the previous token was var. |
+ previous_code_token = tokenutil.CustomSearch( |
+ token, |
+ lambda t: t.type not in Type.NON_CODE_TYPES, |
+ reverse=True) |
+ if previous_code_token and previous_code_token.IsKeyword('var'): |
+ # Add local variable declaration to the top of the unused locals |
+ # stack. |
+ self._unused_local_variables_by_scope[-1][identifier] = token |
+ elif token.type == Type.IDENTIFIER: |
+ # This covers most cases where the variable is used as an identifier. |
+ self._MarkLocalVariableUsed(token) |
+ elif token.type == Type.SIMPLE_LVALUE and '.' in identifier: |
+ # This covers cases where a value is assigned to a property of the |
+ # variable. |
+ self._MarkLocalVariableUsed(token) |
+ elif token.type == Type.START_BLOCK: |
+ if in_function and state.IsFunctionOpen(): |
+ # Push a new map onto the stack |
+ self._unused_local_variables_by_scope.append({}) |
+ elif token.type == Type.END_BLOCK: |
+ if state.IsFunctionClose(): |
+ # Pop the stack and report any remaining locals as unused. |
+ unused_local_variables = self._unused_local_variables_by_scope.pop() |
+ for unused_token in unused_local_variables.values(): |
+ self._HandleError( |
+ errors.UNUSED_LOCAL_VARIABLE, |
+ 'Unused local variable: %s.' % unused_token.string, |
+ unused_token) |
+ |
+ def _MarkLocalVariableUsed(self, token): |
+ """Marks the local variable as used in the relevant scope. |
+ |
+ Marks the local variable as used in the scope nearest to the current |
+ scope that matches the given token. |
+ |
+ Args: |
+ token: The token representing the potential usage of a local variable. |
+ """ |
+ |
+ identifier = token.string.split('.')[0] |
+ # Find the first instance of the identifier in the stack of function scopes |
+ # and mark it used. |
+ for unused_local_variables in reversed( |
+ self._unused_local_variables_by_scope): |
+ if identifier in unused_local_variables: |
+ del unused_local_variables[identifier] |
+ break |
def _ReportMissingProvides(self, missing_provides, token, need_blank_line): |
"""Reports missing provide statements to the error handler. |
Args: |
- missing_provides: A list of strings where each string is a namespace that |
- should be provided, but is not. |
+ missing_provides: A dictionary of string(key) and integer(value) where |
+ each string(key) is a namespace that should be provided, but is not |
+ and integer(value) is first line number where it's required. |
token: The token where the error was detected (also where the new provides |
will be inserted. |
need_blank_line: Whether a blank line needs to be inserted after the new |
provides are inserted. May be True, False, or None, where None |
indicates that the insert location is unknown. |
""" |
+ |
+ missing_provides_msg = 'Missing the following goog.provide statements:\n' |
+ missing_provides_msg += '\n'.join(['goog.provide(\'%s\');' % x for x in |
+ sorted(missing_provides)]) |
+ missing_provides_msg += '\n' |
+ |
+ missing_provides_msg += '\nFirst line where provided: \n' |
+ missing_provides_msg += '\n'.join( |
+ [' %s : line %d' % (x, missing_provides[x]) for x in |
+ sorted(missing_provides)]) |
+ missing_provides_msg += '\n' |
+ |
self._HandleError( |
errors.MISSING_GOOG_PROVIDE, |
- 'Missing the following goog.provide statements:\n' + |
- '\n'.join(map(lambda x: 'goog.provide(\'%s\');' % x, |
- sorted(missing_provides))), |
+ missing_provides_msg, |
token, position=Position.AtBeginning(), |
- fix_data=(missing_provides, need_blank_line)) |
+ fix_data=(missing_provides.keys(), need_blank_line)) |
def _ReportMissingRequires(self, missing_requires, token, need_blank_line): |
"""Reports missing require statements to the error handler. |
Args: |
- missing_requires: A list of strings where each string is a namespace that |
- should be required, but is not. |
+ missing_requires: A dictionary of string(key) and integer(value) where |
+ each string(key) is a namespace that should be required, but is not |
+ and integer(value) is first line number where it's required. |
token: The token where the error was detected (also where the new requires |
will be inserted. |
need_blank_line: Whether a blank line needs to be inserted before the new |
requires are inserted. May be True, False, or None, where None |
indicates that the insert location is unknown. |
""" |
+ |
+ missing_requires_msg = 'Missing the following goog.require statements:\n' |
+ missing_requires_msg += '\n'.join(['goog.require(\'%s\');' % x for x in |
+ sorted(missing_requires)]) |
+ missing_requires_msg += '\n' |
+ |
+ missing_requires_msg += '\nFirst line where required: \n' |
+ missing_requires_msg += '\n'.join( |
+ [' %s : line %d' % (x, missing_requires[x]) for x in |
+ sorted(missing_requires)]) |
+ missing_requires_msg += '\n' |
+ |
self._HandleError( |
errors.MISSING_GOOG_REQUIRE, |
- 'Missing the following goog.require statements:\n' + |
- '\n'.join(map(lambda x: 'goog.require(\'%s\');' % x, |
- sorted(missing_requires))), |
+ missing_requires_msg, |
token, position=Position.AtBeginning(), |
- fix_data=(missing_requires, need_blank_line)) |
+ fix_data=(missing_requires.keys(), need_blank_line)) |
- def Finalize(self, state, tokenizer_mode): |
+ def Finalize(self, state): |
"""Perform all checks that need to occur after all lines are processed.""" |
# Call the base class's Finalize function. |
- super(JavaScriptLintRules, self).Finalize(state, tokenizer_mode) |
+ super(JavaScriptLintRules, self).Finalize(state) |
if error_check.ShouldCheck(Rule.UNUSED_PRIVATE_MEMBERS): |
# Report an error for any declared private member that was never used. |
@@ -477,8 +676,8 @@ class JavaScriptLintRules(ecmalintrules.EcmaScriptLintRules): |
# Clear state to prepare for the next file. |
self._declared_private_member_tokens = {} |
- self._declared_private_members = Set() |
- self._used_private_members = Set() |
+ self._declared_private_members = set() |
+ self._used_private_members = set() |
namespaces_info = self._namespaces_info |
if namespaces_info is not None: |
@@ -508,31 +707,37 @@ class JavaScriptLintRules(ecmalintrules.EcmaScriptLintRules): |
token: The first token in the token stream. |
""" |
sorter = requireprovidesorter.RequireProvideSorter() |
- provides_result = sorter.CheckProvides(token) |
- if provides_result: |
+ first_provide_token = sorter.CheckProvides(token) |
+ if first_provide_token: |
+ new_order = sorter.GetFixedProvideString(first_provide_token) |
self._HandleError( |
errors.GOOG_PROVIDES_NOT_ALPHABETIZED, |
'goog.provide classes must be alphabetized. The correct code is:\n' + |
- '\n'.join( |
- map(lambda x: 'goog.provide(\'%s\');' % x, provides_result[1])), |
- provides_result[0], |
+ new_order, |
+ first_provide_token, |
position=Position.AtBeginning(), |
- fix_data=provides_result[0]) |
+ fix_data=first_provide_token) |
- requires_result = sorter.CheckRequires(token) |
- if requires_result: |
+ first_require_token = sorter.CheckRequires(token) |
+ if first_require_token: |
+ new_order = sorter.GetFixedRequireString(first_require_token) |
self._HandleError( |
errors.GOOG_REQUIRES_NOT_ALPHABETIZED, |
'goog.require classes must be alphabetized. The correct code is:\n' + |
- '\n'.join( |
- map(lambda x: 'goog.require(\'%s\');' % x, requires_result[1])), |
- requires_result[0], |
+ new_order, |
+ first_require_token, |
position=Position.AtBeginning(), |
- fix_data=requires_result[0]) |
+ fix_data=first_require_token) |
def GetLongLineExceptions(self): |
- """Gets a list of regexps for lines which can be longer than the limit.""" |
+ """Gets a list of regexps for lines which can be longer than the limit. |
+ |
+ Returns: |
+ A list of regexps, used as matches (rather than searches). |
+ """ |
return [ |
- re.compile('goog\.require\(.+\);?\s*$'), |
- re.compile('goog\.provide\(.+\);?\s*$') |
+ re.compile(r'goog\.require\(.+\);?\s*$'), |
+ re.compile(r'goog\.provide\(.+\);?\s*$'), |
+ re.compile(r'goog\.setTestOnly\(.+\);?\s*$'), |
+ re.compile(r'[\s/*]*@visibility\s*{.*}[\s*/]*$'), |
] |