12da489cd246702bee5938545b18a6f710ed214bcJamie Gennis#!/usr/bin/env python 22da489cd246702bee5938545b18a6f710ed214bcJamie Gennis# 32da489cd246702bee5938545b18a6f710ed214bcJamie Gennis# Copyright 2011 The Closure Linter Authors. All Rights Reserved. 42da489cd246702bee5938545b18a6f710ed214bcJamie Gennis# 52da489cd246702bee5938545b18a6f710ed214bcJamie Gennis# Licensed under the Apache License, Version 2.0 (the "License"); 62da489cd246702bee5938545b18a6f710ed214bcJamie Gennis# you may not use this file except in compliance with the License. 72da489cd246702bee5938545b18a6f710ed214bcJamie Gennis# You may obtain a copy of the License at 82da489cd246702bee5938545b18a6f710ed214bcJamie Gennis# 92da489cd246702bee5938545b18a6f710ed214bcJamie Gennis# http://www.apache.org/licenses/LICENSE-2.0 102da489cd246702bee5938545b18a6f710ed214bcJamie Gennis# 112da489cd246702bee5938545b18a6f710ed214bcJamie Gennis# Unless required by applicable law or agreed to in writing, software 122da489cd246702bee5938545b18a6f710ed214bcJamie Gennis# distributed under the License is distributed on an "AS-IS" BASIS, 132da489cd246702bee5938545b18a6f710ed214bcJamie Gennis# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 142da489cd246702bee5938545b18a6f710ed214bcJamie Gennis# See the License for the specific language governing permissions and 152da489cd246702bee5938545b18a6f710ed214bcJamie Gennis# limitations under the License. 162da489cd246702bee5938545b18a6f710ed214bcJamie Gennis 172da489cd246702bee5938545b18a6f710ed214bcJamie Gennis"""Methods for checking JS files for common style guide violations. 182da489cd246702bee5938545b18a6f710ed214bcJamie Gennis 192da489cd246702bee5938545b18a6f710ed214bcJamie GennisThese style guide violations should only apply to JavaScript and not an Ecma 202da489cd246702bee5938545b18a6f710ed214bcJamie Gennisscripting languages. 212da489cd246702bee5938545b18a6f710ed214bcJamie Gennis""" 222da489cd246702bee5938545b18a6f710ed214bcJamie Gennis 232da489cd246702bee5938545b18a6f710ed214bcJamie Gennis__author__ = ('robbyw@google.com (Robert Walker)', 242da489cd246702bee5938545b18a6f710ed214bcJamie Gennis 'ajp@google.com (Andy Perelson)', 252da489cd246702bee5938545b18a6f710ed214bcJamie Gennis 'jacobr@google.com (Jacob Richman)') 262da489cd246702bee5938545b18a6f710ed214bcJamie Gennis 272da489cd246702bee5938545b18a6f710ed214bcJamie Gennisimport re 282da489cd246702bee5938545b18a6f710ed214bcJamie Gennisfrom sets import Set 292da489cd246702bee5938545b18a6f710ed214bcJamie Gennisfrom closure_linter import ecmalintrules 302da489cd246702bee5938545b18a6f710ed214bcJamie Gennisfrom closure_linter import error_check 312da489cd246702bee5938545b18a6f710ed214bcJamie Gennisfrom closure_linter import errors 322da489cd246702bee5938545b18a6f710ed214bcJamie Gennisfrom closure_linter import javascripttokenizer 332da489cd246702bee5938545b18a6f710ed214bcJamie Gennisfrom closure_linter import javascripttokens 342da489cd246702bee5938545b18a6f710ed214bcJamie Gennisfrom closure_linter import requireprovidesorter 352da489cd246702bee5938545b18a6f710ed214bcJamie Gennisfrom closure_linter import tokenutil 362da489cd246702bee5938545b18a6f710ed214bcJamie Gennisfrom closure_linter.common import error 372da489cd246702bee5938545b18a6f710ed214bcJamie Gennisfrom closure_linter.common import position 382da489cd246702bee5938545b18a6f710ed214bcJamie Gennis 392da489cd246702bee5938545b18a6f710ed214bcJamie Gennis# Shorthand 402da489cd246702bee5938545b18a6f710ed214bcJamie GennisError = error.Error 412da489cd246702bee5938545b18a6f710ed214bcJamie GennisPosition = position.Position 422da489cd246702bee5938545b18a6f710ed214bcJamie GennisRule = error_check.Rule 432da489cd246702bee5938545b18a6f710ed214bcJamie GennisType = javascripttokens.JavaScriptTokenType 442da489cd246702bee5938545b18a6f710ed214bcJamie Gennis 452da489cd246702bee5938545b18a6f710ed214bcJamie Gennis 462da489cd246702bee5938545b18a6f710ed214bcJamie Gennisclass JavaScriptLintRules(ecmalintrules.EcmaScriptLintRules): 472da489cd246702bee5938545b18a6f710ed214bcJamie Gennis """JavaScript lint rules that catch JavaScript specific style errors.""" 482da489cd246702bee5938545b18a6f710ed214bcJamie Gennis 492da489cd246702bee5938545b18a6f710ed214bcJamie Gennis def __init__(self, namespaces_info): 502da489cd246702bee5938545b18a6f710ed214bcJamie Gennis """Initializes a JavaScriptLintRules instance.""" 512da489cd246702bee5938545b18a6f710ed214bcJamie Gennis ecmalintrules.EcmaScriptLintRules.__init__(self) 522da489cd246702bee5938545b18a6f710ed214bcJamie Gennis self._namespaces_info = namespaces_info 532da489cd246702bee5938545b18a6f710ed214bcJamie Gennis self._declared_private_member_tokens = {} 542da489cd246702bee5938545b18a6f710ed214bcJamie Gennis self._declared_private_members = Set() 552da489cd246702bee5938545b18a6f710ed214bcJamie Gennis self._used_private_members = Set() 562da489cd246702bee5938545b18a6f710ed214bcJamie Gennis 572da489cd246702bee5938545b18a6f710ed214bcJamie Gennis def HandleMissingParameterDoc(self, token, param_name): 582da489cd246702bee5938545b18a6f710ed214bcJamie Gennis """Handle errors associated with a parameter missing a param tag.""" 592da489cd246702bee5938545b18a6f710ed214bcJamie Gennis self._HandleError(errors.MISSING_PARAMETER_DOCUMENTATION, 602da489cd246702bee5938545b18a6f710ed214bcJamie Gennis 'Missing docs for parameter: "%s"' % param_name, token) 612da489cd246702bee5938545b18a6f710ed214bcJamie Gennis 622da489cd246702bee5938545b18a6f710ed214bcJamie Gennis def __ContainsRecordType(self, token): 632da489cd246702bee5938545b18a6f710ed214bcJamie Gennis """Check whether the given token contains a record type. 642da489cd246702bee5938545b18a6f710ed214bcJamie Gennis 652da489cd246702bee5938545b18a6f710ed214bcJamie Gennis Args: 662da489cd246702bee5938545b18a6f710ed214bcJamie Gennis token: The token being checked 672da489cd246702bee5938545b18a6f710ed214bcJamie Gennis 682da489cd246702bee5938545b18a6f710ed214bcJamie Gennis Returns: 692da489cd246702bee5938545b18a6f710ed214bcJamie Gennis True if the token contains a record type, False otherwise. 702da489cd246702bee5938545b18a6f710ed214bcJamie Gennis """ 712da489cd246702bee5938545b18a6f710ed214bcJamie Gennis # If we see more than one left-brace in the string of an annotation token, 722da489cd246702bee5938545b18a6f710ed214bcJamie Gennis # then there's a record type in there. 732da489cd246702bee5938545b18a6f710ed214bcJamie Gennis return ( 742da489cd246702bee5938545b18a6f710ed214bcJamie Gennis token and token.type == Type.DOC_FLAG and 752da489cd246702bee5938545b18a6f710ed214bcJamie Gennis token.attached_object.type is not None and 762da489cd246702bee5938545b18a6f710ed214bcJamie Gennis token.attached_object.type.find('{') != token.string.rfind('{')) 772da489cd246702bee5938545b18a6f710ed214bcJamie Gennis 782da489cd246702bee5938545b18a6f710ed214bcJamie Gennis def CheckToken(self, token, state): 792da489cd246702bee5938545b18a6f710ed214bcJamie Gennis """Checks a token, given the current parser_state, for warnings and errors. 802da489cd246702bee5938545b18a6f710ed214bcJamie Gennis 812da489cd246702bee5938545b18a6f710ed214bcJamie Gennis Args: 822da489cd246702bee5938545b18a6f710ed214bcJamie Gennis token: The current token under consideration 832da489cd246702bee5938545b18a6f710ed214bcJamie Gennis state: parser_state object that indicates the current state in the page 842da489cd246702bee5938545b18a6f710ed214bcJamie Gennis """ 852da489cd246702bee5938545b18a6f710ed214bcJamie Gennis if self.__ContainsRecordType(token): 862da489cd246702bee5938545b18a6f710ed214bcJamie Gennis # We should bail out and not emit any warnings for this annotation. 872da489cd246702bee5938545b18a6f710ed214bcJamie Gennis # TODO(nicksantos): Support record types for real. 882da489cd246702bee5938545b18a6f710ed214bcJamie Gennis state.GetDocComment().Invalidate() 892da489cd246702bee5938545b18a6f710ed214bcJamie Gennis return 902da489cd246702bee5938545b18a6f710ed214bcJamie Gennis 912da489cd246702bee5938545b18a6f710ed214bcJamie Gennis # Call the base class's CheckToken function. 922da489cd246702bee5938545b18a6f710ed214bcJamie Gennis super(JavaScriptLintRules, self).CheckToken(token, state) 932da489cd246702bee5938545b18a6f710ed214bcJamie Gennis 942da489cd246702bee5938545b18a6f710ed214bcJamie Gennis # Store some convenience variables 952da489cd246702bee5938545b18a6f710ed214bcJamie Gennis namespaces_info = self._namespaces_info 962da489cd246702bee5938545b18a6f710ed214bcJamie Gennis 972da489cd246702bee5938545b18a6f710ed214bcJamie Gennis if error_check.ShouldCheck(Rule.UNUSED_PRIVATE_MEMBERS): 982da489cd246702bee5938545b18a6f710ed214bcJamie Gennis # Find all assignments to private members. 992da489cd246702bee5938545b18a6f710ed214bcJamie Gennis if token.type == Type.SIMPLE_LVALUE: 1002da489cd246702bee5938545b18a6f710ed214bcJamie Gennis identifier = token.string 1012da489cd246702bee5938545b18a6f710ed214bcJamie Gennis if identifier.endswith('_') and not identifier.endswith('__'): 1022da489cd246702bee5938545b18a6f710ed214bcJamie Gennis doc_comment = state.GetDocComment() 1032da489cd246702bee5938545b18a6f710ed214bcJamie Gennis suppressed = (doc_comment and doc_comment.HasFlag('suppress') and 1042da489cd246702bee5938545b18a6f710ed214bcJamie Gennis doc_comment.GetFlag('suppress').type == 'underscore') 1052da489cd246702bee5938545b18a6f710ed214bcJamie Gennis if not suppressed: 1062da489cd246702bee5938545b18a6f710ed214bcJamie Gennis # Look for static members defined on a provided namespace. 1072da489cd246702bee5938545b18a6f710ed214bcJamie Gennis namespace = namespaces_info.GetClosurizedNamespace(identifier) 1082da489cd246702bee5938545b18a6f710ed214bcJamie Gennis provided_namespaces = namespaces_info.GetProvidedNamespaces() 1092da489cd246702bee5938545b18a6f710ed214bcJamie Gennis 1102da489cd246702bee5938545b18a6f710ed214bcJamie Gennis # Skip cases of this.something_.somethingElse_. 1112da489cd246702bee5938545b18a6f710ed214bcJamie Gennis regex = re.compile('^this\.[a-zA-Z_]+$') 1122da489cd246702bee5938545b18a6f710ed214bcJamie Gennis if namespace in provided_namespaces or regex.match(identifier): 1132da489cd246702bee5938545b18a6f710ed214bcJamie Gennis variable = identifier.split('.')[-1] 1142da489cd246702bee5938545b18a6f710ed214bcJamie Gennis self._declared_private_member_tokens[variable] = token 1152da489cd246702bee5938545b18a6f710ed214bcJamie Gennis self._declared_private_members.add(variable) 1162da489cd246702bee5938545b18a6f710ed214bcJamie Gennis elif not identifier.endswith('__'): 1172da489cd246702bee5938545b18a6f710ed214bcJamie Gennis # Consider setting public members of private members to be a usage. 1182da489cd246702bee5938545b18a6f710ed214bcJamie Gennis for piece in identifier.split('.'): 1192da489cd246702bee5938545b18a6f710ed214bcJamie Gennis if piece.endswith('_'): 1202da489cd246702bee5938545b18a6f710ed214bcJamie Gennis self._used_private_members.add(piece) 1212da489cd246702bee5938545b18a6f710ed214bcJamie Gennis 1222da489cd246702bee5938545b18a6f710ed214bcJamie Gennis # Find all usages of private members. 1232da489cd246702bee5938545b18a6f710ed214bcJamie Gennis if token.type == Type.IDENTIFIER: 1242da489cd246702bee5938545b18a6f710ed214bcJamie Gennis for piece in token.string.split('.'): 1252da489cd246702bee5938545b18a6f710ed214bcJamie Gennis if piece.endswith('_'): 1262da489cd246702bee5938545b18a6f710ed214bcJamie Gennis self._used_private_members.add(piece) 1272da489cd246702bee5938545b18a6f710ed214bcJamie Gennis 1282da489cd246702bee5938545b18a6f710ed214bcJamie Gennis if token.type == Type.DOC_FLAG: 1292da489cd246702bee5938545b18a6f710ed214bcJamie Gennis flag = token.attached_object 1302da489cd246702bee5938545b18a6f710ed214bcJamie Gennis 1312da489cd246702bee5938545b18a6f710ed214bcJamie Gennis if flag.flag_type == 'param' and flag.name_token is not None: 1322da489cd246702bee5938545b18a6f710ed214bcJamie Gennis self._CheckForMissingSpaceBeforeToken( 1332da489cd246702bee5938545b18a6f710ed214bcJamie Gennis token.attached_object.name_token) 1342da489cd246702bee5938545b18a6f710ed214bcJamie Gennis 1352da489cd246702bee5938545b18a6f710ed214bcJamie Gennis if (error_check.ShouldCheck(Rule.OPTIONAL_TYPE_MARKER) and 1362da489cd246702bee5938545b18a6f710ed214bcJamie Gennis flag.type is not None and flag.name is not None): 1372da489cd246702bee5938545b18a6f710ed214bcJamie Gennis # Check for optional marker in type. 1382da489cd246702bee5938545b18a6f710ed214bcJamie Gennis if (flag.type.endswith('=') and 1392da489cd246702bee5938545b18a6f710ed214bcJamie Gennis not flag.name.startswith('opt_')): 1402da489cd246702bee5938545b18a6f710ed214bcJamie Gennis self._HandleError(errors.JSDOC_MISSING_OPTIONAL_PREFIX, 1412da489cd246702bee5938545b18a6f710ed214bcJamie Gennis 'Optional parameter name %s must be prefixed ' 1422da489cd246702bee5938545b18a6f710ed214bcJamie Gennis 'with opt_.' % flag.name, 1432da489cd246702bee5938545b18a6f710ed214bcJamie Gennis token) 1442da489cd246702bee5938545b18a6f710ed214bcJamie Gennis elif (not flag.type.endswith('=') and 1452da489cd246702bee5938545b18a6f710ed214bcJamie Gennis flag.name.startswith('opt_')): 1462da489cd246702bee5938545b18a6f710ed214bcJamie Gennis self._HandleError(errors.JSDOC_MISSING_OPTIONAL_TYPE, 1472da489cd246702bee5938545b18a6f710ed214bcJamie Gennis 'Optional parameter %s type must end with =.' % 1482da489cd246702bee5938545b18a6f710ed214bcJamie Gennis flag.name, 1492da489cd246702bee5938545b18a6f710ed214bcJamie Gennis token) 1502da489cd246702bee5938545b18a6f710ed214bcJamie Gennis 1512da489cd246702bee5938545b18a6f710ed214bcJamie Gennis if flag.flag_type in state.GetDocFlag().HAS_TYPE: 1522da489cd246702bee5938545b18a6f710ed214bcJamie Gennis # Check for both missing type token and empty type braces '{}' 1532da489cd246702bee5938545b18a6f710ed214bcJamie Gennis # Missing suppress types are reported separately and we allow enums 1542da489cd246702bee5938545b18a6f710ed214bcJamie Gennis # without types. 1552da489cd246702bee5938545b18a6f710ed214bcJamie Gennis if (flag.flag_type not in ('suppress', 'enum') and 1562da489cd246702bee5938545b18a6f710ed214bcJamie Gennis (not flag.type or flag.type.isspace())): 1572da489cd246702bee5938545b18a6f710ed214bcJamie Gennis self._HandleError(errors.MISSING_JSDOC_TAG_TYPE, 1582da489cd246702bee5938545b18a6f710ed214bcJamie Gennis 'Missing type in %s tag' % token.string, token) 1592da489cd246702bee5938545b18a6f710ed214bcJamie Gennis 1602da489cd246702bee5938545b18a6f710ed214bcJamie Gennis elif flag.name_token and flag.type_end_token and tokenutil.Compare( 1612da489cd246702bee5938545b18a6f710ed214bcJamie Gennis flag.type_end_token, flag.name_token) > 0: 1622da489cd246702bee5938545b18a6f710ed214bcJamie Gennis self._HandleError( 1632da489cd246702bee5938545b18a6f710ed214bcJamie Gennis errors.OUT_OF_ORDER_JSDOC_TAG_TYPE, 1642da489cd246702bee5938545b18a6f710ed214bcJamie Gennis 'Type should be immediately after %s tag' % token.string, 1652da489cd246702bee5938545b18a6f710ed214bcJamie Gennis token) 1662da489cd246702bee5938545b18a6f710ed214bcJamie Gennis 1672da489cd246702bee5938545b18a6f710ed214bcJamie Gennis elif token.type == Type.DOUBLE_QUOTE_STRING_START: 1682da489cd246702bee5938545b18a6f710ed214bcJamie Gennis next_token = token.next 1692da489cd246702bee5938545b18a6f710ed214bcJamie Gennis while next_token.type == Type.STRING_TEXT: 1702da489cd246702bee5938545b18a6f710ed214bcJamie Gennis if javascripttokenizer.JavaScriptTokenizer.SINGLE_QUOTE.search( 1712da489cd246702bee5938545b18a6f710ed214bcJamie Gennis next_token.string): 1722da489cd246702bee5938545b18a6f710ed214bcJamie Gennis break 1732da489cd246702bee5938545b18a6f710ed214bcJamie Gennis next_token = next_token.next 1742da489cd246702bee5938545b18a6f710ed214bcJamie Gennis else: 1752da489cd246702bee5938545b18a6f710ed214bcJamie Gennis self._HandleError( 1762da489cd246702bee5938545b18a6f710ed214bcJamie Gennis errors.UNNECESSARY_DOUBLE_QUOTED_STRING, 1772da489cd246702bee5938545b18a6f710ed214bcJamie Gennis 'Single-quoted string preferred over double-quoted string.', 1782da489cd246702bee5938545b18a6f710ed214bcJamie Gennis token, 1792da489cd246702bee5938545b18a6f710ed214bcJamie Gennis Position.All(token.string)) 1802da489cd246702bee5938545b18a6f710ed214bcJamie Gennis 1812da489cd246702bee5938545b18a6f710ed214bcJamie Gennis elif token.type == Type.END_DOC_COMMENT: 1822da489cd246702bee5938545b18a6f710ed214bcJamie Gennis doc_comment = state.GetDocComment() 1832da489cd246702bee5938545b18a6f710ed214bcJamie Gennis 1842da489cd246702bee5938545b18a6f710ed214bcJamie Gennis # When @externs appears in a @fileoverview comment, it should trigger 1852da489cd246702bee5938545b18a6f710ed214bcJamie Gennis # the same limited doc checks as a special filename like externs.js. 1862da489cd246702bee5938545b18a6f710ed214bcJamie Gennis if doc_comment.HasFlag('fileoverview') and doc_comment.HasFlag('externs'): 1872da489cd246702bee5938545b18a6f710ed214bcJamie Gennis self._SetLimitedDocChecks(True) 1882da489cd246702bee5938545b18a6f710ed214bcJamie Gennis 1892da489cd246702bee5938545b18a6f710ed214bcJamie Gennis if (error_check.ShouldCheck(Rule.BLANK_LINES_AT_TOP_LEVEL) and 1902da489cd246702bee5938545b18a6f710ed214bcJamie Gennis not self._is_html and state.InTopLevel() and not state.InBlock()): 1912da489cd246702bee5938545b18a6f710ed214bcJamie Gennis 1922da489cd246702bee5938545b18a6f710ed214bcJamie Gennis # Check if we're in a fileoverview or constructor JsDoc. 1932da489cd246702bee5938545b18a6f710ed214bcJamie Gennis is_constructor = ( 1942da489cd246702bee5938545b18a6f710ed214bcJamie Gennis doc_comment.HasFlag('constructor') or 1952da489cd246702bee5938545b18a6f710ed214bcJamie Gennis doc_comment.HasFlag('interface')) 1962da489cd246702bee5938545b18a6f710ed214bcJamie Gennis is_file_overview = doc_comment.HasFlag('fileoverview') 1972da489cd246702bee5938545b18a6f710ed214bcJamie Gennis 1982da489cd246702bee5938545b18a6f710ed214bcJamie Gennis # If the comment is not a file overview, and it does not immediately 1992da489cd246702bee5938545b18a6f710ed214bcJamie Gennis # precede some code, skip it. 2002da489cd246702bee5938545b18a6f710ed214bcJamie Gennis # NOTE: The tokenutil methods are not used here because of their 2012da489cd246702bee5938545b18a6f710ed214bcJamie Gennis # behavior at the top of a file. 2022da489cd246702bee5938545b18a6f710ed214bcJamie Gennis next_token = token.next 2032da489cd246702bee5938545b18a6f710ed214bcJamie Gennis if (not next_token or 2042da489cd246702bee5938545b18a6f710ed214bcJamie Gennis (not is_file_overview and next_token.type in Type.NON_CODE_TYPES)): 2052da489cd246702bee5938545b18a6f710ed214bcJamie Gennis return 2062da489cd246702bee5938545b18a6f710ed214bcJamie Gennis 2072da489cd246702bee5938545b18a6f710ed214bcJamie Gennis # Don't require extra blank lines around suppression of extra 2082da489cd246702bee5938545b18a6f710ed214bcJamie Gennis # goog.require errors. 2092da489cd246702bee5938545b18a6f710ed214bcJamie Gennis if (doc_comment.SuppressionOnly() and 2102da489cd246702bee5938545b18a6f710ed214bcJamie Gennis next_token.type == Type.IDENTIFIER and 2112da489cd246702bee5938545b18a6f710ed214bcJamie Gennis next_token.string in ['goog.provide', 'goog.require']): 2122da489cd246702bee5938545b18a6f710ed214bcJamie Gennis return 2132da489cd246702bee5938545b18a6f710ed214bcJamie Gennis 2142da489cd246702bee5938545b18a6f710ed214bcJamie Gennis # Find the start of this block (include comments above the block, unless 2152da489cd246702bee5938545b18a6f710ed214bcJamie Gennis # this is a file overview). 2162da489cd246702bee5938545b18a6f710ed214bcJamie Gennis block_start = doc_comment.start_token 2172da489cd246702bee5938545b18a6f710ed214bcJamie Gennis if not is_file_overview: 2182da489cd246702bee5938545b18a6f710ed214bcJamie Gennis token = block_start.previous 2192da489cd246702bee5938545b18a6f710ed214bcJamie Gennis while token and token.type in Type.COMMENT_TYPES: 2202da489cd246702bee5938545b18a6f710ed214bcJamie Gennis block_start = token 2212da489cd246702bee5938545b18a6f710ed214bcJamie Gennis token = token.previous 2222da489cd246702bee5938545b18a6f710ed214bcJamie Gennis 2232da489cd246702bee5938545b18a6f710ed214bcJamie Gennis # Count the number of blank lines before this block. 2242da489cd246702bee5938545b18a6f710ed214bcJamie Gennis blank_lines = 0 2252da489cd246702bee5938545b18a6f710ed214bcJamie Gennis token = block_start.previous 2262da489cd246702bee5938545b18a6f710ed214bcJamie Gennis while token and token.type in [Type.WHITESPACE, Type.BLANK_LINE]: 2272da489cd246702bee5938545b18a6f710ed214bcJamie Gennis if token.type == Type.BLANK_LINE: 2282da489cd246702bee5938545b18a6f710ed214bcJamie Gennis # A blank line. 2292da489cd246702bee5938545b18a6f710ed214bcJamie Gennis blank_lines += 1 2302da489cd246702bee5938545b18a6f710ed214bcJamie Gennis elif token.type == Type.WHITESPACE and not token.line.strip(): 2312da489cd246702bee5938545b18a6f710ed214bcJamie Gennis # A line with only whitespace on it. 2322da489cd246702bee5938545b18a6f710ed214bcJamie Gennis blank_lines += 1 2332da489cd246702bee5938545b18a6f710ed214bcJamie Gennis token = token.previous 2342da489cd246702bee5938545b18a6f710ed214bcJamie Gennis 2352da489cd246702bee5938545b18a6f710ed214bcJamie Gennis # Log errors. 2362da489cd246702bee5938545b18a6f710ed214bcJamie Gennis error_message = False 2372da489cd246702bee5938545b18a6f710ed214bcJamie Gennis expected_blank_lines = 0 2382da489cd246702bee5938545b18a6f710ed214bcJamie Gennis 2392da489cd246702bee5938545b18a6f710ed214bcJamie Gennis if is_file_overview and blank_lines == 0: 2402da489cd246702bee5938545b18a6f710ed214bcJamie Gennis error_message = 'Should have a blank line before a file overview.' 2412da489cd246702bee5938545b18a6f710ed214bcJamie Gennis expected_blank_lines = 1 2422da489cd246702bee5938545b18a6f710ed214bcJamie Gennis elif is_constructor and blank_lines != 3: 2432da489cd246702bee5938545b18a6f710ed214bcJamie Gennis error_message = ( 2442da489cd246702bee5938545b18a6f710ed214bcJamie Gennis 'Should have 3 blank lines before a constructor/interface.') 2452da489cd246702bee5938545b18a6f710ed214bcJamie Gennis expected_blank_lines = 3 2462da489cd246702bee5938545b18a6f710ed214bcJamie Gennis elif not is_file_overview and not is_constructor and blank_lines != 2: 2472da489cd246702bee5938545b18a6f710ed214bcJamie Gennis error_message = 'Should have 2 blank lines between top-level blocks.' 2482da489cd246702bee5938545b18a6f710ed214bcJamie Gennis expected_blank_lines = 2 2492da489cd246702bee5938545b18a6f710ed214bcJamie Gennis 2502da489cd246702bee5938545b18a6f710ed214bcJamie Gennis if error_message: 2512da489cd246702bee5938545b18a6f710ed214bcJamie Gennis self._HandleError( 2522da489cd246702bee5938545b18a6f710ed214bcJamie Gennis errors.WRONG_BLANK_LINE_COUNT, error_message, 2532da489cd246702bee5938545b18a6f710ed214bcJamie Gennis block_start, Position.AtBeginning(), 2542da489cd246702bee5938545b18a6f710ed214bcJamie Gennis expected_blank_lines - blank_lines) 2552da489cd246702bee5938545b18a6f710ed214bcJamie Gennis 2562da489cd246702bee5938545b18a6f710ed214bcJamie Gennis elif token.type == Type.END_BLOCK: 2572da489cd246702bee5938545b18a6f710ed214bcJamie Gennis if state.InFunction() and state.IsFunctionClose(): 2582da489cd246702bee5938545b18a6f710ed214bcJamie Gennis is_immediately_called = (token.next and 2592da489cd246702bee5938545b18a6f710ed214bcJamie Gennis token.next.type == Type.START_PAREN) 2602da489cd246702bee5938545b18a6f710ed214bcJamie Gennis 2612da489cd246702bee5938545b18a6f710ed214bcJamie Gennis function = state.GetFunction() 2622da489cd246702bee5938545b18a6f710ed214bcJamie Gennis if not self._limited_doc_checks: 2632da489cd246702bee5938545b18a6f710ed214bcJamie Gennis if (function.has_return and function.doc and 2642da489cd246702bee5938545b18a6f710ed214bcJamie Gennis not is_immediately_called and 2652da489cd246702bee5938545b18a6f710ed214bcJamie Gennis not function.doc.HasFlag('return') and 2662da489cd246702bee5938545b18a6f710ed214bcJamie Gennis not function.doc.InheritsDocumentation() and 2672da489cd246702bee5938545b18a6f710ed214bcJamie Gennis not function.doc.HasFlag('constructor')): 2682da489cd246702bee5938545b18a6f710ed214bcJamie Gennis # Check for proper documentation of return value. 2692da489cd246702bee5938545b18a6f710ed214bcJamie Gennis self._HandleError( 2702da489cd246702bee5938545b18a6f710ed214bcJamie Gennis errors.MISSING_RETURN_DOCUMENTATION, 2712da489cd246702bee5938545b18a6f710ed214bcJamie Gennis 'Missing @return JsDoc in function with non-trivial return', 2722da489cd246702bee5938545b18a6f710ed214bcJamie Gennis function.doc.end_token, Position.AtBeginning()) 2732da489cd246702bee5938545b18a6f710ed214bcJamie Gennis elif (not function.has_return and 2742da489cd246702bee5938545b18a6f710ed214bcJamie Gennis not function.has_throw and 2752da489cd246702bee5938545b18a6f710ed214bcJamie Gennis function.doc and 2762da489cd246702bee5938545b18a6f710ed214bcJamie Gennis function.doc.HasFlag('return') and 2772da489cd246702bee5938545b18a6f710ed214bcJamie Gennis not state.InInterfaceMethod()): 2782da489cd246702bee5938545b18a6f710ed214bcJamie Gennis return_flag = function.doc.GetFlag('return') 2792da489cd246702bee5938545b18a6f710ed214bcJamie Gennis if (return_flag.type is None or ( 2802da489cd246702bee5938545b18a6f710ed214bcJamie Gennis 'undefined' not in return_flag.type and 2812da489cd246702bee5938545b18a6f710ed214bcJamie Gennis 'void' not in return_flag.type and 2822da489cd246702bee5938545b18a6f710ed214bcJamie Gennis '*' not in return_flag.type)): 2832da489cd246702bee5938545b18a6f710ed214bcJamie Gennis self._HandleError( 2842da489cd246702bee5938545b18a6f710ed214bcJamie Gennis errors.UNNECESSARY_RETURN_DOCUMENTATION, 2852da489cd246702bee5938545b18a6f710ed214bcJamie Gennis 'Found @return JsDoc on function that returns nothing', 2862da489cd246702bee5938545b18a6f710ed214bcJamie Gennis return_flag.flag_token, Position.AtBeginning()) 2872da489cd246702bee5938545b18a6f710ed214bcJamie Gennis 2882da489cd246702bee5938545b18a6f710ed214bcJamie Gennis if state.InFunction() and state.IsFunctionClose(): 2892da489cd246702bee5938545b18a6f710ed214bcJamie Gennis is_immediately_called = (token.next and 2902da489cd246702bee5938545b18a6f710ed214bcJamie Gennis token.next.type == Type.START_PAREN) 2912da489cd246702bee5938545b18a6f710ed214bcJamie Gennis if (function.has_this and function.doc and 2922da489cd246702bee5938545b18a6f710ed214bcJamie Gennis not function.doc.HasFlag('this') and 2932da489cd246702bee5938545b18a6f710ed214bcJamie Gennis not function.is_constructor and 2942da489cd246702bee5938545b18a6f710ed214bcJamie Gennis not function.is_interface and 2952da489cd246702bee5938545b18a6f710ed214bcJamie Gennis '.prototype.' not in function.name): 2962da489cd246702bee5938545b18a6f710ed214bcJamie Gennis self._HandleError( 2972da489cd246702bee5938545b18a6f710ed214bcJamie Gennis errors.MISSING_JSDOC_TAG_THIS, 2982da489cd246702bee5938545b18a6f710ed214bcJamie Gennis 'Missing @this JsDoc in function referencing "this". (' 2992da489cd246702bee5938545b18a6f710ed214bcJamie Gennis 'this usually means you are trying to reference "this" in ' 3002da489cd246702bee5938545b18a6f710ed214bcJamie Gennis 'a static function, or you have forgotten to mark a ' 3012da489cd246702bee5938545b18a6f710ed214bcJamie Gennis 'constructor with @constructor)', 3022da489cd246702bee5938545b18a6f710ed214bcJamie Gennis function.doc.end_token, Position.AtBeginning()) 3032da489cd246702bee5938545b18a6f710ed214bcJamie Gennis 3042da489cd246702bee5938545b18a6f710ed214bcJamie Gennis elif token.type == Type.IDENTIFIER: 3052da489cd246702bee5938545b18a6f710ed214bcJamie Gennis if token.string == 'goog.inherits' and not state.InFunction(): 3062da489cd246702bee5938545b18a6f710ed214bcJamie Gennis if state.GetLastNonSpaceToken().line_number == token.line_number: 3072da489cd246702bee5938545b18a6f710ed214bcJamie Gennis self._HandleError( 3082da489cd246702bee5938545b18a6f710ed214bcJamie Gennis errors.MISSING_LINE, 3092da489cd246702bee5938545b18a6f710ed214bcJamie Gennis 'Missing newline between constructor and goog.inherits', 3102da489cd246702bee5938545b18a6f710ed214bcJamie Gennis token, 3112da489cd246702bee5938545b18a6f710ed214bcJamie Gennis Position.AtBeginning()) 3122da489cd246702bee5938545b18a6f710ed214bcJamie Gennis 3132da489cd246702bee5938545b18a6f710ed214bcJamie Gennis extra_space = state.GetLastNonSpaceToken().next 3142da489cd246702bee5938545b18a6f710ed214bcJamie Gennis while extra_space != token: 3152da489cd246702bee5938545b18a6f710ed214bcJamie Gennis if extra_space.type == Type.BLANK_LINE: 3162da489cd246702bee5938545b18a6f710ed214bcJamie Gennis self._HandleError( 3172da489cd246702bee5938545b18a6f710ed214bcJamie Gennis errors.EXTRA_LINE, 3182da489cd246702bee5938545b18a6f710ed214bcJamie Gennis 'Extra line between constructor and goog.inherits', 3192da489cd246702bee5938545b18a6f710ed214bcJamie Gennis extra_space) 3202da489cd246702bee5938545b18a6f710ed214bcJamie Gennis extra_space = extra_space.next 3212da489cd246702bee5938545b18a6f710ed214bcJamie Gennis 3222da489cd246702bee5938545b18a6f710ed214bcJamie Gennis # TODO(robbyw): Test the last function was a constructor. 3232da489cd246702bee5938545b18a6f710ed214bcJamie Gennis # TODO(robbyw): Test correct @extends and @implements documentation. 3242da489cd246702bee5938545b18a6f710ed214bcJamie Gennis 3252da489cd246702bee5938545b18a6f710ed214bcJamie Gennis elif (token.string == 'goog.provide' and 3262da489cd246702bee5938545b18a6f710ed214bcJamie Gennis not state.InFunction() and 3272da489cd246702bee5938545b18a6f710ed214bcJamie Gennis namespaces_info is not None): 3282da489cd246702bee5938545b18a6f710ed214bcJamie Gennis namespace = tokenutil.Search(token, Type.STRING_TEXT).string 3292da489cd246702bee5938545b18a6f710ed214bcJamie Gennis 3302da489cd246702bee5938545b18a6f710ed214bcJamie Gennis # Report extra goog.provide statement. 3312da489cd246702bee5938545b18a6f710ed214bcJamie Gennis if namespaces_info.IsExtraProvide(token): 3322da489cd246702bee5938545b18a6f710ed214bcJamie Gennis self._HandleError( 3332da489cd246702bee5938545b18a6f710ed214bcJamie Gennis errors.EXTRA_GOOG_PROVIDE, 3342da489cd246702bee5938545b18a6f710ed214bcJamie Gennis 'Unnecessary goog.provide: ' + namespace, 3352da489cd246702bee5938545b18a6f710ed214bcJamie Gennis token, position=Position.AtBeginning()) 3362da489cd246702bee5938545b18a6f710ed214bcJamie Gennis 3372da489cd246702bee5938545b18a6f710ed214bcJamie Gennis if namespaces_info.IsLastProvide(token): 3382da489cd246702bee5938545b18a6f710ed214bcJamie Gennis # Report missing provide statements after the last existing provide. 3392da489cd246702bee5938545b18a6f710ed214bcJamie Gennis missing_provides = namespaces_info.GetMissingProvides() 3402da489cd246702bee5938545b18a6f710ed214bcJamie Gennis if missing_provides: 3412da489cd246702bee5938545b18a6f710ed214bcJamie Gennis self._ReportMissingProvides( 3422da489cd246702bee5938545b18a6f710ed214bcJamie Gennis missing_provides, 3432da489cd246702bee5938545b18a6f710ed214bcJamie Gennis tokenutil.GetLastTokenInSameLine(token).next, 3442da489cd246702bee5938545b18a6f710ed214bcJamie Gennis False) 3452da489cd246702bee5938545b18a6f710ed214bcJamie Gennis 3462da489cd246702bee5938545b18a6f710ed214bcJamie Gennis # If there are no require statements, missing requires should be 3472da489cd246702bee5938545b18a6f710ed214bcJamie Gennis # reported after the last provide. 3482da489cd246702bee5938545b18a6f710ed214bcJamie Gennis if not namespaces_info.GetRequiredNamespaces(): 3492da489cd246702bee5938545b18a6f710ed214bcJamie Gennis missing_requires = namespaces_info.GetMissingRequires() 3502da489cd246702bee5938545b18a6f710ed214bcJamie Gennis if missing_requires: 3512da489cd246702bee5938545b18a6f710ed214bcJamie Gennis self._ReportMissingRequires( 3522da489cd246702bee5938545b18a6f710ed214bcJamie Gennis missing_requires, 3532da489cd246702bee5938545b18a6f710ed214bcJamie Gennis tokenutil.GetLastTokenInSameLine(token).next, 3542da489cd246702bee5938545b18a6f710ed214bcJamie Gennis True) 3552da489cd246702bee5938545b18a6f710ed214bcJamie Gennis 3562da489cd246702bee5938545b18a6f710ed214bcJamie Gennis elif (token.string == 'goog.require' and 3572da489cd246702bee5938545b18a6f710ed214bcJamie Gennis not state.InFunction() and 3582da489cd246702bee5938545b18a6f710ed214bcJamie Gennis namespaces_info is not None): 3592da489cd246702bee5938545b18a6f710ed214bcJamie Gennis namespace = tokenutil.Search(token, Type.STRING_TEXT).string 3602da489cd246702bee5938545b18a6f710ed214bcJamie Gennis 3612da489cd246702bee5938545b18a6f710ed214bcJamie Gennis # If there are no provide statements, missing provides should be 3622da489cd246702bee5938545b18a6f710ed214bcJamie Gennis # reported before the first require. 3632da489cd246702bee5938545b18a6f710ed214bcJamie Gennis if (namespaces_info.IsFirstRequire(token) and 3642da489cd246702bee5938545b18a6f710ed214bcJamie Gennis not namespaces_info.GetProvidedNamespaces()): 3652da489cd246702bee5938545b18a6f710ed214bcJamie Gennis missing_provides = namespaces_info.GetMissingProvides() 3662da489cd246702bee5938545b18a6f710ed214bcJamie Gennis if missing_provides: 3672da489cd246702bee5938545b18a6f710ed214bcJamie Gennis self._ReportMissingProvides( 3682da489cd246702bee5938545b18a6f710ed214bcJamie Gennis missing_provides, 3692da489cd246702bee5938545b18a6f710ed214bcJamie Gennis tokenutil.GetFirstTokenInSameLine(token), 3702da489cd246702bee5938545b18a6f710ed214bcJamie Gennis True) 3712da489cd246702bee5938545b18a6f710ed214bcJamie Gennis 3722da489cd246702bee5938545b18a6f710ed214bcJamie Gennis # Report extra goog.require statement. 3732da489cd246702bee5938545b18a6f710ed214bcJamie Gennis if namespaces_info.IsExtraRequire(token): 3742da489cd246702bee5938545b18a6f710ed214bcJamie Gennis self._HandleError( 3752da489cd246702bee5938545b18a6f710ed214bcJamie Gennis errors.EXTRA_GOOG_REQUIRE, 3762da489cd246702bee5938545b18a6f710ed214bcJamie Gennis 'Unnecessary goog.require: ' + namespace, 3772da489cd246702bee5938545b18a6f710ed214bcJamie Gennis token, position=Position.AtBeginning()) 3782da489cd246702bee5938545b18a6f710ed214bcJamie Gennis 3792da489cd246702bee5938545b18a6f710ed214bcJamie Gennis # Report missing goog.require statements. 3802da489cd246702bee5938545b18a6f710ed214bcJamie Gennis if namespaces_info.IsLastRequire(token): 3812da489cd246702bee5938545b18a6f710ed214bcJamie Gennis missing_requires = namespaces_info.GetMissingRequires() 3822da489cd246702bee5938545b18a6f710ed214bcJamie Gennis if missing_requires: 3832da489cd246702bee5938545b18a6f710ed214bcJamie Gennis self._ReportMissingRequires( 3842da489cd246702bee5938545b18a6f710ed214bcJamie Gennis missing_requires, 3852da489cd246702bee5938545b18a6f710ed214bcJamie Gennis tokenutil.GetLastTokenInSameLine(token).next, 3862da489cd246702bee5938545b18a6f710ed214bcJamie Gennis False) 3872da489cd246702bee5938545b18a6f710ed214bcJamie Gennis 3882da489cd246702bee5938545b18a6f710ed214bcJamie Gennis elif token.type == Type.OPERATOR: 3892da489cd246702bee5938545b18a6f710ed214bcJamie Gennis last_in_line = token.IsLastInLine() 3902da489cd246702bee5938545b18a6f710ed214bcJamie Gennis # If the token is unary and appears to be used in a unary context 3912da489cd246702bee5938545b18a6f710ed214bcJamie Gennis # it's ok. Otherwise, if it's at the end of the line or immediately 3922da489cd246702bee5938545b18a6f710ed214bcJamie Gennis # before a comment, it's ok. 3932da489cd246702bee5938545b18a6f710ed214bcJamie Gennis # Don't report an error before a start bracket - it will be reported 3942da489cd246702bee5938545b18a6f710ed214bcJamie Gennis # by that token's space checks. 3952da489cd246702bee5938545b18a6f710ed214bcJamie Gennis if (not token.metadata.IsUnaryOperator() and not last_in_line 3962da489cd246702bee5938545b18a6f710ed214bcJamie Gennis and not token.next.IsComment() 3972da489cd246702bee5938545b18a6f710ed214bcJamie Gennis and not token.next.IsOperator(',') 3982da489cd246702bee5938545b18a6f710ed214bcJamie Gennis and not token.next.type in (Type.WHITESPACE, Type.END_PAREN, 3992da489cd246702bee5938545b18a6f710ed214bcJamie Gennis Type.END_BRACKET, Type.SEMICOLON, 4002da489cd246702bee5938545b18a6f710ed214bcJamie Gennis Type.START_BRACKET)): 4012da489cd246702bee5938545b18a6f710ed214bcJamie Gennis self._HandleError( 4022da489cd246702bee5938545b18a6f710ed214bcJamie Gennis errors.MISSING_SPACE, 4032da489cd246702bee5938545b18a6f710ed214bcJamie Gennis 'Missing space after "%s"' % token.string, 4042da489cd246702bee5938545b18a6f710ed214bcJamie Gennis token, 4052da489cd246702bee5938545b18a6f710ed214bcJamie Gennis Position.AtEnd(token.string)) 4062da489cd246702bee5938545b18a6f710ed214bcJamie Gennis elif token.type == Type.WHITESPACE: 4072da489cd246702bee5938545b18a6f710ed214bcJamie Gennis first_in_line = token.IsFirstInLine() 4082da489cd246702bee5938545b18a6f710ed214bcJamie Gennis last_in_line = token.IsLastInLine() 4092da489cd246702bee5938545b18a6f710ed214bcJamie Gennis # Check whitespace length if it's not the first token of the line and 4102da489cd246702bee5938545b18a6f710ed214bcJamie Gennis # if it's not immediately before a comment. 4112da489cd246702bee5938545b18a6f710ed214bcJamie Gennis if not last_in_line and not first_in_line and not token.next.IsComment(): 4122da489cd246702bee5938545b18a6f710ed214bcJamie Gennis # Ensure there is no space after opening parentheses. 4132da489cd246702bee5938545b18a6f710ed214bcJamie Gennis if (token.previous.type in (Type.START_PAREN, Type.START_BRACKET, 4142da489cd246702bee5938545b18a6f710ed214bcJamie Gennis Type.FUNCTION_NAME) 4152da489cd246702bee5938545b18a6f710ed214bcJamie Gennis or token.next.type == Type.START_PARAMETERS): 4162da489cd246702bee5938545b18a6f710ed214bcJamie Gennis self._HandleError( 4172da489cd246702bee5938545b18a6f710ed214bcJamie Gennis errors.EXTRA_SPACE, 4182da489cd246702bee5938545b18a6f710ed214bcJamie Gennis 'Extra space after "%s"' % token.previous.string, 4192da489cd246702bee5938545b18a6f710ed214bcJamie Gennis token, 4202da489cd246702bee5938545b18a6f710ed214bcJamie Gennis Position.All(token.string)) 4212da489cd246702bee5938545b18a6f710ed214bcJamie Gennis 4222da489cd246702bee5938545b18a6f710ed214bcJamie Gennis def _ReportMissingProvides(self, missing_provides, token, need_blank_line): 4232da489cd246702bee5938545b18a6f710ed214bcJamie Gennis """Reports missing provide statements to the error handler. 4242da489cd246702bee5938545b18a6f710ed214bcJamie Gennis 4252da489cd246702bee5938545b18a6f710ed214bcJamie Gennis Args: 4262da489cd246702bee5938545b18a6f710ed214bcJamie Gennis missing_provides: A list of strings where each string is a namespace that 4272da489cd246702bee5938545b18a6f710ed214bcJamie Gennis should be provided, but is not. 4282da489cd246702bee5938545b18a6f710ed214bcJamie Gennis token: The token where the error was detected (also where the new provides 4292da489cd246702bee5938545b18a6f710ed214bcJamie Gennis will be inserted. 4302da489cd246702bee5938545b18a6f710ed214bcJamie Gennis need_blank_line: Whether a blank line needs to be inserted after the new 4312da489cd246702bee5938545b18a6f710ed214bcJamie Gennis provides are inserted. May be True, False, or None, where None 4322da489cd246702bee5938545b18a6f710ed214bcJamie Gennis indicates that the insert location is unknown. 4332da489cd246702bee5938545b18a6f710ed214bcJamie Gennis """ 4342da489cd246702bee5938545b18a6f710ed214bcJamie Gennis self._HandleError( 4352da489cd246702bee5938545b18a6f710ed214bcJamie Gennis errors.MISSING_GOOG_PROVIDE, 4362da489cd246702bee5938545b18a6f710ed214bcJamie Gennis 'Missing the following goog.provide statements:\n' + 4372da489cd246702bee5938545b18a6f710ed214bcJamie Gennis '\n'.join(map(lambda x: 'goog.provide(\'%s\');' % x, 4382da489cd246702bee5938545b18a6f710ed214bcJamie Gennis sorted(missing_provides))), 4392da489cd246702bee5938545b18a6f710ed214bcJamie Gennis token, position=Position.AtBeginning(), 4402da489cd246702bee5938545b18a6f710ed214bcJamie Gennis fix_data=(missing_provides, need_blank_line)) 4412da489cd246702bee5938545b18a6f710ed214bcJamie Gennis 4422da489cd246702bee5938545b18a6f710ed214bcJamie Gennis def _ReportMissingRequires(self, missing_requires, token, need_blank_line): 4432da489cd246702bee5938545b18a6f710ed214bcJamie Gennis """Reports missing require statements to the error handler. 4442da489cd246702bee5938545b18a6f710ed214bcJamie Gennis 4452da489cd246702bee5938545b18a6f710ed214bcJamie Gennis Args: 4462da489cd246702bee5938545b18a6f710ed214bcJamie Gennis missing_requires: A list of strings where each string is a namespace that 4472da489cd246702bee5938545b18a6f710ed214bcJamie Gennis should be required, but is not. 4482da489cd246702bee5938545b18a6f710ed214bcJamie Gennis token: The token where the error was detected (also where the new requires 4492da489cd246702bee5938545b18a6f710ed214bcJamie Gennis will be inserted. 4502da489cd246702bee5938545b18a6f710ed214bcJamie Gennis need_blank_line: Whether a blank line needs to be inserted before the new 4512da489cd246702bee5938545b18a6f710ed214bcJamie Gennis requires are inserted. May be True, False, or None, where None 4522da489cd246702bee5938545b18a6f710ed214bcJamie Gennis indicates that the insert location is unknown. 4532da489cd246702bee5938545b18a6f710ed214bcJamie Gennis """ 4542da489cd246702bee5938545b18a6f710ed214bcJamie Gennis self._HandleError( 4552da489cd246702bee5938545b18a6f710ed214bcJamie Gennis errors.MISSING_GOOG_REQUIRE, 4562da489cd246702bee5938545b18a6f710ed214bcJamie Gennis 'Missing the following goog.require statements:\n' + 4572da489cd246702bee5938545b18a6f710ed214bcJamie Gennis '\n'.join(map(lambda x: 'goog.require(\'%s\');' % x, 4582da489cd246702bee5938545b18a6f710ed214bcJamie Gennis sorted(missing_requires))), 4592da489cd246702bee5938545b18a6f710ed214bcJamie Gennis token, position=Position.AtBeginning(), 4602da489cd246702bee5938545b18a6f710ed214bcJamie Gennis fix_data=(missing_requires, need_blank_line)) 4612da489cd246702bee5938545b18a6f710ed214bcJamie Gennis 4622da489cd246702bee5938545b18a6f710ed214bcJamie Gennis def Finalize(self, state, tokenizer_mode): 4632da489cd246702bee5938545b18a6f710ed214bcJamie Gennis """Perform all checks that need to occur after all lines are processed.""" 4642da489cd246702bee5938545b18a6f710ed214bcJamie Gennis # Call the base class's Finalize function. 4652da489cd246702bee5938545b18a6f710ed214bcJamie Gennis super(JavaScriptLintRules, self).Finalize(state, tokenizer_mode) 4662da489cd246702bee5938545b18a6f710ed214bcJamie Gennis 4672da489cd246702bee5938545b18a6f710ed214bcJamie Gennis if error_check.ShouldCheck(Rule.UNUSED_PRIVATE_MEMBERS): 4682da489cd246702bee5938545b18a6f710ed214bcJamie Gennis # Report an error for any declared private member that was never used. 4692da489cd246702bee5938545b18a6f710ed214bcJamie Gennis unused_private_members = (self._declared_private_members - 4702da489cd246702bee5938545b18a6f710ed214bcJamie Gennis self._used_private_members) 4712da489cd246702bee5938545b18a6f710ed214bcJamie Gennis 4722da489cd246702bee5938545b18a6f710ed214bcJamie Gennis for variable in unused_private_members: 4732da489cd246702bee5938545b18a6f710ed214bcJamie Gennis token = self._declared_private_member_tokens[variable] 4742da489cd246702bee5938545b18a6f710ed214bcJamie Gennis self._HandleError(errors.UNUSED_PRIVATE_MEMBER, 4752da489cd246702bee5938545b18a6f710ed214bcJamie Gennis 'Unused private member: %s.' % token.string, 4762da489cd246702bee5938545b18a6f710ed214bcJamie Gennis token) 4772da489cd246702bee5938545b18a6f710ed214bcJamie Gennis 4782da489cd246702bee5938545b18a6f710ed214bcJamie Gennis # Clear state to prepare for the next file. 4792da489cd246702bee5938545b18a6f710ed214bcJamie Gennis self._declared_private_member_tokens = {} 4802da489cd246702bee5938545b18a6f710ed214bcJamie Gennis self._declared_private_members = Set() 4812da489cd246702bee5938545b18a6f710ed214bcJamie Gennis self._used_private_members = Set() 4822da489cd246702bee5938545b18a6f710ed214bcJamie Gennis 4832da489cd246702bee5938545b18a6f710ed214bcJamie Gennis namespaces_info = self._namespaces_info 4842da489cd246702bee5938545b18a6f710ed214bcJamie Gennis if namespaces_info is not None: 4852da489cd246702bee5938545b18a6f710ed214bcJamie Gennis # If there are no provide or require statements, missing provides and 4862da489cd246702bee5938545b18a6f710ed214bcJamie Gennis # requires should be reported on line 1. 4872da489cd246702bee5938545b18a6f710ed214bcJamie Gennis if (not namespaces_info.GetProvidedNamespaces() and 4882da489cd246702bee5938545b18a6f710ed214bcJamie Gennis not namespaces_info.GetRequiredNamespaces()): 4892da489cd246702bee5938545b18a6f710ed214bcJamie Gennis missing_provides = namespaces_info.GetMissingProvides() 4902da489cd246702bee5938545b18a6f710ed214bcJamie Gennis if missing_provides: 4912da489cd246702bee5938545b18a6f710ed214bcJamie Gennis self._ReportMissingProvides( 4922da489cd246702bee5938545b18a6f710ed214bcJamie Gennis missing_provides, state.GetFirstToken(), None) 4932da489cd246702bee5938545b18a6f710ed214bcJamie Gennis 4942da489cd246702bee5938545b18a6f710ed214bcJamie Gennis missing_requires = namespaces_info.GetMissingRequires() 4952da489cd246702bee5938545b18a6f710ed214bcJamie Gennis if missing_requires: 4962da489cd246702bee5938545b18a6f710ed214bcJamie Gennis self._ReportMissingRequires( 4972da489cd246702bee5938545b18a6f710ed214bcJamie Gennis missing_requires, state.GetFirstToken(), None) 4982da489cd246702bee5938545b18a6f710ed214bcJamie Gennis 4992da489cd246702bee5938545b18a6f710ed214bcJamie Gennis self._CheckSortedRequiresProvides(state.GetFirstToken()) 5002da489cd246702bee5938545b18a6f710ed214bcJamie Gennis 5012da489cd246702bee5938545b18a6f710ed214bcJamie Gennis def _CheckSortedRequiresProvides(self, token): 5022da489cd246702bee5938545b18a6f710ed214bcJamie Gennis """Checks that all goog.require and goog.provide statements are sorted. 5032da489cd246702bee5938545b18a6f710ed214bcJamie Gennis 5042da489cd246702bee5938545b18a6f710ed214bcJamie Gennis Note that this method needs to be run after missing statements are added to 5052da489cd246702bee5938545b18a6f710ed214bcJamie Gennis preserve alphabetical order. 5062da489cd246702bee5938545b18a6f710ed214bcJamie Gennis 5072da489cd246702bee5938545b18a6f710ed214bcJamie Gennis Args: 5082da489cd246702bee5938545b18a6f710ed214bcJamie Gennis token: The first token in the token stream. 5092da489cd246702bee5938545b18a6f710ed214bcJamie Gennis """ 5102da489cd246702bee5938545b18a6f710ed214bcJamie Gennis sorter = requireprovidesorter.RequireProvideSorter() 5112da489cd246702bee5938545b18a6f710ed214bcJamie Gennis provides_result = sorter.CheckProvides(token) 5122da489cd246702bee5938545b18a6f710ed214bcJamie Gennis if provides_result: 5132da489cd246702bee5938545b18a6f710ed214bcJamie Gennis self._HandleError( 5142da489cd246702bee5938545b18a6f710ed214bcJamie Gennis errors.GOOG_PROVIDES_NOT_ALPHABETIZED, 5152da489cd246702bee5938545b18a6f710ed214bcJamie Gennis 'goog.provide classes must be alphabetized. The correct code is:\n' + 5162da489cd246702bee5938545b18a6f710ed214bcJamie Gennis '\n'.join( 5172da489cd246702bee5938545b18a6f710ed214bcJamie Gennis map(lambda x: 'goog.provide(\'%s\');' % x, provides_result[1])), 5182da489cd246702bee5938545b18a6f710ed214bcJamie Gennis provides_result[0], 5192da489cd246702bee5938545b18a6f710ed214bcJamie Gennis position=Position.AtBeginning(), 5202da489cd246702bee5938545b18a6f710ed214bcJamie Gennis fix_data=provides_result[0]) 5212da489cd246702bee5938545b18a6f710ed214bcJamie Gennis 5222da489cd246702bee5938545b18a6f710ed214bcJamie Gennis requires_result = sorter.CheckRequires(token) 5232da489cd246702bee5938545b18a6f710ed214bcJamie Gennis if requires_result: 5242da489cd246702bee5938545b18a6f710ed214bcJamie Gennis self._HandleError( 5252da489cd246702bee5938545b18a6f710ed214bcJamie Gennis errors.GOOG_REQUIRES_NOT_ALPHABETIZED, 5262da489cd246702bee5938545b18a6f710ed214bcJamie Gennis 'goog.require classes must be alphabetized. The correct code is:\n' + 5272da489cd246702bee5938545b18a6f710ed214bcJamie Gennis '\n'.join( 5282da489cd246702bee5938545b18a6f710ed214bcJamie Gennis map(lambda x: 'goog.require(\'%s\');' % x, requires_result[1])), 5292da489cd246702bee5938545b18a6f710ed214bcJamie Gennis requires_result[0], 5302da489cd246702bee5938545b18a6f710ed214bcJamie Gennis position=Position.AtBeginning(), 5312da489cd246702bee5938545b18a6f710ed214bcJamie Gennis fix_data=requires_result[0]) 5322da489cd246702bee5938545b18a6f710ed214bcJamie Gennis 5332da489cd246702bee5938545b18a6f710ed214bcJamie Gennis def GetLongLineExceptions(self): 5342da489cd246702bee5938545b18a6f710ed214bcJamie Gennis """Gets a list of regexps for lines which can be longer than the limit.""" 5352da489cd246702bee5938545b18a6f710ed214bcJamie Gennis return [ 53666a37686207944273ced825e0e8b6b6375f8c3deJamie Gennis re.compile('.*// @suppress longLineCheck$'), 5372da489cd246702bee5938545b18a6f710ed214bcJamie Gennis re.compile('goog\.require\(.+\);?\s*$'), 5382da489cd246702bee5938545b18a6f710ed214bcJamie Gennis re.compile('goog\.provide\(.+\);?\s*$') 5392da489cd246702bee5938545b18a6f710ed214bcJamie Gennis ] 540