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

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

Issue 2328693002: Updated linter with upstream release (2.3.19) (Closed)
Patch Set: Created 4 years, 3 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/ecmalintrules.py
diff --git a/third_party/closure_linter/closure_linter/ecmalintrules.py b/third_party/closure_linter/closure_linter/ecmalintrules.py
index 6ed2e97f5715e006441b7ad0360ca2e095ae895d..80331c25cc4273d5f12a35cbf6e2eeeab3a04de6 100755
--- a/third_party/closure_linter/closure_linter/ecmalintrules.py
+++ b/third_party/closure_linter/closure_linter/ecmalintrules.py
@@ -41,6 +41,13 @@ from closure_linter.common import position
FLAGS = flags.FLAGS
flags.DEFINE_list('custom_jsdoc_tags', '', 'Extra jsdoc tags to allow')
+# TODO(user): When flipping this to True, remove logic from unit tests
+# that overrides this flag.
+flags.DEFINE_boolean('dot_on_next_line', False, 'Require dots to be'
+ 'placed on the next line for wrapped expressions')
+
+flags.DEFINE_boolean('check_trailing_comma', False, 'Check trailing commas'
+ ' (ES3, not needed from ES5 onwards)')
# TODO(robbyw): Check for extra parens on return statements
# TODO(robbyw): Check for 0px in strings
@@ -92,7 +99,7 @@ class EcmaScriptLintRules(checkerbase.LintRulesBase):
['@%s' % tag for tag in statetracker.DocFlag.HAS_TYPE])
JSDOC_FLAGS_DESCRIPTION_NOT_REQUIRED = frozenset([
- '@param', '@return', '@returns'])
+ '@fileoverview', '@param', '@return', '@returns'])
def __init__(self):
"""Initialize this lint rule object."""
@@ -128,8 +135,8 @@ class EcmaScriptLintRules(checkerbase.LintRulesBase):
while token and token.line_number == line_number:
if state.IsTypeToken(token):
line.insert(0, 'x' * len(token.string))
- elif token.type in (Type.IDENTIFIER, Type.NORMAL):
- # Dots are acceptable places to wrap.
+ elif token.type in (Type.IDENTIFIER, Type.OPERATOR):
+ # Dots are acceptable places to wrap (may be tokenized as identifiers).
line.insert(0, token.string.replace('.', ' '))
else:
line.insert(0, token.string)
@@ -172,31 +179,29 @@ class EcmaScriptLintRules(checkerbase.LintRulesBase):
errors.LINE_TOO_LONG,
'Line too long (%d characters).' % len(line), last_token)
- def _CheckJsDocType(self, token):
+ def _CheckJsDocType(self, token, js_type):
"""Checks the given type for style errors.
Args:
token: The DOC_FLAG token for the flag whose type to check.
+ js_type: The flag's typeannotation.TypeAnnotation instance.
"""
- flag = token.attached_object
- 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)
+ if not js_type: return
- # 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 js_type.type_group and len(js_type.sub_types) == 2:
+ identifiers = [t.identifier for t in js_type.sub_types]
+ if 'null' in identifiers:
+ # Don't warn if the identifier is a template type (e.g. {TYPE|null}.
+ if not identifiers[0].isupper() and not identifiers[1].isupper():
+ self._HandleError(
+ errors.JSDOC_PREFER_QUESTION_TO_PIPE_NULL,
+ 'Prefer "?Type" to "Type|null": "%s"' % js_type, token)
- 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,
- 'Type must always be surrounded by curly braces.', token)
+ # TODO(user): We should report an error for wrong usage of '?' and '|'
+ # e.g. {?number|string|null} etc.
+
+ for sub_type in js_type.IterTypes():
+ self._CheckJsDocType(token, sub_type)
def _CheckForMissingSpaceBeforeToken(self, token):
"""Checks for a missing space at the beginning of a token.
@@ -228,26 +233,49 @@ class EcmaScriptLintRules(checkerbase.LintRulesBase):
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)):
+ last_code and last_code.type in (Type.NORMAL, Type.IDENTIFIER) and
+ last_code.line_number == token.line_number):
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
+ not tokenutil.IsDot(token) 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
+ # Check wrapping of operators.
+ next_code = tokenutil.GetNextCodeToken(token)
+
+ is_dot = tokenutil.IsDot(token)
+ wrapped_before = last_code and last_code.line_number != token.line_number
+ wrapped_after = next_code and next_code.line_number != token.line_number
+
+ if FLAGS.dot_on_next_line and is_dot and wrapped_after:
+ self._HandleError(
+ errors.LINE_ENDS_WITH_DOT,
+ '"." must go on the following line',
+ token)
+ if (not is_dot and wrapped_before and
not token.metadata.IsUnaryOperator()):
self._HandleError(
errors.LINE_STARTS_WITH_OPERATOR,
- 'Binary operator should go on previous line "%s"' % token.string,
+ 'Binary operator must go on previous line "%s"' % token.string,
token)
+ def _IsLabel(self, token):
+ # A ':' token is considered part of a label if it occurs in a case
+ # statement, a plain label, or an object literal, i.e. is not part of a
+ # ternary.
+
+ return (token.string == ':' and
+ token.metadata.context.type in (Context.LITERAL_ELEMENT,
+ Context.CASE_BLOCK,
+ Context.STATEMENT))
+
def _ExpectSpaceBeforeOperator(self, token):
"""Returns whether a space should appear before the given operator token.
@@ -260,13 +288,13 @@ class EcmaScriptLintRules(checkerbase.LintRulesBase):
if token.string == ',' or token.metadata.IsUnaryPostOperator():
return False
+ if tokenutil.IsDot(token):
+ return False
+
# Colons should appear in labels, object literals, the case of a switch
# statement, and ternary operator. Only want a space in the case of the
# ternary operator.
- if (token.string == ':' and
- token.metadata.context.type in (Context.LITERAL_ELEMENT,
- Context.CASE_BLOCK,
- Context.STATEMENT)):
+ if self._IsLabel(token):
return False
if token.metadata.IsUnaryOperator() and token.IsFirstInLine():
@@ -318,19 +346,16 @@ class EcmaScriptLintRules(checkerbase.LintRulesBase):
self._CheckForMissingSpaceBeforeToken(token)
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,
- 'Illegal comma at end of object literal', last_code,
- position=Position.All(last_code.string))
+
+ if FLAGS.check_trailing_comma:
+ if last_code.IsOperator(','):
+ self._HandleError(
+ errors.COMMA_AT_END_OF_LITERAL,
+ 'Illegal comma at end of object literal', last_code,
+ 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():
# A semicolons should not be included at the end of a function
# declaration.
@@ -342,10 +367,14 @@ class EcmaScriptLintRules(checkerbase.LintRulesBase):
token.next, position=Position.All(token.next.string))
# 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):
+ # that is not immediately called or used by a dot operator.
+ if (state.InAssignedFunction() and token.next
+ and token.next.type != Type.SEMICOLON):
+ next_token = tokenutil.GetNextCodeToken(token)
+ is_immediately_used = next_token and (
+ next_token.type == Type.START_PAREN or
+ tokenutil.IsDot(next_token))
+ if not is_immediately_used:
self._HandleError(
errors.MISSING_SEMICOLON_AFTER_FUNCTION,
'Missing semicolon after function assigned to a variable',
@@ -402,13 +431,25 @@ class EcmaScriptLintRules(checkerbase.LintRulesBase):
token, position=Position.All(token.string))
elif token_type == Type.START_PAREN:
- if token.previous and token.previous.type == Type.KEYWORD:
+ # Ensure that opening parentheses have a space before any keyword
+ # that is not being invoked like a member function.
+ if (token.previous and token.previous.type == Type.KEYWORD and
+ (not token.previous.metadata or
+ not token.previous.metadata.last_code or
+ not token.previous.metadata.last_code.string or
+ token.previous.metadata.last_code.string[-1:] != '.')):
self._HandleError(errors.MISSING_SPACE, 'Missing space before "("',
token, position=Position.AtBeginning())
elif token.previous and token.previous.type == Type.WHITESPACE:
before_space = token.previous.previous
+ # Ensure that there is no extra space before a function invocation,
+ # even if the function being invoked happens to be a keyword.
if (before_space and before_space.line_number == token.line_number and
- before_space.type == Type.IDENTIFIER):
+ before_space.type == Type.IDENTIFIER or
+ (before_space.type == Type.KEYWORD and before_space.metadata and
+ before_space.metadata.last_code and
+ before_space.metadata.last_code.string and
+ before_space.metadata.last_code.string[-1:] == '.')):
self._HandleError(
errors.EXTRA_SPACE, 'Extra space before "("',
token.previous, position=Position.All(token.previous.string))
@@ -419,6 +460,15 @@ class EcmaScriptLintRules(checkerbase.LintRulesBase):
# 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.
+
+ last_code = token.metadata.last_code
+ if FLAGS.check_trailing_comma and token_type == Type.END_BRACKET:
+ if last_code.IsOperator(','):
+ self._HandleError(
+ errors.COMMA_AT_END_OF_LITERAL,
+ 'Illegal comma at end of array literal', last_code,
+ position=Position.All(last_code.string))
+
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 ==
@@ -429,14 +479,6 @@ class EcmaScriptLintRules(checkerbase.LintRulesBase):
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,
- 'Illegal comma at end of array literal', last_code,
- position=Position.All(last_code.string))
-
elif token_type == Type.WHITESPACE:
if self.ILLEGAL_TAB.search(token.string):
if token.IsFirstInLine():
@@ -492,7 +534,7 @@ class EcmaScriptLintRules(checkerbase.LintRulesBase):
'Invalid suppress syntax: should be @suppress {errortype}. '
'Spaces matter.', token)
else:
- for suppress_type in re.split(r'\||,', flag.type):
+ for suppress_type in flag.jstype.IterIdentifiers():
if suppress_type not in state.GetDocFlag().SUPPRESS_TYPES:
self._HandleError(
errors.INVALID_SUPPRESS_TYPE,
@@ -551,13 +593,20 @@ class EcmaScriptLintRules(checkerbase.LintRulesBase):
else:
self._CheckForMissingSpaceBeforeToken(flag.description_start_token)
- if flag.flag_type in state.GetDocFlag().HAS_TYPE:
+ if flag.HasType():
if flag.type_start_token is not None:
self._CheckForMissingSpaceBeforeToken(
token.attached_object.type_start_token)
- if flag.type and not flag.type.isspace():
- self._CheckJsDocType(token)
+ if flag.jstype and not flag.jstype.IsEmpty():
+ self._CheckJsDocType(token, flag.jstype)
+
+ 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,
+ 'Type must always be surrounded by curly braces.', token)
if token_type in (Type.DOC_FLAG, Type.DOC_INLINE_FLAG):
if (token.values['name'] not in state.GetDocFlag().LEGAL_DOC and
@@ -637,14 +686,14 @@ class EcmaScriptLintRules(checkerbase.LintRulesBase):
# These flags are only legal on localizable message definitions;
# 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,
- 'Member "%s" should not have @%s JsDoc' % (identifier, f),
- token)
+ if not identifier.startswith('MSG_') and '.MSG_' not in identifier:
+ for f in ('desc', 'hidden', 'meaning'):
+ if jsdoc.HasFlag(f):
+ self._HandleError(
+ errors.INVALID_USE_OF_DESC_TAG,
+ 'Member "%s" does not start with MSG_ and thus '
+ 'should not have @%s JsDoc' % (identifier, f),
+ token)
# Check for illegaly assigning live objects as prototype property values.
index = identifier.find('.prototype.')

Powered by Google App Engine
This is Rietveld 408576698