| 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 6406ff799b63b81cf36a31d820aec740db041445..79c644868cfb9e1786f0ed36b19fe45faebdd110 100755
|
| --- a/third_party/closure_linter/closure_linter/javascriptlintrules.py
|
| +++ b/third_party/closure_linter/closure_linter/javascriptlintrules.py
|
| @@ -62,22 +62,6 @@ class JavaScriptLintRules(ecmalintrules.EcmaScriptLintRules):
|
| self._HandleError(errors.MISSING_PARAMETER_DOCUMENTATION,
|
| 'Missing docs for parameter: "%s"' % param_name, token)
|
|
|
| - def __ContainsRecordType(self, token):
|
| - """Check whether the given token contains a record type.
|
| -
|
| - Args:
|
| - token: The token being checked
|
| -
|
| - Returns:
|
| - True if the token contains a record type, False otherwise.
|
| - """
|
| - # If we see more than one left-brace in the string of an annotation token,
|
| - # then there's a record type in there.
|
| - return (
|
| - token and token.type == Type.DOC_FLAG and
|
| - 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.
|
| @@ -87,14 +71,6 @@ class JavaScriptLintRules(ecmalintrules.EcmaScriptLintRules):
|
| state: parser_state object that indicates the current state in the page
|
| """
|
|
|
| - # 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()
|
| - return
|
| -
|
| # Call the base class's CheckToken function.
|
| super(JavaScriptLintRules, self).CheckToken(token, state)
|
|
|
| @@ -110,10 +86,9 @@ class JavaScriptLintRules(ecmalintrules.EcmaScriptLintRules):
|
| identifier = token.string
|
| 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' or
|
| - doc_comment.GetFlag('suppress').type ==
|
| - 'unusedPrivateMembers'))
|
| + suppressed = doc_comment and (
|
| + 'underscore' in doc_comment.suppressions or
|
| + 'unusedPrivateMembers' in doc_comment.suppressions)
|
| if not suppressed:
|
| # Look for static members defined on a provided namespace.
|
| if namespaces_info:
|
| @@ -151,14 +126,12 @@ class JavaScriptLintRules(ecmalintrules.EcmaScriptLintRules):
|
| 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'):
|
| + if flag.jstype.IsVarArgsType() 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'):
|
| + elif not flag.jstype.IsVarArgsType() and flag.name == 'var_args':
|
| self._HandleError(errors.JSDOC_MISSING_VAR_ARGS_TYPE,
|
| 'Variable length argument %s type must start '
|
| 'with \'...\'.' % flag.name,
|
| @@ -166,13 +139,13 @@ class JavaScriptLintRules(ecmalintrules.EcmaScriptLintRules):
|
|
|
| if error_check.ShouldCheck(Rule.OPTIONAL_TYPE_MARKER):
|
| # Check for optional marker in type.
|
| - if (flag.type.endswith('=') and
|
| + if (flag.jstype.opt_arg 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
|
| + elif (not flag.jstype.opt_arg and
|
| flag.name.startswith('opt_')):
|
| self._HandleError(errors.JSDOC_MISSING_OPTIONAL_TYPE,
|
| 'Optional parameter %s type must end with =.' %
|
| @@ -183,11 +156,8 @@ class JavaScriptLintRules(ecmalintrules.EcmaScriptLintRules):
|
| # Check for both missing type token and empty type braces '{}'
|
| # 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())):
|
| + if (flag.flag_type not in state.GetDocFlag().CAN_OMIT_TYPE
|
| + and (not flag.jstype or flag.jstype.IsEmpty())):
|
| self._HandleError(errors.MISSING_JSDOC_TAG_TYPE,
|
| 'Missing type in %s tag' % token.string, token)
|
|
|
| @@ -319,15 +289,17 @@ class JavaScriptLintRules(ecmalintrules.EcmaScriptLintRules):
|
| function.doc and
|
| function.doc.HasFlag('return') and
|
| not state.InInterfaceMethod()):
|
| - return_flag = function.doc.GetFlag('return')
|
| - if (return_flag.type is None or (
|
| - 'undefined' not in return_flag.type and
|
| - 'void' not in return_flag.type and
|
| - '*' not in return_flag.type)):
|
| + flag = function.doc.GetFlag('return')
|
| + valid_no_return_names = ['undefined', 'void', '*']
|
| + invalid_return = flag.jstype is None or not any(
|
| + sub_type.identifier in valid_no_return_names
|
| + for sub_type in flag.jstype.IterTypeGroup())
|
| +
|
| + if invalid_return:
|
| self._HandleError(
|
| errors.UNNECESSARY_RETURN_DOCUMENTATION,
|
| 'Found @return JsDoc on function that returns nothing',
|
| - return_flag.flag_token, position=Position.AtBeginning())
|
| + flag.flag_token, position=Position.AtBeginning())
|
|
|
| # b/4073735. Method in object literal definition of prototype can
|
| # safely reference 'this'.
|
| @@ -440,12 +412,15 @@ class JavaScriptLintRules(ecmalintrules.EcmaScriptLintRules):
|
| # If there are no require statements, missing requires should be
|
| # reported after the last provide.
|
| if not namespaces_info.GetRequiredNamespaces():
|
| - missing_requires = namespaces_info.GetMissingRequires()
|
| + missing_requires, illegal_alias_statements = (
|
| + namespaces_info.GetMissingRequires())
|
| if missing_requires:
|
| self._ReportMissingRequires(
|
| missing_requires,
|
| tokenutil.GetLastTokenInSameLine(token).next,
|
| True)
|
| + if illegal_alias_statements:
|
| + self._ReportIllegalAliasStatement(illegal_alias_statements)
|
|
|
| elif (token.string == 'goog.require' and
|
| not state.InFunction() and
|
| @@ -477,12 +452,15 @@ class JavaScriptLintRules(ecmalintrules.EcmaScriptLintRules):
|
|
|
| # Report missing goog.require statements.
|
| if namespaces_info.IsLastRequire(token):
|
| - missing_requires = namespaces_info.GetMissingRequires()
|
| + missing_requires, illegal_alias_statements = (
|
| + namespaces_info.GetMissingRequires())
|
| if missing_requires:
|
| self._ReportMissingRequires(
|
| missing_requires,
|
| tokenutil.GetLastTokenInSameLine(token).next,
|
| False)
|
| + if illegal_alias_statements:
|
| + self._ReportIllegalAliasStatement(illegal_alias_statements)
|
|
|
| elif token.type == Type.OPERATOR:
|
| last_in_line = token.IsLastInLine()
|
| @@ -494,6 +472,7 @@ 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 tokenutil.IsDot(token)
|
| and token.next.type not in (Type.WHITESPACE, Type.END_PAREN,
|
| Type.END_BRACKET, Type.SEMICOLON,
|
| Type.START_BRACKET)):
|
| @@ -558,11 +537,11 @@ class JavaScriptLintRules(ecmalintrules.EcmaScriptLintRules):
|
| 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)
|
| + self._MarkLocalVariableUsed(token.string)
|
| 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)
|
| + self._MarkLocalVariableUsed(token.string)
|
| elif token.type == Type.START_BLOCK:
|
| if in_function and state.IsFunctionOpen():
|
| # Push a new map onto the stack
|
| @@ -576,18 +555,40 @@ class JavaScriptLintRules(ecmalintrules.EcmaScriptLintRules):
|
| errors.UNUSED_LOCAL_VARIABLE,
|
| 'Unused local variable: %s.' % unused_token.string,
|
| unused_token)
|
| + elif token.type == Type.DOC_FLAG:
|
| + # Flags that use aliased symbols should be counted.
|
| + flag = token.attached_object
|
| + js_type = flag and flag.jstype
|
| + if flag and flag.flag_type in state.GetDocFlag().HAS_TYPE and js_type:
|
| + self._MarkAliasUsed(js_type)
|
|
|
| - def _MarkLocalVariableUsed(self, token):
|
| - """Marks the local variable as used in the relevant scope.
|
| + def _MarkAliasUsed(self, js_type):
|
| + """Marks aliases in a type as used.
|
|
|
| + Recursively iterates over all subtypes in a jsdoc type annotation and
|
| + tracks usage of aliased symbols (which may be local variables).
|
| 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.
|
| + js_type: The jsdoc type, a typeannotation.TypeAnnotation object.
|
| """
|
| + if js_type.alias:
|
| + self._MarkLocalVariableUsed(js_type.identifier)
|
| + for sub_type in js_type.IterTypes():
|
| + self._MarkAliasUsed(sub_type)
|
| +
|
| + def _MarkLocalVariableUsed(self, identifier):
|
| + """Marks the local variable as used in the relevant scope.
|
| +
|
| + Marks the local variable in the scope nearest to the current scope that
|
| + matches the given identifier as used.
|
|
|
| - identifier = token.string.split('.')[0]
|
| + Args:
|
| + identifier: The identifier representing the potential usage of a local
|
| + variable.
|
| + """
|
| + identifier = identifier.split('.', 1)[0]
|
| # Find the first instance of the identifier in the stack of function scopes
|
| # and mark it used.
|
| for unused_local_variables in reversed(
|
| @@ -658,6 +659,15 @@ class JavaScriptLintRules(ecmalintrules.EcmaScriptLintRules):
|
| token, position=Position.AtBeginning(),
|
| fix_data=(missing_requires.keys(), need_blank_line))
|
|
|
| + def _ReportIllegalAliasStatement(self, illegal_alias_statements):
|
| + """Reports alias statements that would need a goog.require."""
|
| + for namespace, token in illegal_alias_statements.iteritems():
|
| + self._HandleError(
|
| + errors.ALIAS_STMT_NEEDS_GOOG_REQUIRE,
|
| + 'The alias definition would need the namespace \'%s\' which is not '
|
| + 'required through any other symbol.' % namespace,
|
| + token, position=Position.AtBeginning())
|
| +
|
| def Finalize(self, state):
|
| """Perform all checks that need to occur after all lines are processed."""
|
| # Call the base class's Finalize function.
|
| @@ -690,10 +700,12 @@ class JavaScriptLintRules(ecmalintrules.EcmaScriptLintRules):
|
| self._ReportMissingProvides(
|
| missing_provides, state.GetFirstToken(), None)
|
|
|
| - missing_requires = namespaces_info.GetMissingRequires()
|
| + missing_requires, illegal_alias = namespaces_info.GetMissingRequires()
|
| if missing_requires:
|
| self._ReportMissingRequires(
|
| missing_requires, state.GetFirstToken(), None)
|
| + if illegal_alias:
|
| + self._ReportIllegalAliasStatement(illegal_alias)
|
|
|
| self._CheckSortedRequiresProvides(state.GetFirstToken())
|
|
|
| @@ -736,8 +748,8 @@ class JavaScriptLintRules(ecmalintrules.EcmaScriptLintRules):
|
| A list of regexps, used as matches (rather than searches).
|
| """
|
| return [
|
| - re.compile(r'goog\.require\(.+\);?\s*$'),
|
| - re.compile(r'goog\.provide\(.+\);?\s*$'),
|
| - re.compile(r'goog\.setTestOnly\(.+\);?\s*$'),
|
| + re.compile(r'((var|let|const) .+\s*=\s*)?goog\.require\(.+\);?\s*$'),
|
| + re.compile(r'goog\.(forwardDeclare|module|provide|setTestOnly)'
|
| + r'\(.+\);?\s*$'),
|
| re.compile(r'[\s/*]*@visibility\s*{.*}[\s*/]*$'),
|
| ]
|
|
|