| Index: third_party/closure_linter/closure_linter/ecmalintrules.py
|
| diff --git a/third_party/closure_linter/closure_linter/ecmalintrules.py b/third_party/closure_linter/closure_linter/ecmalintrules.py
|
| index 1187f514e7326a5baa674082fe9b65679dec1137..6ed2e97f5715e006441b7ad0360ca2e095ae895d 100755
|
| --- a/third_party/closure_linter/closure_linter/ecmalintrules.py
|
| +++ b/third_party/closure_linter/closure_linter/ecmalintrules.py
|
| @@ -23,21 +23,21 @@ __author__ = ('robbyw@google.com (Robert Walker)',
|
|
|
| import re
|
|
|
| +import gflags as flags
|
| +
|
| from closure_linter import checkerbase
|
| from closure_linter import ecmametadatapass
|
| from closure_linter import error_check
|
| +from closure_linter import errorrules
|
| from closure_linter import errors
|
| from closure_linter import indentation
|
| -from closure_linter import javascripttokens
|
| from closure_linter import javascripttokenizer
|
| +from closure_linter import javascripttokens
|
| from closure_linter import statetracker
|
| from closure_linter import tokenutil
|
| from closure_linter.common import error
|
| -from closure_linter.common import htmlutil
|
| -from closure_linter.common import lintrunner
|
| from closure_linter.common import position
|
| -from closure_linter.common import tokens
|
| -import gflags as flags
|
| +
|
|
|
| FLAGS = flags.FLAGS
|
| flags.DEFINE_list('custom_jsdoc_tags', '', 'Extra jsdoc tags to allow')
|
| @@ -55,6 +55,7 @@ Position = position.Position
|
| Rule = error_check.Rule
|
| Type = javascripttokens.JavaScriptTokenType
|
|
|
| +
|
| class EcmaScriptLintRules(checkerbase.LintRulesBase):
|
| """EmcaScript lint style checking rules.
|
|
|
| @@ -67,14 +68,15 @@ class EcmaScriptLintRules(checkerbase.LintRulesBase):
|
| language.
|
| """
|
|
|
| - # Static constants.
|
| - MAX_LINE_LENGTH = 80
|
| + # It will be initialized in constructor so the flags are initialized.
|
| + max_line_length = -1
|
|
|
| + # Static constants.
|
| MISSING_PARAMETER_SPACE = re.compile(r',\S')
|
|
|
| - EXTRA_SPACE = re.compile('(\(\s|\s\))')
|
| + EXTRA_SPACE = re.compile(r'(\(\s|\s\))')
|
|
|
| - ENDS_WITH_SPACE = re.compile('\s$')
|
| + ENDS_WITH_SPACE = re.compile(r'\s$')
|
|
|
| ILLEGAL_TAB = re.compile(r'\t')
|
|
|
| @@ -85,12 +87,18 @@ class EcmaScriptLintRules(checkerbase.LintRulesBase):
|
| AUTHOR_SPEC = re.compile(r'(\s*)[^\s]+@[^(\s]+(\s*)\(.+\)')
|
|
|
| # Acceptable tokens to remove for line too long testing.
|
| - LONG_LINE_IGNORE = frozenset(['*', '//', '@see'] +
|
| + LONG_LINE_IGNORE = frozenset(
|
| + ['*', '//', '@see'] +
|
| ['@%s' % tag for tag in statetracker.DocFlag.HAS_TYPE])
|
|
|
| + JSDOC_FLAGS_DESCRIPTION_NOT_REQUIRED = frozenset([
|
| + '@param', '@return', '@returns'])
|
| +
|
| def __init__(self):
|
| """Initialize this lint rule object."""
|
| checkerbase.LintRulesBase.__init__(self)
|
| + if EcmaScriptLintRules.max_line_length == -1:
|
| + EcmaScriptLintRules.max_line_length = errorrules.GetMaxLineLength()
|
|
|
| def Initialize(self, checker, limited_doc_checks, is_html):
|
| """Initialize this lint rule object before parsing a new file."""
|
| @@ -107,6 +115,7 @@ class EcmaScriptLintRules(checkerbase.LintRulesBase):
|
|
|
| Args:
|
| last_token: The last token in the line.
|
| + state: parser_state object that indicates the current state in the page
|
| """
|
| # Start from the last token so that we have the flag object attached to
|
| # and DOC_FLAG tokens.
|
| @@ -130,7 +139,7 @@ class EcmaScriptLintRules(checkerbase.LintRulesBase):
|
| line = line.rstrip('\n\r\f')
|
| try:
|
| length = len(unicode(line, 'utf-8'))
|
| - except:
|
| + except (LookupError, UnicodeDecodeError):
|
| # Unknown encoding. The line length may be wrong, as was originally the
|
| # case for utf-8 (see bug 1735846). For now just accept the default
|
| # length, but as we find problems we can either add test for other
|
| @@ -138,7 +147,7 @@ class EcmaScriptLintRules(checkerbase.LintRulesBase):
|
| # false positives at the cost of more false negatives.
|
| length = len(line)
|
|
|
| - if length > self.MAX_LINE_LENGTH:
|
| + if length > EcmaScriptLintRules.max_line_length:
|
|
|
| # If the line matches one of the exceptions, then it's ok.
|
| for long_line_regexp in self.GetLongLineExceptions():
|
| @@ -150,15 +159,17 @@ class EcmaScriptLintRules(checkerbase.LintRulesBase):
|
| parts = set(line.split())
|
|
|
| # We allow two "words" (type and name) when the line contains @param
|
| - max = 1
|
| + max_parts = 1
|
| if '@param' in parts:
|
| - max = 2
|
| + max_parts = 2
|
|
|
| # Custom tags like @requires may have url like descriptions, so ignore
|
| # the tag, similar to how we handle @see.
|
| custom_tags = set(['@%s' % f for f in FLAGS.custom_jsdoc_tags])
|
| - if (len(parts.difference(self.LONG_LINE_IGNORE | custom_tags)) > max):
|
| - self._HandleError(errors.LINE_TOO_LONG,
|
| + if (len(parts.difference(self.LONG_LINE_IGNORE | custom_tags))
|
| + > max_parts):
|
| + self._HandleError(
|
| + errors.LINE_TOO_LONG,
|
| 'Line too long (%d characters).' % len(line), last_token)
|
|
|
| def _CheckJsDocType(self, token):
|
| @@ -168,25 +179,23 @@ class EcmaScriptLintRules(checkerbase.LintRulesBase):
|
| token: The DOC_FLAG token for the flag whose type to check.
|
| """
|
| flag = token.attached_object
|
| - type = flag.type
|
| - if type and type is not None and not type.isspace():
|
| - pieces = self.TYPE_SPLIT.split(type)
|
| - if len(pieces) == 1 and type.count('|') == 1 and (
|
| - type.endswith('|null') or type.startswith('null|')):
|
| - self._HandleError(errors.JSDOC_PREFER_QUESTION_TO_PIPE_NULL,
|
| - 'Prefer "?Type" to "Type|null": "%s"' % type, token)
|
| -
|
| - for p in pieces:
|
| - if p.count('|') and p.count('?'):
|
| - # TODO(robbyw): We should do actual parsing of JsDoc types. As is,
|
| - # this won't report an error for {number|Array.<string>?}, etc.
|
| - self._HandleError(errors.JSDOC_ILLEGAL_QUESTION_WITH_PIPE,
|
| - 'JsDoc types cannot contain both "?" and "|": "%s"' % p, token)
|
| + flag_type = flag.type
|
| + if flag_type and flag_type is not None and not flag_type.isspace():
|
| + pieces = self.TYPE_SPLIT.split(flag_type)
|
| + if len(pieces) == 1 and flag_type.count('|') == 1 and (
|
| + flag_type.endswith('|null') or flag_type.startswith('null|')):
|
| + self._HandleError(
|
| + errors.JSDOC_PREFER_QUESTION_TO_PIPE_NULL,
|
| + 'Prefer "?Type" to "Type|null": "%s"' % flag_type, token)
|
| +
|
| + # TODO(user): We should do actual parsing of JsDoc types to report an
|
| + # error for wrong usage of '?' and '|' e.g. {?number|string|null} etc.
|
|
|
| if error_check.ShouldCheck(Rule.BRACES_AROUND_TYPE) and (
|
| flag.type_start_token.type != Type.DOC_START_BRACE or
|
| flag.type_end_token.type != Type.DOC_END_BRACE):
|
| - self._HandleError(errors.MISSING_BRACES_AROUND_TYPE,
|
| + self._HandleError(
|
| + errors.MISSING_BRACES_AROUND_TYPE,
|
| 'Type must always be surrounded by curly braces.', token)
|
|
|
| def _CheckForMissingSpaceBeforeToken(self, token):
|
| @@ -207,7 +216,37 @@ class EcmaScriptLintRules(checkerbase.LintRulesBase):
|
| errors.MISSING_SPACE,
|
| 'Missing space before "%s"' % token.string,
|
| token,
|
| - Position.AtBeginning())
|
| + position=Position.AtBeginning())
|
| +
|
| + def _CheckOperator(self, token):
|
| + """Checks an operator for spacing and line style.
|
| +
|
| + Args:
|
| + token: The operator token.
|
| + """
|
| + last_code = token.metadata.last_code
|
| +
|
| + if not self._ExpectSpaceBeforeOperator(token):
|
| + if (token.previous and token.previous.type == Type.WHITESPACE and
|
| + last_code and last_code.type in (Type.NORMAL, Type.IDENTIFIER)):
|
| + self._HandleError(
|
| + errors.EXTRA_SPACE, 'Extra space before "%s"' % token.string,
|
| + token.previous, position=Position.All(token.previous.string))
|
| +
|
| + elif (token.previous and
|
| + not token.previous.IsComment() and
|
| + token.previous.type in Type.EXPRESSION_ENDER_TYPES):
|
| + self._HandleError(errors.MISSING_SPACE,
|
| + 'Missing space before "%s"' % token.string, token,
|
| + position=Position.AtBeginning())
|
| +
|
| + # Check that binary operators are not used to start lines.
|
| + if ((not last_code or last_code.line_number != token.line_number) and
|
| + not token.metadata.IsUnaryOperator()):
|
| + self._HandleError(
|
| + errors.LINE_STARTS_WITH_OPERATOR,
|
| + 'Binary operator should go on previous line "%s"' % token.string,
|
| + token)
|
|
|
| def _ExpectSpaceBeforeOperator(self, token):
|
| """Returns whether a space should appear before the given operator token.
|
| @@ -247,7 +286,7 @@ class EcmaScriptLintRules(checkerbase.LintRulesBase):
|
| last_in_line = token.IsLastInLine()
|
| last_non_space_token = state.GetLastNonSpaceToken()
|
|
|
| - type = token.type
|
| + token_type = token.type
|
|
|
| # Process the line change.
|
| if not self._is_html and error_check.ShouldCheck(Rule.INDENTATION):
|
| @@ -259,11 +298,12 @@ class EcmaScriptLintRules(checkerbase.LintRulesBase):
|
| if last_in_line:
|
| self._CheckLineLength(token, state)
|
|
|
| - if type == Type.PARAMETERS:
|
| + if token_type == Type.PARAMETERS:
|
| # Find missing spaces in parameter lists.
|
| if self.MISSING_PARAMETER_SPACE.search(token.string):
|
| + fix_data = ', '.join([s.strip() for s in token.string.split(',')])
|
| self._HandleError(errors.MISSING_SPACE, 'Missing space after ","',
|
| - token)
|
| + token, position=None, fix_data=fix_data.strip())
|
|
|
| # Find extra spaces at the beginning of parameter lists. Make sure
|
| # we aren't at the beginning of a continuing multi-line list.
|
| @@ -271,54 +311,64 @@ class EcmaScriptLintRules(checkerbase.LintRulesBase):
|
| space_count = len(token.string) - len(token.string.lstrip())
|
| if space_count:
|
| self._HandleError(errors.EXTRA_SPACE, 'Extra space after "("',
|
| - token, Position(0, space_count))
|
| + token, position=Position(0, space_count))
|
|
|
| - elif (type == Type.START_BLOCK and
|
| + elif (token_type == Type.START_BLOCK and
|
| token.metadata.context.type == Context.BLOCK):
|
| self._CheckForMissingSpaceBeforeToken(token)
|
|
|
| - elif type == Type.END_BLOCK:
|
| + elif token_type == Type.END_BLOCK:
|
| # This check is for object literal end block tokens, but there is no need
|
| # to test that condition since a comma at the end of any other kind of
|
| # block is undoubtedly a parse error.
|
| last_code = token.metadata.last_code
|
| if last_code.IsOperator(','):
|
| - self._HandleError(errors.COMMA_AT_END_OF_LITERAL,
|
| + self._HandleError(
|
| + errors.COMMA_AT_END_OF_LITERAL,
|
| 'Illegal comma at end of object literal', last_code,
|
| - Position.All(last_code.string))
|
| + position=Position.All(last_code.string))
|
|
|
| if state.InFunction() and state.IsFunctionClose():
|
| is_immediately_called = (token.next and
|
| token.next.type == Type.START_PAREN)
|
| if state.InTopLevelFunction():
|
| - # When the function was top-level and not immediately called, check
|
| - # that it's terminated by a semi-colon.
|
| - if state.InAssignedFunction():
|
| - if not is_immediately_called and (last_in_line or
|
| - not token.next.type == Type.SEMICOLON):
|
| - self._HandleError(errors.MISSING_SEMICOLON_AFTER_FUNCTION,
|
| - 'Missing semicolon after function assigned to a variable',
|
| - token, Position.AtEnd(token.string))
|
| - else:
|
| + # A semicolons should not be included at the end of a function
|
| + # declaration.
|
| + if not state.InAssignedFunction():
|
| if not last_in_line and token.next.type == Type.SEMICOLON:
|
| - self._HandleError(errors.ILLEGAL_SEMICOLON_AFTER_FUNCTION,
|
| + self._HandleError(
|
| + errors.ILLEGAL_SEMICOLON_AFTER_FUNCTION,
|
| 'Illegal semicolon after function declaration',
|
| - token.next, Position.All(token.next.string))
|
| + token.next, position=Position.All(token.next.string))
|
|
|
| - if (state.InInterfaceMethod() and last_code.type != Type.START_BLOCK):
|
| + # A semicolon should be included at the end of a function expression
|
| + # that is not immediately called.
|
| + if state.InAssignedFunction():
|
| + if not is_immediately_called and (
|
| + last_in_line or token.next.type != Type.SEMICOLON):
|
| + self._HandleError(
|
| + errors.MISSING_SEMICOLON_AFTER_FUNCTION,
|
| + 'Missing semicolon after function assigned to a variable',
|
| + token, position=Position.AtEnd(token.string))
|
| +
|
| + if state.InInterfaceMethod() and last_code.type != Type.START_BLOCK:
|
| self._HandleError(errors.INTERFACE_METHOD_CANNOT_HAVE_CODE,
|
| - 'Interface methods cannot contain code', last_code)
|
| + 'Interface methods cannot contain code', last_code)
|
|
|
| elif (state.IsBlockClose() and
|
| token.next and token.next.type == Type.SEMICOLON):
|
| - self._HandleError(errors.REDUNDANT_SEMICOLON,
|
| - 'No semicolon is required to end a code block',
|
| - token.next, Position.All(token.next.string))
|
| -
|
| - elif type == Type.SEMICOLON:
|
| + if (last_code.metadata.context.parent.type != Context.OBJECT_LITERAL
|
| + and last_code.metadata.context.type != Context.OBJECT_LITERAL):
|
| + self._HandleError(
|
| + errors.REDUNDANT_SEMICOLON,
|
| + 'No semicolon is required to end a code block',
|
| + token.next, position=Position.All(token.next.string))
|
| +
|
| + elif token_type == Type.SEMICOLON:
|
| if token.previous and token.previous.type == Type.WHITESPACE:
|
| - self._HandleError(errors.EXTRA_SPACE, 'Extra space before ";"',
|
| - token.previous, Position.All(token.previous.string))
|
| + self._HandleError(
|
| + errors.EXTRA_SPACE, 'Extra space before ";"',
|
| + token.previous, position=Position.All(token.previous.string))
|
|
|
| if token.next and token.next.line_number == token.line_number:
|
| if token.metadata.context.type != Context.FOR_GROUP_BLOCK:
|
| @@ -327,10 +377,11 @@ class EcmaScriptLintRules(checkerbase.LintRulesBase):
|
|
|
| elif token.next.type not in (
|
| Type.WHITESPACE, Type.SEMICOLON, Type.END_PAREN):
|
| - self._HandleError(errors.MISSING_SPACE,
|
| + self._HandleError(
|
| + errors.MISSING_SPACE,
|
| 'Missing space after ";" in for statement',
|
| token.next,
|
| - Position.AtBeginning())
|
| + position=Position.AtBeginning())
|
|
|
| last_code = token.metadata.last_code
|
| if last_code and last_code.type == Type.SEMICOLON:
|
| @@ -339,7 +390,8 @@ class EcmaScriptLintRules(checkerbase.LintRulesBase):
|
| # NOTE(user): This is not a perfect check, and will not throw an error
|
| # for cases like: for (var i = 0;; i < n; i++) {}, but then your code
|
| # probably won't work either.
|
| - for_token = tokenutil.CustomSearch(last_code,
|
| + for_token = tokenutil.CustomSearch(
|
| + last_code,
|
| lambda token: token.type == Type.KEYWORD and token.string == 'for',
|
| end_func=lambda token: token.type == Type.SEMICOLON,
|
| distance=None,
|
| @@ -347,93 +399,79 @@ class EcmaScriptLintRules(checkerbase.LintRulesBase):
|
|
|
| if not for_token:
|
| self._HandleError(errors.REDUNDANT_SEMICOLON, 'Redundant semicolon',
|
| - token, Position.All(token.string))
|
| + token, position=Position.All(token.string))
|
|
|
| - elif type == Type.START_PAREN:
|
| + elif token_type == Type.START_PAREN:
|
| if token.previous and token.previous.type == Type.KEYWORD:
|
| self._HandleError(errors.MISSING_SPACE, 'Missing space before "("',
|
| - token, Position.AtBeginning())
|
| + token, position=Position.AtBeginning())
|
| elif token.previous and token.previous.type == Type.WHITESPACE:
|
| before_space = token.previous.previous
|
| if (before_space and before_space.line_number == token.line_number and
|
| before_space.type == Type.IDENTIFIER):
|
| - self._HandleError(errors.EXTRA_SPACE, 'Extra space before "("',
|
| - token.previous, Position.All(token.previous.string))
|
| + self._HandleError(
|
| + errors.EXTRA_SPACE, 'Extra space before "("',
|
| + token.previous, position=Position.All(token.previous.string))
|
|
|
| - elif type == Type.START_BRACKET:
|
| + elif token_type == Type.START_BRACKET:
|
| self._HandleStartBracket(token, last_non_space_token)
|
| - elif type in (Type.END_PAREN, Type.END_BRACKET):
|
| + elif token_type in (Type.END_PAREN, Type.END_BRACKET):
|
| # Ensure there is no space before closing parentheses, except when
|
| # it's in a for statement with an omitted section, or when it's at the
|
| # beginning of a line.
|
| if (token.previous and token.previous.type == Type.WHITESPACE and
|
| not token.previous.IsFirstInLine() and
|
| not (last_non_space_token and last_non_space_token.line_number ==
|
| - token.line_number and
|
| + token.line_number and
|
| last_non_space_token.type == Type.SEMICOLON)):
|
| - self._HandleError(errors.EXTRA_SPACE, 'Extra space before "%s"' %
|
| - token.string, token.previous, Position.All(token.previous.string))
|
| + self._HandleError(
|
| + errors.EXTRA_SPACE, 'Extra space before "%s"' %
|
| + token.string, token.previous,
|
| + position=Position.All(token.previous.string))
|
|
|
| if token.type == Type.END_BRACKET:
|
| last_code = token.metadata.last_code
|
| if last_code.IsOperator(','):
|
| - self._HandleError(errors.COMMA_AT_END_OF_LITERAL,
|
| + self._HandleError(
|
| + errors.COMMA_AT_END_OF_LITERAL,
|
| 'Illegal comma at end of array literal', last_code,
|
| - Position.All(last_code.string))
|
| + position=Position.All(last_code.string))
|
|
|
| - elif type == Type.WHITESPACE:
|
| + elif token_type == Type.WHITESPACE:
|
| if self.ILLEGAL_TAB.search(token.string):
|
| if token.IsFirstInLine():
|
| if token.next:
|
| - self._HandleError(errors.ILLEGAL_TAB,
|
| + self._HandleError(
|
| + errors.ILLEGAL_TAB,
|
| 'Illegal tab in whitespace before "%s"' % token.next.string,
|
| - token, Position.All(token.string))
|
| + token, position=Position.All(token.string))
|
| else:
|
| - self._HandleError(errors.ILLEGAL_TAB,
|
| + self._HandleError(
|
| + errors.ILLEGAL_TAB,
|
| 'Illegal tab in whitespace',
|
| - token, Position.All(token.string))
|
| + token, position=Position.All(token.string))
|
| else:
|
| - self._HandleError(errors.ILLEGAL_TAB,
|
| + self._HandleError(
|
| + errors.ILLEGAL_TAB,
|
| 'Illegal tab in whitespace after "%s"' % token.previous.string,
|
| - token, Position.All(token.string))
|
| + token, position=Position.All(token.string))
|
|
|
| # Check whitespace length if it's not the first token of the line and
|
| # if it's not immediately before a comment.
|
| if last_in_line:
|
| # Check for extra whitespace at the end of a line.
|
| self._HandleError(errors.EXTRA_SPACE, 'Extra space at end of line',
|
| - token, Position.All(token.string))
|
| + token, position=Position.All(token.string))
|
| elif not first_in_line and not token.next.IsComment():
|
| if token.length > 1:
|
| - self._HandleError(errors.EXTRA_SPACE, 'Extra space after "%s"' %
|
| + self._HandleError(
|
| + errors.EXTRA_SPACE, 'Extra space after "%s"' %
|
| token.previous.string, token,
|
| - Position(1, len(token.string) - 1))
|
| + position=Position(1, len(token.string) - 1))
|
|
|
| - elif type == Type.OPERATOR:
|
| - last_code = token.metadata.last_code
|
| -
|
| - if not self._ExpectSpaceBeforeOperator(token):
|
| - if (token.previous and token.previous.type == Type.WHITESPACE and
|
| - last_code and last_code.type in (Type.NORMAL, Type.IDENTIFIER)):
|
| - self._HandleError(errors.EXTRA_SPACE,
|
| - 'Extra space before "%s"' % token.string, token.previous,
|
| - Position.All(token.previous.string))
|
| -
|
| - elif (token.previous and
|
| - not token.previous.IsComment() and
|
| - token.previous.type in Type.EXPRESSION_ENDER_TYPES):
|
| - self._HandleError(errors.MISSING_SPACE,
|
| - 'Missing space before "%s"' % token.string, token,
|
| - Position.AtBeginning())
|
| -
|
| - # Check that binary operators are not used to start lines.
|
| - if ((not last_code or last_code.line_number != token.line_number) and
|
| - not token.metadata.IsUnaryOperator()):
|
| - self._HandleError(errors.LINE_STARTS_WITH_OPERATOR,
|
| - 'Binary operator should go on previous line "%s"' % token.string,
|
| - token)
|
| -
|
| - elif type == Type.DOC_FLAG:
|
| + elif token_type == Type.OPERATOR:
|
| + self._CheckOperator(token)
|
| + elif token_type == Type.DOC_FLAG:
|
| flag = token.attached_object
|
|
|
| if flag.flag_type == 'bug':
|
| @@ -443,21 +481,22 @@ class EcmaScriptLintRules(checkerbase.LintRulesBase):
|
|
|
| if not string.isdigit():
|
| self._HandleError(errors.NO_BUG_NUMBER_AFTER_BUG_TAG,
|
| - '@bug should be followed by a bug number', token)
|
| + '@bug should be followed by a bug number', token)
|
|
|
| elif flag.flag_type == 'suppress':
|
| if flag.type is None:
|
| # A syntactically invalid suppress tag will get tokenized as a normal
|
| # flag, indicating an error.
|
| - self._HandleError(errors.INCORRECT_SUPPRESS_SYNTAX,
|
| + self._HandleError(
|
| + errors.INCORRECT_SUPPRESS_SYNTAX,
|
| 'Invalid suppress syntax: should be @suppress {errortype}. '
|
| 'Spaces matter.', token)
|
| else:
|
| - for suppress_type in flag.type.split('|'):
|
| + for suppress_type in re.split(r'\||,', flag.type):
|
| if suppress_type not in state.GetDocFlag().SUPPRESS_TYPES:
|
| - self._HandleError(errors.INVALID_SUPPRESS_TYPE,
|
| - 'Invalid suppression type: %s' % suppress_type,
|
| - token)
|
| + self._HandleError(
|
| + errors.INVALID_SUPPRESS_TYPE,
|
| + 'Invalid suppression type: %s' % suppress_type, token)
|
|
|
| elif (error_check.ShouldCheck(Rule.WELL_FORMED_AUTHOR) and
|
| flag.flag_type == 'author'):
|
| @@ -478,12 +517,12 @@ class EcmaScriptLintRules(checkerbase.LintRulesBase):
|
| if num_spaces < 1:
|
| self._HandleError(errors.MISSING_SPACE,
|
| 'Missing space after email address',
|
| - token.next, Position(result.start(2), 0))
|
| + token.next, position=Position(result.start(2), 0))
|
| elif num_spaces > 1:
|
| - self._HandleError(errors.EXTRA_SPACE,
|
| - 'Extra space after email address',
|
| - token.next,
|
| - Position(result.start(2) + 1, num_spaces - 1))
|
| + self._HandleError(
|
| + errors.EXTRA_SPACE, 'Extra space after email address',
|
| + token.next,
|
| + position=Position(result.start(2) + 1, num_spaces - 1))
|
|
|
| # Check for extra spaces before email address. Can't be too few, if
|
| # not at least one we wouldn't match @author tag.
|
| @@ -491,81 +530,54 @@ class EcmaScriptLintRules(checkerbase.LintRulesBase):
|
| if num_spaces > 1:
|
| self._HandleError(errors.EXTRA_SPACE,
|
| 'Extra space before email address',
|
| - token.next, Position(1, num_spaces - 1))
|
| + token.next, position=Position(1, num_spaces - 1))
|
|
|
| elif (flag.flag_type in state.GetDocFlag().HAS_DESCRIPTION and
|
| not self._limited_doc_checks):
|
| if flag.flag_type == 'param':
|
| if flag.name is None:
|
| self._HandleError(errors.MISSING_JSDOC_PARAM_NAME,
|
| - 'Missing name in @param tag', token)
|
| + 'Missing name in @param tag', token)
|
|
|
| if not flag.description or flag.description is None:
|
| flag_name = token.type
|
| if 'name' in token.values:
|
| flag_name = '@' + token.values['name']
|
| - self._HandleError(errors.MISSING_JSDOC_TAG_DESCRIPTION,
|
| - 'Missing description in %s tag' % flag_name, token)
|
| - else:
|
| - self._CheckForMissingSpaceBeforeToken(flag.description_start_token)
|
| -
|
| - # We want punctuation to be inside of any tags ending a description,
|
| - # so strip tags before checking description. See bug 1127192. Note
|
| - # that depending on how lines break, the real description end token
|
| - # may consist only of stripped html and the effective end token can
|
| - # be different.
|
| - end_token = flag.description_end_token
|
| - end_string = htmlutil.StripTags(end_token.string).strip()
|
| - while (end_string == '' and not
|
| - end_token.type in Type.FLAG_ENDING_TYPES):
|
| - end_token = end_token.previous
|
| - if end_token.type in Type.FLAG_DESCRIPTION_TYPES:
|
| - end_string = htmlutil.StripTags(end_token.string).rstrip()
|
| -
|
| - if not (end_string.endswith('.') or end_string.endswith('?') or
|
| - end_string.endswith('!')):
|
| - # Find the position for the missing punctuation, inside of any html
|
| - # tags.
|
| - desc_str = end_token.string.rstrip()
|
| - while desc_str.endswith('>'):
|
| - start_tag_index = desc_str.rfind('<')
|
| - if start_tag_index < 0:
|
| - break
|
| - desc_str = desc_str[:start_tag_index].rstrip()
|
| - end_position = Position(len(desc_str), 0)
|
|
|
| + if flag_name not in self.JSDOC_FLAGS_DESCRIPTION_NOT_REQUIRED:
|
| self._HandleError(
|
| - errors.JSDOC_TAG_DESCRIPTION_ENDS_WITH_INVALID_CHARACTER,
|
| - ('%s descriptions must end with valid punctuation such as a '
|
| - 'period.' % token.string),
|
| - end_token, end_position)
|
| + errors.MISSING_JSDOC_TAG_DESCRIPTION,
|
| + 'Missing description in %s tag' % flag_name, token)
|
| + else:
|
| + self._CheckForMissingSpaceBeforeToken(flag.description_start_token)
|
|
|
| if flag.flag_type in state.GetDocFlag().HAS_TYPE:
|
| if flag.type_start_token is not None:
|
| self._CheckForMissingSpaceBeforeToken(
|
| token.attached_object.type_start_token)
|
|
|
| - if flag.type and flag.type != '' and not flag.type.isspace():
|
| + if flag.type and not flag.type.isspace():
|
| self._CheckJsDocType(token)
|
|
|
| - if type in (Type.DOC_FLAG, Type.DOC_INLINE_FLAG):
|
| - if (token.values['name'] not in state.GetDocFlag().LEGAL_DOC and
|
| - token.values['name'] not in FLAGS.custom_jsdoc_tags):
|
| - self._HandleError(errors.INVALID_JSDOC_TAG,
|
| - 'Invalid JsDoc tag: %s' % token.values['name'], token)
|
| -
|
| - if (error_check.ShouldCheck(Rule.NO_BRACES_AROUND_INHERIT_DOC) and
|
| - token.values['name'] == 'inheritDoc' and
|
| - type == Type.DOC_INLINE_FLAG):
|
| - self._HandleError(errors.UNNECESSARY_BRACES_AROUND_INHERIT_DOC,
|
| - 'Unnecessary braces around @inheritDoc',
|
| - token)
|
| -
|
| - elif type == Type.SIMPLE_LVALUE:
|
| + if token_type in (Type.DOC_FLAG, Type.DOC_INLINE_FLAG):
|
| + if (token.values['name'] not in state.GetDocFlag().LEGAL_DOC and
|
| + token.values['name'] not in FLAGS.custom_jsdoc_tags):
|
| + self._HandleError(
|
| + errors.INVALID_JSDOC_TAG,
|
| + 'Invalid JsDoc tag: %s' % token.values['name'], token)
|
| +
|
| + if (error_check.ShouldCheck(Rule.NO_BRACES_AROUND_INHERIT_DOC) and
|
| + token.values['name'] == 'inheritDoc' and
|
| + token_type == Type.DOC_INLINE_FLAG):
|
| + self._HandleError(errors.UNNECESSARY_BRACES_AROUND_INHERIT_DOC,
|
| + 'Unnecessary braces around @inheritDoc',
|
| + token)
|
| +
|
| + elif token_type == Type.SIMPLE_LVALUE:
|
| identifier = token.values['identifier']
|
|
|
| if ((not state.InFunction() or state.InConstructor()) and
|
| - not state.InParentheses() and not state.InObjectLiteralDescendant()):
|
| + state.InTopLevel() and not state.InObjectLiteralDescendant()):
|
| jsdoc = state.GetDocComment()
|
| if not state.HasDocComment(identifier):
|
| # Only test for documentation on identifiers with .s in them to
|
| @@ -577,9 +589,10 @@ class EcmaScriptLintRules(checkerbase.LintRulesBase):
|
| self._limited_doc_checks):
|
| comment = state.GetLastComment()
|
| if not (comment and comment.lower().count('jsdoc inherited')):
|
| - self._HandleError(errors.MISSING_MEMBER_DOCUMENTATION,
|
| + self._HandleError(
|
| + errors.MISSING_MEMBER_DOCUMENTATION,
|
| "No docs found for member '%s'" % identifier,
|
| - token);
|
| + token)
|
| elif jsdoc and (not state.InConstructor() or
|
| identifier.startswith('this.')):
|
| # We are at the top level and the function/member is documented.
|
| @@ -589,31 +602,36 @@ class EcmaScriptLintRules(checkerbase.LintRulesBase):
|
| #
|
| # @inheritDoc is deprecated in favor of using @override, and they
|
| if (jsdoc.HasFlag('override') and not jsdoc.HasFlag('constructor')
|
| - and not ('accessControls' in jsdoc.suppressions)):
|
| - self._HandleError(errors.INVALID_OVERRIDE_PRIVATE,
|
| + and ('accessControls' not in jsdoc.suppressions)):
|
| + self._HandleError(
|
| + errors.INVALID_OVERRIDE_PRIVATE,
|
| '%s should not override a private member.' % identifier,
|
| jsdoc.GetFlag('override').flag_token)
|
| if (jsdoc.HasFlag('inheritDoc') and not jsdoc.HasFlag('constructor')
|
| - and not ('accessControls' in jsdoc.suppressions)):
|
| - self._HandleError(errors.INVALID_INHERIT_DOC_PRIVATE,
|
| + and ('accessControls' not in jsdoc.suppressions)):
|
| + self._HandleError(
|
| + errors.INVALID_INHERIT_DOC_PRIVATE,
|
| '%s should not inherit from a private member.' % identifier,
|
| jsdoc.GetFlag('inheritDoc').flag_token)
|
| if (not jsdoc.HasFlag('private') and
|
| - not ('underscore' in jsdoc.suppressions) and not
|
| + ('underscore' not in jsdoc.suppressions) and not
|
| ((jsdoc.HasFlag('inheritDoc') or jsdoc.HasFlag('override')) and
|
| ('accessControls' in jsdoc.suppressions))):
|
| - self._HandleError(errors.MISSING_PRIVATE,
|
| + self._HandleError(
|
| + errors.MISSING_PRIVATE,
|
| 'Member "%s" must have @private JsDoc.' %
|
| identifier, token)
|
| if jsdoc.HasFlag('private') and 'underscore' in jsdoc.suppressions:
|
| - self._HandleError(errors.UNNECESSARY_SUPPRESS,
|
| + self._HandleError(
|
| + errors.UNNECESSARY_SUPPRESS,
|
| '@suppress {underscore} is not necessary with @private',
|
| jsdoc.suppressions['underscore'])
|
| elif (jsdoc.HasFlag('private') and
|
| not self.InExplicitlyTypedLanguage()):
|
| # It is convention to hide public fields in some ECMA
|
| # implementations from documentation using the @private tag.
|
| - self._HandleError(errors.EXTRA_PRIVATE,
|
| + self._HandleError(
|
| + errors.EXTRA_PRIVATE,
|
| 'Member "%s" must not have @private JsDoc' %
|
| identifier, token)
|
|
|
| @@ -621,9 +639,10 @@ class EcmaScriptLintRules(checkerbase.LintRulesBase):
|
| # such variables always begin with the prefix MSG_.
|
| for f in ('desc', 'hidden', 'meaning'):
|
| if (jsdoc.HasFlag(f)
|
| - and not identifier.startswith('MSG_')
|
| - and identifier.find('.MSG_') == -1):
|
| - self._HandleError(errors.INVALID_USE_OF_DESC_TAG,
|
| + and not identifier.startswith('MSG_')
|
| + and identifier.find('.MSG_') == -1):
|
| + self._HandleError(
|
| + errors.INVALID_USE_OF_DESC_TAG,
|
| 'Member "%s" should not have @%s JsDoc' % (identifier, f),
|
| token)
|
|
|
| @@ -636,28 +655,30 @@ class EcmaScriptLintRules(checkerbase.LintRulesBase):
|
| if next_code and (
|
| next_code.type in (Type.START_BRACKET, Type.START_BLOCK) or
|
| next_code.IsOperator('new')):
|
| - self._HandleError(errors.ILLEGAL_PROTOTYPE_MEMBER_VALUE,
|
| + self._HandleError(
|
| + errors.ILLEGAL_PROTOTYPE_MEMBER_VALUE,
|
| 'Member %s cannot have a non-primitive value' % identifier,
|
| token)
|
|
|
| - elif type == Type.END_PARAMETERS:
|
| + elif token_type == Type.END_PARAMETERS:
|
| # Find extra space at the end of parameter lists. We check the token
|
| # prior to the current one when it is a closing paren.
|
| if (token.previous and token.previous.type == Type.PARAMETERS
|
| and self.ENDS_WITH_SPACE.search(token.previous.string)):
|
| self._HandleError(errors.EXTRA_SPACE, 'Extra space before ")"',
|
| - token.previous)
|
| + token.previous)
|
|
|
| jsdoc = state.GetDocComment()
|
| if state.GetFunction().is_interface:
|
| if token.previous and token.previous.type == Type.PARAMETERS:
|
| - self._HandleError(errors.INTERFACE_CONSTRUCTOR_CANNOT_HAVE_PARAMS,
|
| + self._HandleError(
|
| + errors.INTERFACE_CONSTRUCTOR_CANNOT_HAVE_PARAMS,
|
| 'Interface constructor cannot have parameters',
|
| token.previous)
|
| elif (state.InTopLevel() and jsdoc and not jsdoc.HasFlag('see')
|
| - and not jsdoc.InheritsDocumentation()
|
| - and not state.InObjectLiteralDescendant() and not
|
| - jsdoc.IsInvalidated()):
|
| + and not jsdoc.InheritsDocumentation()
|
| + and not state.InObjectLiteralDescendant() and not
|
| + jsdoc.IsInvalidated()):
|
| distance, edit = jsdoc.CompareParameters(state.GetParams())
|
| if distance:
|
| params_iter = iter(state.GetParams())
|
| @@ -678,12 +699,13 @@ class EcmaScriptLintRules(checkerbase.LintRulesBase):
|
| elif op == 'D':
|
| # Deletion
|
| self._HandleError(errors.EXTRA_PARAMETER_DOCUMENTATION,
|
| - 'Found docs for non-existing parameter: "%s"' %
|
| - docs_iter.next(), token)
|
| + 'Found docs for non-existing parameter: "%s"' %
|
| + docs_iter.next(), token)
|
| elif op == 'S':
|
| # Substitution
|
| if not self._limited_doc_checks:
|
| - self._HandleError(errors.WRONG_PARAMETER_DOCUMENTATION,
|
| + self._HandleError(
|
| + errors.WRONG_PARAMETER_DOCUMENTATION,
|
| 'Parameter mismatch: got "%s", expected "%s"' %
|
| (params_iter.next(), docs_iter.next()), token)
|
|
|
| @@ -692,32 +714,33 @@ class EcmaScriptLintRules(checkerbase.LintRulesBase):
|
| params_iter.next()
|
| docs_iter.next()
|
|
|
| - elif type == Type.STRING_TEXT:
|
| + elif token_type == Type.STRING_TEXT:
|
| # If this is the first token after the start of the string, but it's at
|
| # the end of a line, we know we have a multi-line string.
|
| - if token.previous.type in (Type.SINGLE_QUOTE_STRING_START,
|
| + if token.previous.type in (
|
| + Type.SINGLE_QUOTE_STRING_START,
|
| Type.DOUBLE_QUOTE_STRING_START) and last_in_line:
|
| self._HandleError(errors.MULTI_LINE_STRING,
|
| - 'Multi-line strings are not allowed', token)
|
| -
|
| + 'Multi-line strings are not allowed', token)
|
|
|
| # This check is orthogonal to the ones above, and repeats some types, so
|
| # it is a plain if and not an elif.
|
| if token.type in Type.COMMENT_TYPES:
|
| if self.ILLEGAL_TAB.search(token.string):
|
| self._HandleError(errors.ILLEGAL_TAB,
|
| - 'Illegal tab in comment "%s"' % token.string, token)
|
| + 'Illegal tab in comment "%s"' % token.string, token)
|
|
|
| trimmed = token.string.rstrip()
|
| if last_in_line and token.string != trimmed:
|
| # Check for extra whitespace at the end of a line.
|
| - self._HandleError(errors.EXTRA_SPACE, 'Extra space at end of line',
|
| - token, Position(len(trimmed), len(token.string) - len(trimmed)))
|
| + self._HandleError(
|
| + errors.EXTRA_SPACE, 'Extra space at end of line', token,
|
| + position=Position(len(trimmed), len(token.string) - len(trimmed)))
|
|
|
| # This check is also orthogonal since it is based on metadata.
|
| if token.metadata.is_implied_semicolon:
|
| self._HandleError(errors.MISSING_SEMICOLON,
|
| - 'Missing semicolon at end of line', token)
|
| + 'Missing semicolon at end of line', token)
|
|
|
| def _HandleStartBracket(self, token, last_non_space_token):
|
| """Handles a token that is an open bracket.
|
| @@ -729,8 +752,9 @@ class EcmaScriptLintRules(checkerbase.LintRulesBase):
|
| if (not token.IsFirstInLine() and token.previous.type == Type.WHITESPACE and
|
| last_non_space_token and
|
| last_non_space_token.type in Type.EXPRESSION_ENDER_TYPES):
|
| - self._HandleError(errors.EXTRA_SPACE, 'Extra space before "["',
|
| - token.previous, Position.All(token.previous.string))
|
| + self._HandleError(
|
| + errors.EXTRA_SPACE, 'Extra space before "["',
|
| + token.previous, position=Position.All(token.previous.string))
|
| # If the [ token is the first token in a line we shouldn't complain
|
| # about a missing space before [. This is because some Ecma script
|
| # languages allow syntax like:
|
| @@ -746,29 +770,31 @@ class EcmaScriptLintRules(checkerbase.LintRulesBase):
|
| # should trigger a proper indentation warning message as [ is not indented
|
| # by four spaces.
|
| elif (not token.IsFirstInLine() and token.previous and
|
| - not token.previous.type in (
|
| + token.previous.type not in (
|
| [Type.WHITESPACE, Type.START_PAREN, Type.START_BRACKET] +
|
| Type.EXPRESSION_ENDER_TYPES)):
|
| self._HandleError(errors.MISSING_SPACE, 'Missing space before "["',
|
| - token, Position.AtBeginning())
|
| + token, position=Position.AtBeginning())
|
| +
|
| + def Finalize(self, state):
|
| + """Perform all checks that need to occur after all lines are processed.
|
|
|
| - def Finalize(self, state, tokenizer_mode):
|
| + Args:
|
| + state: State of the parser after parsing all tokens
|
| +
|
| + Raises:
|
| + TypeError: If not overridden.
|
| + """
|
| last_non_space_token = state.GetLastNonSpaceToken()
|
| # Check last line for ending with newline.
|
| - if state.GetLastLine() and not (state.GetLastLine().isspace() or
|
| + if state.GetLastLine() and not (
|
| + state.GetLastLine().isspace() or
|
| state.GetLastLine().rstrip('\n\r\f') != state.GetLastLine()):
|
| self._HandleError(
|
| errors.FILE_MISSING_NEWLINE,
|
| 'File does not end with new line. (%s)' % state.GetLastLine(),
|
| last_non_space_token)
|
|
|
| - # Check that the mode is not mid comment, argument list, etc.
|
| - if not tokenizer_mode == Modes.TEXT_MODE:
|
| - self._HandleError(
|
| - errors.FILE_IN_BLOCK,
|
| - 'File ended in mode "%s".' % tokenizer_mode,
|
| - last_non_space_token)
|
| -
|
| try:
|
| self._indentation.Finalize()
|
| except Exception, e:
|
| @@ -778,7 +804,11 @@ class EcmaScriptLintRules(checkerbase.LintRulesBase):
|
| last_non_space_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 []
|
|
|
| def InExplicitlyTypedLanguage(self):
|
|
|