15821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#!/usr/bin/env python 25821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# Copyright 2011 The Closure Linter Authors. All Rights Reserved. 35821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# 45821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# Licensed under the Apache License, Version 2.0 (the "License"); 55821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# you may not use this file except in compliance with the License. 65821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# You may obtain a copy of the License at 75821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# 85821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# http://www.apache.org/licenses/LICENSE-2.0 95821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# 105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# Unless required by applicable law or agreed to in writing, software 115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# distributed under the License is distributed on an "AS-IS" BASIS, 125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# See the License for the specific language governing permissions and 145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# limitations under the License. 155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)"""Methods for checking JS files for common style guide violations. 175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)These style guide violations should only apply to JavaScript and not an Ecma 195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)scripting languages. 205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)""" 215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)__author__ = ('robbyw@google.com (Robert Walker)', 235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 'ajp@google.com (Andy Perelson)', 245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 'jacobr@google.com (Jacob Richman)') 255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)import re 275f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) 285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)from closure_linter import ecmalintrules 295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)from closure_linter import error_check 305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)from closure_linter import errors 315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)from closure_linter import javascripttokenizer 325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)from closure_linter import javascripttokens 335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)from closure_linter import requireprovidesorter 345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)from closure_linter import tokenutil 355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)from closure_linter.common import error 365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)from closure_linter.common import position 375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# Shorthand 395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)Error = error.Error 405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)Position = position.Position 415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)Rule = error_check.Rule 425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)Type = javascripttokens.JavaScriptTokenType 435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)class JavaScriptLintRules(ecmalintrules.EcmaScriptLintRules): 465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) """JavaScript lint rules that catch JavaScript specific style errors.""" 475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) def __init__(self, namespaces_info): 495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) """Initializes a JavaScriptLintRules instance.""" 505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) ecmalintrules.EcmaScriptLintRules.__init__(self) 515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) self._namespaces_info = namespaces_info 525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) self._declared_private_member_tokens = {} 535f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) self._declared_private_members = set() 545f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) self._used_private_members = set() 555f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) # A stack of dictionaries, one for each function scope entered. Each 565f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) # dictionary is keyed by an identifier that defines a local variable and has 575f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) # a token as its value. 585f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) self._unused_local_variables_by_scope = [] 595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) def HandleMissingParameterDoc(self, token, param_name): 615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) """Handle errors associated with a parameter missing a param tag.""" 625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) self._HandleError(errors.MISSING_PARAMETER_DOCUMENTATION, 635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 'Missing docs for parameter: "%s"' % param_name, token) 645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) def __ContainsRecordType(self, token): 665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) """Check whether the given token contains a record type. 675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) Args: 695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) token: The token being checked 705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) Returns: 725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) True if the token contains a record type, False otherwise. 735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) """ 745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) # If we see more than one left-brace in the string of an annotation token, 755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) # then there's a record type in there. 765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return ( 775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) token and token.type == Type.DOC_FLAG and 785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) token.attached_object.type is not None and 795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) token.attached_object.type.find('{') != token.string.rfind('{')) 805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 815f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) # pylint: disable=too-many-statements 825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) def CheckToken(self, token, state): 835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) """Checks a token, given the current parser_state, for warnings and errors. 845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) Args: 865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) token: The current token under consideration 875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) state: parser_state object that indicates the current state in the page 885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) """ 895f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) 905f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) # For @param don't ignore record type. 915f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) if (self.__ContainsRecordType(token) and 925f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) token.attached_object.flag_type != 'param'): 935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) # We should bail out and not emit any warnings for this annotation. 945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) # TODO(nicksantos): Support record types for real. 955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) state.GetDocComment().Invalidate() 965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return 975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) # Call the base class's CheckToken function. 995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) super(JavaScriptLintRules, self).CheckToken(token, state) 1005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) # Store some convenience variables 1025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) namespaces_info = self._namespaces_info 1035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1045f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) if error_check.ShouldCheck(Rule.UNUSED_LOCAL_VARIABLES): 1055f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) self._CheckUnusedLocalVariables(token, state) 1065f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) 1075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if error_check.ShouldCheck(Rule.UNUSED_PRIVATE_MEMBERS): 1085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) # Find all assignments to private members. 1095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if token.type == Type.SIMPLE_LVALUE: 1105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) identifier = token.string 1115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if identifier.endswith('_') and not identifier.endswith('__'): 1125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) doc_comment = state.GetDocComment() 1135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) suppressed = (doc_comment and doc_comment.HasFlag('suppress') and 1145f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) (doc_comment.GetFlag('suppress').type == 'underscore' or 1155f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) doc_comment.GetFlag('suppress').type == 1165f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) 'unusedPrivateMembers')) 1175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if not suppressed: 1185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) # Look for static members defined on a provided namespace. 1195f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) if namespaces_info: 1205f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) namespace = namespaces_info.GetClosurizedNamespace(identifier) 1215f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) provided_namespaces = namespaces_info.GetProvidedNamespaces() 1225f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) else: 1235f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) namespace = None 1245f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) provided_namespaces = set() 1255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) # Skip cases of this.something_.somethingElse_. 1275f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) regex = re.compile(r'^this\.[a-zA-Z_]+$') 1285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if namespace in provided_namespaces or regex.match(identifier): 1295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) variable = identifier.split('.')[-1] 1305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) self._declared_private_member_tokens[variable] = token 1315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) self._declared_private_members.add(variable) 1325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) elif not identifier.endswith('__'): 1335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) # Consider setting public members of private members to be a usage. 1345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) for piece in identifier.split('.'): 1355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if piece.endswith('_'): 1365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) self._used_private_members.add(piece) 1375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) # Find all usages of private members. 1395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if token.type == Type.IDENTIFIER: 1405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) for piece in token.string.split('.'): 1415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if piece.endswith('_'): 1425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) self._used_private_members.add(piece) 1435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if token.type == Type.DOC_FLAG: 1455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) flag = token.attached_object 1465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if flag.flag_type == 'param' and flag.name_token is not None: 1485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) self._CheckForMissingSpaceBeforeToken( 1495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) token.attached_object.name_token) 1505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1515f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) if flag.type is not None and flag.name is not None: 1525f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) if error_check.ShouldCheck(Rule.VARIABLE_ARG_MARKER): 1535f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) # Check for variable arguments marker in type. 1545f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) if (flag.type.startswith('...') and 1555f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) flag.name != 'var_args'): 1565f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) self._HandleError(errors.JSDOC_MISSING_VAR_ARGS_NAME, 1575f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) 'Variable length argument %s must be renamed ' 1585f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) 'to var_args.' % flag.name, 1595f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) token) 1605f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) elif (not flag.type.startswith('...') and 1615f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) flag.name == 'var_args'): 1625f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) self._HandleError(errors.JSDOC_MISSING_VAR_ARGS_TYPE, 1635f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) 'Variable length argument %s type must start ' 1645f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) 'with \'...\'.' % flag.name, 1655f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) token) 1665f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) 1675f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) if error_check.ShouldCheck(Rule.OPTIONAL_TYPE_MARKER): 1685f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) # Check for optional marker in type. 1695f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) if (flag.type.endswith('=') and 1705f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) not flag.name.startswith('opt_')): 1715f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) self._HandleError(errors.JSDOC_MISSING_OPTIONAL_PREFIX, 1725f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) 'Optional parameter name %s must be prefixed ' 1735f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) 'with opt_.' % flag.name, 1745f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) token) 1755f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) elif (not flag.type.endswith('=') and 1765f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) flag.name.startswith('opt_')): 1775f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) self._HandleError(errors.JSDOC_MISSING_OPTIONAL_TYPE, 1785f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) 'Optional parameter %s type must end with =.' % 1795f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) flag.name, 1805f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) token) 1815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if flag.flag_type in state.GetDocFlag().HAS_TYPE: 1835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) # Check for both missing type token and empty type braces '{}' 1845f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) # Missing suppress types are reported separately and we allow enums, 1855f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) # const, private, public and protected without types. 1865f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) allowed_flags = set(['suppress']).union( 1875f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) state.GetDocFlag().CAN_OMIT_TYPE) 1885f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) 1895f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) if (flag.flag_type not in allowed_flags and 1905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) (not flag.type or flag.type.isspace())): 1915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) self._HandleError(errors.MISSING_JSDOC_TAG_TYPE, 1925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 'Missing type in %s tag' % token.string, token) 1935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) elif flag.name_token and flag.type_end_token and tokenutil.Compare( 1955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) flag.type_end_token, flag.name_token) > 0: 1965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) self._HandleError( 1975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) errors.OUT_OF_ORDER_JSDOC_TAG_TYPE, 1985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 'Type should be immediately after %s tag' % token.string, 1995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) token) 2005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 2015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) elif token.type == Type.DOUBLE_QUOTE_STRING_START: 2025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) next_token = token.next 2035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) while next_token.type == Type.STRING_TEXT: 2045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if javascripttokenizer.JavaScriptTokenizer.SINGLE_QUOTE.search( 2055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) next_token.string): 2065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) break 2075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) next_token = next_token.next 2085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) else: 2095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) self._HandleError( 2105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) errors.UNNECESSARY_DOUBLE_QUOTED_STRING, 2115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 'Single-quoted string preferred over double-quoted string.', 2125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) token, 2135f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) position=Position.All(token.string)) 2145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 2155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) elif token.type == Type.END_DOC_COMMENT: 2165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) doc_comment = state.GetDocComment() 2175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 2185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) # When @externs appears in a @fileoverview comment, it should trigger 2195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) # the same limited doc checks as a special filename like externs.js. 2205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if doc_comment.HasFlag('fileoverview') and doc_comment.HasFlag('externs'): 2215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) self._SetLimitedDocChecks(True) 2225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 2235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (error_check.ShouldCheck(Rule.BLANK_LINES_AT_TOP_LEVEL) and 2245f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) not self._is_html and 2255f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) state.InTopLevel() and 2265f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) not state.InNonScopeBlock()): 2275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 2285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) # Check if we're in a fileoverview or constructor JsDoc. 2295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) is_constructor = ( 2305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) doc_comment.HasFlag('constructor') or 2315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) doc_comment.HasFlag('interface')) 2325f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) # @fileoverview is an optional tag so if the dosctring is the first 2335f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) # token in the file treat it as a file level docstring. 2345f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) is_file_level_comment = ( 2355f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) doc_comment.HasFlag('fileoverview') or 2365f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) not doc_comment.start_token.previous) 2375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 2385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) # If the comment is not a file overview, and it does not immediately 2395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) # precede some code, skip it. 2405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) # NOTE: The tokenutil methods are not used here because of their 2415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) # behavior at the top of a file. 2425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) next_token = token.next 2435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (not next_token or 2445f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) (not is_file_level_comment and 2455f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) next_token.type in Type.NON_CODE_TYPES)): 2465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return 2475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 2485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) # Don't require extra blank lines around suppression of extra 2495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) # goog.require errors. 2505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (doc_comment.SuppressionOnly() and 2515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) next_token.type == Type.IDENTIFIER and 2525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) next_token.string in ['goog.provide', 'goog.require']): 2535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return 2545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 2555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) # Find the start of this block (include comments above the block, unless 2565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) # this is a file overview). 2575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) block_start = doc_comment.start_token 2585f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) if not is_file_level_comment: 2595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) token = block_start.previous 2605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) while token and token.type in Type.COMMENT_TYPES: 2615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) block_start = token 2625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) token = token.previous 2635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 2645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) # Count the number of blank lines before this block. 2655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) blank_lines = 0 2665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) token = block_start.previous 2675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) while token and token.type in [Type.WHITESPACE, Type.BLANK_LINE]: 2685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if token.type == Type.BLANK_LINE: 2695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) # A blank line. 2705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) blank_lines += 1 2715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) elif token.type == Type.WHITESPACE and not token.line.strip(): 2725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) # A line with only whitespace on it. 2735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) blank_lines += 1 2745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) token = token.previous 2755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 2765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) # Log errors. 2775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) error_message = False 2785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) expected_blank_lines = 0 2795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 2805f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) # Only need blank line before file overview if it is not the beginning 2815f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) # of the file, e.g. copyright is first. 2825f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) if is_file_level_comment and blank_lines == 0 and block_start.previous: 2835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) error_message = 'Should have a blank line before a file overview.' 2845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) expected_blank_lines = 1 2855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) elif is_constructor and blank_lines != 3: 2865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) error_message = ( 2875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 'Should have 3 blank lines before a constructor/interface.') 2885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) expected_blank_lines = 3 2895f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) elif (not is_file_level_comment and not is_constructor and 2905f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) blank_lines != 2): 2915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) error_message = 'Should have 2 blank lines between top-level blocks.' 2925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) expected_blank_lines = 2 2935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 2945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if error_message: 2955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) self._HandleError( 2965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) errors.WRONG_BLANK_LINE_COUNT, error_message, 2975f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) block_start, position=Position.AtBeginning(), 2985f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) fix_data=expected_blank_lines - blank_lines) 2995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 3005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) elif token.type == Type.END_BLOCK: 3015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if state.InFunction() and state.IsFunctionClose(): 3025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) is_immediately_called = (token.next and 3035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) token.next.type == Type.START_PAREN) 3045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 3055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) function = state.GetFunction() 3065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if not self._limited_doc_checks: 3075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (function.has_return and function.doc and 3085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) not is_immediately_called and 3095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) not function.doc.HasFlag('return') and 3105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) not function.doc.InheritsDocumentation() and 3115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) not function.doc.HasFlag('constructor')): 3125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) # Check for proper documentation of return value. 3135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) self._HandleError( 3145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) errors.MISSING_RETURN_DOCUMENTATION, 3155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 'Missing @return JsDoc in function with non-trivial return', 3165f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) function.doc.end_token, position=Position.AtBeginning()) 3175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) elif (not function.has_return and 3185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) not function.has_throw and 3195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) function.doc and 3205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) function.doc.HasFlag('return') and 3215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) not state.InInterfaceMethod()): 3225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return_flag = function.doc.GetFlag('return') 3235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (return_flag.type is None or ( 3245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 'undefined' not in return_flag.type and 3255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 'void' not in return_flag.type and 3265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) '*' not in return_flag.type)): 3275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) self._HandleError( 3285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) errors.UNNECESSARY_RETURN_DOCUMENTATION, 3295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 'Found @return JsDoc on function that returns nothing', 3305f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) return_flag.flag_token, position=Position.AtBeginning()) 3315f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) 3325f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) # b/4073735. Method in object literal definition of prototype can 3335f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) # safely reference 'this'. 3345f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) prototype_object_literal = False 3355f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) block_start = None 3365f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) previous_code = None 3375f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) previous_previous_code = None 3385f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) 3395f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) # Search for cases where prototype is defined as object literal. 3405f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) # previous_previous_code 3415f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) # | previous_code 3425f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) # | | block_start 3435f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) # | | | 3445f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) # a.b.prototype = { 3455f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) # c : function() { 3465f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) # this.d = 1; 3475f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) # } 3485f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) # } 3495f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) 3505f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) # If in object literal, find first token of block so to find previous 3515f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) # tokens to check above condition. 3525f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) if state.InObjectLiteral(): 3535f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) block_start = state.GetCurrentBlockStart() 3545f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) 3555f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) # If an object literal then get previous token (code type). For above 3565f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) # case it should be '='. 3575f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) if block_start: 3585f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) previous_code = tokenutil.SearchExcept(block_start, 3595f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) Type.NON_CODE_TYPES, 3605f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) reverse=True) 3615f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) 3625f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) # If previous token to block is '=' then get its previous token. 3635f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) if previous_code and previous_code.IsOperator('='): 3645f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) previous_previous_code = tokenutil.SearchExcept(previous_code, 3655f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) Type.NON_CODE_TYPES, 3665f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) reverse=True) 3675f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) 3685f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) # If variable/token before '=' ends with '.prototype' then its above 3695f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) # case of prototype defined with object literal. 3705f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) prototype_object_literal = (previous_previous_code and 3715f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) previous_previous_code.string.endswith( 3725f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) '.prototype')) 3735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 3745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (function.has_this and function.doc and 3755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) not function.doc.HasFlag('this') and 3765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) not function.is_constructor and 3775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) not function.is_interface and 3785f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) '.prototype.' not in function.name and 3795f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) not prototype_object_literal): 3805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) self._HandleError( 3815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) errors.MISSING_JSDOC_TAG_THIS, 3825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 'Missing @this JsDoc in function referencing "this". (' 3835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 'this usually means you are trying to reference "this" in ' 3845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 'a static function, or you have forgotten to mark a ' 3855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 'constructor with @constructor)', 3865f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) function.doc.end_token, position=Position.AtBeginning()) 3875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 3885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) elif token.type == Type.IDENTIFIER: 3895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if token.string == 'goog.inherits' and not state.InFunction(): 3905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if state.GetLastNonSpaceToken().line_number == token.line_number: 3915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) self._HandleError( 3925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) errors.MISSING_LINE, 3935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 'Missing newline between constructor and goog.inherits', 3945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) token, 3955f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) position=Position.AtBeginning()) 3965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 3975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) extra_space = state.GetLastNonSpaceToken().next 3985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) while extra_space != token: 3995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if extra_space.type == Type.BLANK_LINE: 4005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) self._HandleError( 4015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) errors.EXTRA_LINE, 4025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 'Extra line between constructor and goog.inherits', 4035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) extra_space) 4045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) extra_space = extra_space.next 4055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 4065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) # TODO(robbyw): Test the last function was a constructor. 4075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) # TODO(robbyw): Test correct @extends and @implements documentation. 4085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 4095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) elif (token.string == 'goog.provide' and 4105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) not state.InFunction() and 4115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) namespaces_info is not None): 4125f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) namespace = tokenutil.GetStringAfterToken(token) 4135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 4145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) # Report extra goog.provide statement. 4155f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) if not namespace or namespaces_info.IsExtraProvide(token): 4165f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) if not namespace: 4175f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) msg = 'Empty namespace in goog.provide' 4185f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) else: 4195f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) msg = 'Unnecessary goog.provide: ' + namespace 4205f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) 4215f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) # Hint to user if this is a Test namespace. 4225f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) if namespace.endswith('Test'): 4235f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) msg += (' *Test namespaces must be mentioned in the ' 4245f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) 'goog.setTestOnly() call') 4255f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) 4265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) self._HandleError( 4275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) errors.EXTRA_GOOG_PROVIDE, 4285f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) msg, 4295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) token, position=Position.AtBeginning()) 4305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 4315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if namespaces_info.IsLastProvide(token): 4325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) # Report missing provide statements after the last existing provide. 4335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) missing_provides = namespaces_info.GetMissingProvides() 4345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if missing_provides: 4355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) self._ReportMissingProvides( 4365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) missing_provides, 4375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) tokenutil.GetLastTokenInSameLine(token).next, 4385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) False) 4395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 4405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) # If there are no require statements, missing requires should be 4415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) # reported after the last provide. 4425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if not namespaces_info.GetRequiredNamespaces(): 4435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) missing_requires = namespaces_info.GetMissingRequires() 4445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if missing_requires: 4455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) self._ReportMissingRequires( 4465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) missing_requires, 4475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) tokenutil.GetLastTokenInSameLine(token).next, 4485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) True) 4495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 4505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) elif (token.string == 'goog.require' and 4515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) not state.InFunction() and 4525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) namespaces_info is not None): 4535f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) namespace = tokenutil.GetStringAfterToken(token) 4545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 4555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) # If there are no provide statements, missing provides should be 4565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) # reported before the first require. 4575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (namespaces_info.IsFirstRequire(token) and 4585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) not namespaces_info.GetProvidedNamespaces()): 4595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) missing_provides = namespaces_info.GetMissingProvides() 4605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if missing_provides: 4615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) self._ReportMissingProvides( 4625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) missing_provides, 4635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) tokenutil.GetFirstTokenInSameLine(token), 4645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) True) 4655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 4665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) # Report extra goog.require statement. 4675f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) if not namespace or namespaces_info.IsExtraRequire(token): 4685f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) if not namespace: 4695f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) msg = 'Empty namespace in goog.require' 4705f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) else: 4715f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) msg = 'Unnecessary goog.require: ' + namespace 4725f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) 4735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) self._HandleError( 4745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) errors.EXTRA_GOOG_REQUIRE, 4755f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) msg, 4765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) token, position=Position.AtBeginning()) 4775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 4785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) # Report missing goog.require statements. 4795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if namespaces_info.IsLastRequire(token): 4805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) missing_requires = namespaces_info.GetMissingRequires() 4815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if missing_requires: 4825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) self._ReportMissingRequires( 4835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) missing_requires, 4845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) tokenutil.GetLastTokenInSameLine(token).next, 4855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) False) 4865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 4875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) elif token.type == Type.OPERATOR: 4885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) last_in_line = token.IsLastInLine() 4895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) # If the token is unary and appears to be used in a unary context 4905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) # it's ok. Otherwise, if it's at the end of the line or immediately 4915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) # before a comment, it's ok. 4925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) # Don't report an error before a start bracket - it will be reported 4935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) # by that token's space checks. 4945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (not token.metadata.IsUnaryOperator() and not last_in_line 4955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) and not token.next.IsComment() 4965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) and not token.next.IsOperator(',') 4975f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) and token.next.type not in (Type.WHITESPACE, Type.END_PAREN, 4985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) Type.END_BRACKET, Type.SEMICOLON, 4995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) Type.START_BRACKET)): 5005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) self._HandleError( 5015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) errors.MISSING_SPACE, 5025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 'Missing space after "%s"' % token.string, 5035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) token, 5045f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) position=Position.AtEnd(token.string)) 5055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) elif token.type == Type.WHITESPACE: 5065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) first_in_line = token.IsFirstInLine() 5075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) last_in_line = token.IsLastInLine() 5085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) # Check whitespace length if it's not the first token of the line and 5095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) # if it's not immediately before a comment. 5105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if not last_in_line and not first_in_line and not token.next.IsComment(): 5115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) # Ensure there is no space after opening parentheses. 5125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (token.previous.type in (Type.START_PAREN, Type.START_BRACKET, 5135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) Type.FUNCTION_NAME) 5145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) or token.next.type == Type.START_PARAMETERS): 5155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) self._HandleError( 5165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) errors.EXTRA_SPACE, 5175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 'Extra space after "%s"' % token.previous.string, 5185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) token, 5195f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) position=Position.All(token.string)) 5205f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) elif token.type == Type.SEMICOLON: 5215f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) previous_token = tokenutil.SearchExcept(token, Type.NON_CODE_TYPES, 5225f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) reverse=True) 5235f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) if not previous_token: 5245f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) self._HandleError( 5255f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) errors.REDUNDANT_SEMICOLON, 5265f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) 'Semicolon without any statement', 5275f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) token, 5285f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) position=Position.AtEnd(token.string)) 5295f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) elif (previous_token.type == Type.KEYWORD and 5305f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) previous_token.string not in ['break', 'continue', 'return']): 5315f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) self._HandleError( 5325f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) errors.REDUNDANT_SEMICOLON, 5335f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) ('Semicolon after \'%s\' without any statement.' 5345f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) ' Looks like an error.' % previous_token.string), 5355f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) token, 5365f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) position=Position.AtEnd(token.string)) 5375f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) 5385f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) def _CheckUnusedLocalVariables(self, token, state): 5395f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) """Checks for unused local variables in function blocks. 5405f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) 5415f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) Args: 5425f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) token: The token to check. 5435f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) state: The state tracker. 5445f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) """ 5455f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) # We don't use state.InFunction because that disregards scope functions. 5465f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) in_function = state.FunctionDepth() > 0 5475f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) if token.type == Type.SIMPLE_LVALUE or token.type == Type.IDENTIFIER: 5485f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) if in_function: 5495f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) identifier = token.string 5505f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) # Check whether the previous token was var. 5515f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) previous_code_token = tokenutil.CustomSearch( 5525f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) token, 5535f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) lambda t: t.type not in Type.NON_CODE_TYPES, 5545f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) reverse=True) 5555f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) if previous_code_token and previous_code_token.IsKeyword('var'): 5565f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) # Add local variable declaration to the top of the unused locals 5575f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) # stack. 5585f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) self._unused_local_variables_by_scope[-1][identifier] = token 5595f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) elif token.type == Type.IDENTIFIER: 5605f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) # This covers most cases where the variable is used as an identifier. 5615f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) self._MarkLocalVariableUsed(token) 5625f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) elif token.type == Type.SIMPLE_LVALUE and '.' in identifier: 5635f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) # This covers cases where a value is assigned to a property of the 5645f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) # variable. 5655f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) self._MarkLocalVariableUsed(token) 5665f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) elif token.type == Type.START_BLOCK: 5675f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) if in_function and state.IsFunctionOpen(): 5685f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) # Push a new map onto the stack 5695f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) self._unused_local_variables_by_scope.append({}) 5705f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) elif token.type == Type.END_BLOCK: 5715f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) if state.IsFunctionClose(): 5725f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) # Pop the stack and report any remaining locals as unused. 5735f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) unused_local_variables = self._unused_local_variables_by_scope.pop() 5745f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) for unused_token in unused_local_variables.values(): 5755f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) self._HandleError( 5765f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) errors.UNUSED_LOCAL_VARIABLE, 5775f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) 'Unused local variable: %s.' % unused_token.string, 5785f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) unused_token) 5795f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) 5805f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) def _MarkLocalVariableUsed(self, token): 5815f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) """Marks the local variable as used in the relevant scope. 5825f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) 5835f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) Marks the local variable as used in the scope nearest to the current 5845f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) scope that matches the given token. 5855f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) 5865f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) Args: 5875f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) token: The token representing the potential usage of a local variable. 5885f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) """ 5895f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) 5905f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) identifier = token.string.split('.')[0] 5915f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) # Find the first instance of the identifier in the stack of function scopes 5925f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) # and mark it used. 5935f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) for unused_local_variables in reversed( 5945f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) self._unused_local_variables_by_scope): 5955f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) if identifier in unused_local_variables: 5965f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) del unused_local_variables[identifier] 5975f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) break 5985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 5995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) def _ReportMissingProvides(self, missing_provides, token, need_blank_line): 6005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) """Reports missing provide statements to the error handler. 6015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 6025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) Args: 6035f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) missing_provides: A dictionary of string(key) and integer(value) where 6045f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) each string(key) is a namespace that should be provided, but is not 6055f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) and integer(value) is first line number where it's required. 6065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) token: The token where the error was detected (also where the new provides 6075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) will be inserted. 6085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) need_blank_line: Whether a blank line needs to be inserted after the new 6095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) provides are inserted. May be True, False, or None, where None 6105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) indicates that the insert location is unknown. 6115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) """ 6125f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) 6135f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) missing_provides_msg = 'Missing the following goog.provide statements:\n' 6145f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) missing_provides_msg += '\n'.join(['goog.provide(\'%s\');' % x for x in 6155f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) sorted(missing_provides)]) 6165f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) missing_provides_msg += '\n' 6175f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) 6185f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) missing_provides_msg += '\nFirst line where provided: \n' 6195f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) missing_provides_msg += '\n'.join( 6205f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) [' %s : line %d' % (x, missing_provides[x]) for x in 6215f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) sorted(missing_provides)]) 6225f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) missing_provides_msg += '\n' 6235f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) 6245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) self._HandleError( 6255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) errors.MISSING_GOOG_PROVIDE, 6265f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) missing_provides_msg, 6275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) token, position=Position.AtBeginning(), 6285f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) fix_data=(missing_provides.keys(), need_blank_line)) 6295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 6305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) def _ReportMissingRequires(self, missing_requires, token, need_blank_line): 6315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) """Reports missing require statements to the error handler. 6325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 6335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) Args: 6345f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) missing_requires: A dictionary of string(key) and integer(value) where 6355f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) each string(key) is a namespace that should be required, but is not 6365f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) and integer(value) is first line number where it's required. 6375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) token: The token where the error was detected (also where the new requires 6385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) will be inserted. 6395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) need_blank_line: Whether a blank line needs to be inserted before the new 6405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) requires are inserted. May be True, False, or None, where None 6415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) indicates that the insert location is unknown. 6425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) """ 6435f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) 6445f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) missing_requires_msg = 'Missing the following goog.require statements:\n' 6455f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) missing_requires_msg += '\n'.join(['goog.require(\'%s\');' % x for x in 6465f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) sorted(missing_requires)]) 6475f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) missing_requires_msg += '\n' 6485f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) 6495f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) missing_requires_msg += '\nFirst line where required: \n' 6505f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) missing_requires_msg += '\n'.join( 6515f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) [' %s : line %d' % (x, missing_requires[x]) for x in 6525f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) sorted(missing_requires)]) 6535f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) missing_requires_msg += '\n' 6545f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) 6555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) self._HandleError( 6565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) errors.MISSING_GOOG_REQUIRE, 6575f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) missing_requires_msg, 6585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) token, position=Position.AtBeginning(), 6595f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) fix_data=(missing_requires.keys(), need_blank_line)) 6605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 6615f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) def Finalize(self, state): 6625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) """Perform all checks that need to occur after all lines are processed.""" 6635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) # Call the base class's Finalize function. 6645f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) super(JavaScriptLintRules, self).Finalize(state) 6655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 6665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if error_check.ShouldCheck(Rule.UNUSED_PRIVATE_MEMBERS): 6675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) # Report an error for any declared private member that was never used. 6685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) unused_private_members = (self._declared_private_members - 6695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) self._used_private_members) 6705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 6715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) for variable in unused_private_members: 6725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) token = self._declared_private_member_tokens[variable] 6735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) self._HandleError(errors.UNUSED_PRIVATE_MEMBER, 6745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 'Unused private member: %s.' % token.string, 6755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) token) 6765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 6775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) # Clear state to prepare for the next file. 6785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) self._declared_private_member_tokens = {} 6795f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) self._declared_private_members = set() 6805f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) self._used_private_members = set() 6815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 6825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) namespaces_info = self._namespaces_info 6835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if namespaces_info is not None: 6845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) # If there are no provide or require statements, missing provides and 6855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) # requires should be reported on line 1. 6865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (not namespaces_info.GetProvidedNamespaces() and 6875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) not namespaces_info.GetRequiredNamespaces()): 6885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) missing_provides = namespaces_info.GetMissingProvides() 6895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if missing_provides: 6905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) self._ReportMissingProvides( 6915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) missing_provides, state.GetFirstToken(), None) 6925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 6935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) missing_requires = namespaces_info.GetMissingRequires() 6945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if missing_requires: 6955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) self._ReportMissingRequires( 6965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) missing_requires, state.GetFirstToken(), None) 6975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 6985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) self._CheckSortedRequiresProvides(state.GetFirstToken()) 6995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 7005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) def _CheckSortedRequiresProvides(self, token): 7015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) """Checks that all goog.require and goog.provide statements are sorted. 7025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 7035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) Note that this method needs to be run after missing statements are added to 7045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) preserve alphabetical order. 7055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 7065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) Args: 7075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) token: The first token in the token stream. 7085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) """ 7095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) sorter = requireprovidesorter.RequireProvideSorter() 7105f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) first_provide_token = sorter.CheckProvides(token) 7115f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) if first_provide_token: 7125f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) new_order = sorter.GetFixedProvideString(first_provide_token) 7135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) self._HandleError( 7145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) errors.GOOG_PROVIDES_NOT_ALPHABETIZED, 7155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 'goog.provide classes must be alphabetized. The correct code is:\n' + 7165f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) new_order, 7175f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) first_provide_token, 7185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) position=Position.AtBeginning(), 7195f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) fix_data=first_provide_token) 7205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 7215f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) first_require_token = sorter.CheckRequires(token) 7225f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) if first_require_token: 7235f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) new_order = sorter.GetFixedRequireString(first_require_token) 7245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) self._HandleError( 7255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) errors.GOOG_REQUIRES_NOT_ALPHABETIZED, 7265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 'goog.require classes must be alphabetized. The correct code is:\n' + 7275f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) new_order, 7285f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) first_require_token, 7295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) position=Position.AtBeginning(), 7305f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) fix_data=first_require_token) 7315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 7325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) def GetLongLineExceptions(self): 7335f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) """Gets a list of regexps for lines which can be longer than the limit. 7345f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) 7355f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) Returns: 7365f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) A list of regexps, used as matches (rather than searches). 7375f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) """ 7385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return [ 7395f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) re.compile(r'goog\.require\(.+\);?\s*$'), 7405f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) re.compile(r'goog\.provide\(.+\);?\s*$'), 7415f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) re.compile(r'goog\.setTestOnly\(.+\);?\s*$'), 7425f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) re.compile(r'[\s/*]*@visibility\s*{.*}[\s*/]*$'), 7435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) ] 744