12da489cd246702bee5938545b18a6f710ed214bcJamie Gennis#!/usr/bin/env python 22da489cd246702bee5938545b18a6f710ed214bcJamie Gennis# 32da489cd246702bee5938545b18a6f710ed214bcJamie Gennis# Copyright 2008 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"""Core methods for checking EcmaScript files for common style guide violations. 182da489cd246702bee5938545b18a6f710ed214bcJamie Gennis""" 192da489cd246702bee5938545b18a6f710ed214bcJamie Gennis 202da489cd246702bee5938545b18a6f710ed214bcJamie Gennis__author__ = ('robbyw@google.com (Robert Walker)', 212da489cd246702bee5938545b18a6f710ed214bcJamie Gennis 'ajp@google.com (Andy Perelson)', 222da489cd246702bee5938545b18a6f710ed214bcJamie Gennis 'jacobr@google.com (Jacob Richman)') 232da489cd246702bee5938545b18a6f710ed214bcJamie Gennis 242da489cd246702bee5938545b18a6f710ed214bcJamie Gennisimport re 252da489cd246702bee5938545b18a6f710ed214bcJamie Gennis 262da489cd246702bee5938545b18a6f710ed214bcJamie Gennisfrom closure_linter import checkerbase 272da489cd246702bee5938545b18a6f710ed214bcJamie Gennisfrom closure_linter import ecmametadatapass 282da489cd246702bee5938545b18a6f710ed214bcJamie Gennisfrom closure_linter import error_check 292da489cd246702bee5938545b18a6f710ed214bcJamie Gennisfrom closure_linter import errors 302da489cd246702bee5938545b18a6f710ed214bcJamie Gennisfrom closure_linter import indentation 312da489cd246702bee5938545b18a6f710ed214bcJamie Gennisfrom closure_linter import javascripttokens 322da489cd246702bee5938545b18a6f710ed214bcJamie Gennisfrom closure_linter import javascripttokenizer 332da489cd246702bee5938545b18a6f710ed214bcJamie Gennisfrom closure_linter import statetracker 342da489cd246702bee5938545b18a6f710ed214bcJamie Gennisfrom closure_linter import tokenutil 352da489cd246702bee5938545b18a6f710ed214bcJamie Gennisfrom closure_linter.common import error 362da489cd246702bee5938545b18a6f710ed214bcJamie Gennisfrom closure_linter.common import htmlutil 372da489cd246702bee5938545b18a6f710ed214bcJamie Gennisfrom closure_linter.common import lintrunner 382da489cd246702bee5938545b18a6f710ed214bcJamie Gennisfrom closure_linter.common import position 392da489cd246702bee5938545b18a6f710ed214bcJamie Gennisfrom closure_linter.common import tokens 402da489cd246702bee5938545b18a6f710ed214bcJamie Gennisimport gflags as flags 412da489cd246702bee5938545b18a6f710ed214bcJamie Gennis 422da489cd246702bee5938545b18a6f710ed214bcJamie GennisFLAGS = flags.FLAGS 432da489cd246702bee5938545b18a6f710ed214bcJamie Gennisflags.DEFINE_list('custom_jsdoc_tags', '', 'Extra jsdoc tags to allow') 442da489cd246702bee5938545b18a6f710ed214bcJamie Gennis 452da489cd246702bee5938545b18a6f710ed214bcJamie Gennis# TODO(robbyw): Check for extra parens on return statements 462da489cd246702bee5938545b18a6f710ed214bcJamie Gennis# TODO(robbyw): Check for 0px in strings 472da489cd246702bee5938545b18a6f710ed214bcJamie Gennis# TODO(robbyw): Ensure inline jsDoc is in {} 482da489cd246702bee5938545b18a6f710ed214bcJamie Gennis# TODO(robbyw): Check for valid JS types in parameter docs 492da489cd246702bee5938545b18a6f710ed214bcJamie Gennis 502da489cd246702bee5938545b18a6f710ed214bcJamie Gennis# Shorthand 512da489cd246702bee5938545b18a6f710ed214bcJamie GennisContext = ecmametadatapass.EcmaContext 522da489cd246702bee5938545b18a6f710ed214bcJamie GennisError = error.Error 532da489cd246702bee5938545b18a6f710ed214bcJamie GennisModes = javascripttokenizer.JavaScriptModes 542da489cd246702bee5938545b18a6f710ed214bcJamie GennisPosition = position.Position 552da489cd246702bee5938545b18a6f710ed214bcJamie GennisRule = error_check.Rule 562da489cd246702bee5938545b18a6f710ed214bcJamie GennisType = javascripttokens.JavaScriptTokenType 572da489cd246702bee5938545b18a6f710ed214bcJamie Gennis 582da489cd246702bee5938545b18a6f710ed214bcJamie Gennisclass EcmaScriptLintRules(checkerbase.LintRulesBase): 592da489cd246702bee5938545b18a6f710ed214bcJamie Gennis """EmcaScript lint style checking rules. 602da489cd246702bee5938545b18a6f710ed214bcJamie Gennis 612da489cd246702bee5938545b18a6f710ed214bcJamie Gennis Can be used to find common style errors in JavaScript, ActionScript and other 622da489cd246702bee5938545b18a6f710ed214bcJamie Gennis Ecma like scripting languages. Style checkers for Ecma scripting languages 632da489cd246702bee5938545b18a6f710ed214bcJamie Gennis should inherit from this style checker. 642da489cd246702bee5938545b18a6f710ed214bcJamie Gennis Please do not add any state to EcmaScriptLintRules or to any subclasses. 652da489cd246702bee5938545b18a6f710ed214bcJamie Gennis 662da489cd246702bee5938545b18a6f710ed214bcJamie Gennis All state should be added to the StateTracker subclass used for a particular 672da489cd246702bee5938545b18a6f710ed214bcJamie Gennis language. 682da489cd246702bee5938545b18a6f710ed214bcJamie Gennis """ 692da489cd246702bee5938545b18a6f710ed214bcJamie Gennis 702da489cd246702bee5938545b18a6f710ed214bcJamie Gennis # Static constants. 712da489cd246702bee5938545b18a6f710ed214bcJamie Gennis MAX_LINE_LENGTH = 80 722da489cd246702bee5938545b18a6f710ed214bcJamie Gennis 732da489cd246702bee5938545b18a6f710ed214bcJamie Gennis MISSING_PARAMETER_SPACE = re.compile(r',\S') 742da489cd246702bee5938545b18a6f710ed214bcJamie Gennis 752da489cd246702bee5938545b18a6f710ed214bcJamie Gennis EXTRA_SPACE = re.compile('(\(\s|\s\))') 762da489cd246702bee5938545b18a6f710ed214bcJamie Gennis 772da489cd246702bee5938545b18a6f710ed214bcJamie Gennis ENDS_WITH_SPACE = re.compile('\s$') 782da489cd246702bee5938545b18a6f710ed214bcJamie Gennis 792da489cd246702bee5938545b18a6f710ed214bcJamie Gennis ILLEGAL_TAB = re.compile(r'\t') 802da489cd246702bee5938545b18a6f710ed214bcJamie Gennis 812da489cd246702bee5938545b18a6f710ed214bcJamie Gennis # Regex used to split up complex types to check for invalid use of ? and |. 822da489cd246702bee5938545b18a6f710ed214bcJamie Gennis TYPE_SPLIT = re.compile(r'[,<>()]') 832da489cd246702bee5938545b18a6f710ed214bcJamie Gennis 842da489cd246702bee5938545b18a6f710ed214bcJamie Gennis # Regex for form of author lines after the @author tag. 852da489cd246702bee5938545b18a6f710ed214bcJamie Gennis AUTHOR_SPEC = re.compile(r'(\s*)[^\s]+@[^(\s]+(\s*)\(.+\)') 862da489cd246702bee5938545b18a6f710ed214bcJamie Gennis 872da489cd246702bee5938545b18a6f710ed214bcJamie Gennis # Acceptable tokens to remove for line too long testing. 882da489cd246702bee5938545b18a6f710ed214bcJamie Gennis LONG_LINE_IGNORE = frozenset(['*', '//', '@see'] + 892da489cd246702bee5938545b18a6f710ed214bcJamie Gennis ['@%s' % tag for tag in statetracker.DocFlag.HAS_TYPE]) 902da489cd246702bee5938545b18a6f710ed214bcJamie Gennis 912da489cd246702bee5938545b18a6f710ed214bcJamie Gennis def __init__(self): 922da489cd246702bee5938545b18a6f710ed214bcJamie Gennis """Initialize this lint rule object.""" 932da489cd246702bee5938545b18a6f710ed214bcJamie Gennis checkerbase.LintRulesBase.__init__(self) 942da489cd246702bee5938545b18a6f710ed214bcJamie Gennis 952da489cd246702bee5938545b18a6f710ed214bcJamie Gennis def Initialize(self, checker, limited_doc_checks, is_html): 962da489cd246702bee5938545b18a6f710ed214bcJamie Gennis """Initialize this lint rule object before parsing a new file.""" 972da489cd246702bee5938545b18a6f710ed214bcJamie Gennis checkerbase.LintRulesBase.Initialize(self, checker, limited_doc_checks, 982da489cd246702bee5938545b18a6f710ed214bcJamie Gennis is_html) 992da489cd246702bee5938545b18a6f710ed214bcJamie Gennis self._indentation = indentation.IndentationRules() 1002da489cd246702bee5938545b18a6f710ed214bcJamie Gennis 1012da489cd246702bee5938545b18a6f710ed214bcJamie Gennis def HandleMissingParameterDoc(self, token, param_name): 1022da489cd246702bee5938545b18a6f710ed214bcJamie Gennis """Handle errors associated with a parameter missing a @param tag.""" 1032da489cd246702bee5938545b18a6f710ed214bcJamie Gennis raise TypeError('Abstract method HandleMissingParameterDoc not implemented') 1042da489cd246702bee5938545b18a6f710ed214bcJamie Gennis 1052da489cd246702bee5938545b18a6f710ed214bcJamie Gennis def _CheckLineLength(self, last_token, state): 1062da489cd246702bee5938545b18a6f710ed214bcJamie Gennis """Checks whether the line is too long. 1072da489cd246702bee5938545b18a6f710ed214bcJamie Gennis 1082da489cd246702bee5938545b18a6f710ed214bcJamie Gennis Args: 1092da489cd246702bee5938545b18a6f710ed214bcJamie Gennis last_token: The last token in the line. 1102da489cd246702bee5938545b18a6f710ed214bcJamie Gennis """ 1112da489cd246702bee5938545b18a6f710ed214bcJamie Gennis # Start from the last token so that we have the flag object attached to 1122da489cd246702bee5938545b18a6f710ed214bcJamie Gennis # and DOC_FLAG tokens. 1132da489cd246702bee5938545b18a6f710ed214bcJamie Gennis line_number = last_token.line_number 1142da489cd246702bee5938545b18a6f710ed214bcJamie Gennis token = last_token 1152da489cd246702bee5938545b18a6f710ed214bcJamie Gennis 1162da489cd246702bee5938545b18a6f710ed214bcJamie Gennis # Build a representation of the string where spaces indicate potential 1172da489cd246702bee5938545b18a6f710ed214bcJamie Gennis # line-break locations. 1182da489cd246702bee5938545b18a6f710ed214bcJamie Gennis line = [] 1192da489cd246702bee5938545b18a6f710ed214bcJamie Gennis while token and token.line_number == line_number: 1202da489cd246702bee5938545b18a6f710ed214bcJamie Gennis if state.IsTypeToken(token): 1212da489cd246702bee5938545b18a6f710ed214bcJamie Gennis line.insert(0, 'x' * len(token.string)) 1222da489cd246702bee5938545b18a6f710ed214bcJamie Gennis elif token.type in (Type.IDENTIFIER, Type.NORMAL): 1232da489cd246702bee5938545b18a6f710ed214bcJamie Gennis # Dots are acceptable places to wrap. 1242da489cd246702bee5938545b18a6f710ed214bcJamie Gennis line.insert(0, token.string.replace('.', ' ')) 1252da489cd246702bee5938545b18a6f710ed214bcJamie Gennis else: 1262da489cd246702bee5938545b18a6f710ed214bcJamie Gennis line.insert(0, token.string) 1272da489cd246702bee5938545b18a6f710ed214bcJamie Gennis token = token.previous 1282da489cd246702bee5938545b18a6f710ed214bcJamie Gennis 1292da489cd246702bee5938545b18a6f710ed214bcJamie Gennis line = ''.join(line) 1302da489cd246702bee5938545b18a6f710ed214bcJamie Gennis line = line.rstrip('\n\r\f') 1312da489cd246702bee5938545b18a6f710ed214bcJamie Gennis try: 1322da489cd246702bee5938545b18a6f710ed214bcJamie Gennis length = len(unicode(line, 'utf-8')) 1332da489cd246702bee5938545b18a6f710ed214bcJamie Gennis except: 1342da489cd246702bee5938545b18a6f710ed214bcJamie Gennis # Unknown encoding. The line length may be wrong, as was originally the 1352da489cd246702bee5938545b18a6f710ed214bcJamie Gennis # case for utf-8 (see bug 1735846). For now just accept the default 1362da489cd246702bee5938545b18a6f710ed214bcJamie Gennis # length, but as we find problems we can either add test for other 1372da489cd246702bee5938545b18a6f710ed214bcJamie Gennis # possible encodings or return without an error to protect against 1382da489cd246702bee5938545b18a6f710ed214bcJamie Gennis # false positives at the cost of more false negatives. 1392da489cd246702bee5938545b18a6f710ed214bcJamie Gennis length = len(line) 1402da489cd246702bee5938545b18a6f710ed214bcJamie Gennis 1412da489cd246702bee5938545b18a6f710ed214bcJamie Gennis if length > self.MAX_LINE_LENGTH: 1422da489cd246702bee5938545b18a6f710ed214bcJamie Gennis 1432da489cd246702bee5938545b18a6f710ed214bcJamie Gennis # If the line matches one of the exceptions, then it's ok. 1442da489cd246702bee5938545b18a6f710ed214bcJamie Gennis for long_line_regexp in self.GetLongLineExceptions(): 1452da489cd246702bee5938545b18a6f710ed214bcJamie Gennis if long_line_regexp.match(last_token.line): 1462da489cd246702bee5938545b18a6f710ed214bcJamie Gennis return 1472da489cd246702bee5938545b18a6f710ed214bcJamie Gennis 1482da489cd246702bee5938545b18a6f710ed214bcJamie Gennis # If the line consists of only one "word", or multiple words but all 1492da489cd246702bee5938545b18a6f710ed214bcJamie Gennis # except one are ignoreable, then it's ok. 1502da489cd246702bee5938545b18a6f710ed214bcJamie Gennis parts = set(line.split()) 1512da489cd246702bee5938545b18a6f710ed214bcJamie Gennis 1522da489cd246702bee5938545b18a6f710ed214bcJamie Gennis # We allow two "words" (type and name) when the line contains @param 1532da489cd246702bee5938545b18a6f710ed214bcJamie Gennis max = 1 1542da489cd246702bee5938545b18a6f710ed214bcJamie Gennis if '@param' in parts: 1552da489cd246702bee5938545b18a6f710ed214bcJamie Gennis max = 2 1562da489cd246702bee5938545b18a6f710ed214bcJamie Gennis 1572da489cd246702bee5938545b18a6f710ed214bcJamie Gennis # Custom tags like @requires may have url like descriptions, so ignore 1582da489cd246702bee5938545b18a6f710ed214bcJamie Gennis # the tag, similar to how we handle @see. 1592da489cd246702bee5938545b18a6f710ed214bcJamie Gennis custom_tags = set(['@%s' % f for f in FLAGS.custom_jsdoc_tags]) 1602da489cd246702bee5938545b18a6f710ed214bcJamie Gennis if (len(parts.difference(self.LONG_LINE_IGNORE | custom_tags)) > max): 1612da489cd246702bee5938545b18a6f710ed214bcJamie Gennis self._HandleError(errors.LINE_TOO_LONG, 1622da489cd246702bee5938545b18a6f710ed214bcJamie Gennis 'Line too long (%d characters).' % len(line), last_token) 1632da489cd246702bee5938545b18a6f710ed214bcJamie Gennis 1642da489cd246702bee5938545b18a6f710ed214bcJamie Gennis def _CheckJsDocType(self, token): 1652da489cd246702bee5938545b18a6f710ed214bcJamie Gennis """Checks the given type for style errors. 1662da489cd246702bee5938545b18a6f710ed214bcJamie Gennis 1672da489cd246702bee5938545b18a6f710ed214bcJamie Gennis Args: 1682da489cd246702bee5938545b18a6f710ed214bcJamie Gennis token: The DOC_FLAG token for the flag whose type to check. 1692da489cd246702bee5938545b18a6f710ed214bcJamie Gennis """ 1702da489cd246702bee5938545b18a6f710ed214bcJamie Gennis flag = token.attached_object 1712da489cd246702bee5938545b18a6f710ed214bcJamie Gennis type = flag.type 1722da489cd246702bee5938545b18a6f710ed214bcJamie Gennis if type and type is not None and not type.isspace(): 1732da489cd246702bee5938545b18a6f710ed214bcJamie Gennis pieces = self.TYPE_SPLIT.split(type) 1742da489cd246702bee5938545b18a6f710ed214bcJamie Gennis if len(pieces) == 1 and type.count('|') == 1 and ( 1752da489cd246702bee5938545b18a6f710ed214bcJamie Gennis type.endswith('|null') or type.startswith('null|')): 1762da489cd246702bee5938545b18a6f710ed214bcJamie Gennis self._HandleError(errors.JSDOC_PREFER_QUESTION_TO_PIPE_NULL, 1772da489cd246702bee5938545b18a6f710ed214bcJamie Gennis 'Prefer "?Type" to "Type|null": "%s"' % type, token) 1782da489cd246702bee5938545b18a6f710ed214bcJamie Gennis 1792da489cd246702bee5938545b18a6f710ed214bcJamie Gennis for p in pieces: 1802da489cd246702bee5938545b18a6f710ed214bcJamie Gennis if p.count('|') and p.count('?'): 1812da489cd246702bee5938545b18a6f710ed214bcJamie Gennis # TODO(robbyw): We should do actual parsing of JsDoc types. As is, 1822da489cd246702bee5938545b18a6f710ed214bcJamie Gennis # this won't report an error for {number|Array.<string>?}, etc. 1832da489cd246702bee5938545b18a6f710ed214bcJamie Gennis self._HandleError(errors.JSDOC_ILLEGAL_QUESTION_WITH_PIPE, 1842da489cd246702bee5938545b18a6f710ed214bcJamie Gennis 'JsDoc types cannot contain both "?" and "|": "%s"' % p, token) 1852da489cd246702bee5938545b18a6f710ed214bcJamie Gennis 1862da489cd246702bee5938545b18a6f710ed214bcJamie Gennis if error_check.ShouldCheck(Rule.BRACES_AROUND_TYPE) and ( 1872da489cd246702bee5938545b18a6f710ed214bcJamie Gennis flag.type_start_token.type != Type.DOC_START_BRACE or 1882da489cd246702bee5938545b18a6f710ed214bcJamie Gennis flag.type_end_token.type != Type.DOC_END_BRACE): 1892da489cd246702bee5938545b18a6f710ed214bcJamie Gennis self._HandleError(errors.MISSING_BRACES_AROUND_TYPE, 1902da489cd246702bee5938545b18a6f710ed214bcJamie Gennis 'Type must always be surrounded by curly braces.', token) 1912da489cd246702bee5938545b18a6f710ed214bcJamie Gennis 1922da489cd246702bee5938545b18a6f710ed214bcJamie Gennis def _CheckForMissingSpaceBeforeToken(self, token): 1932da489cd246702bee5938545b18a6f710ed214bcJamie Gennis """Checks for a missing space at the beginning of a token. 1942da489cd246702bee5938545b18a6f710ed214bcJamie Gennis 1952da489cd246702bee5938545b18a6f710ed214bcJamie Gennis Reports a MISSING_SPACE error if the token does not begin with a space or 1962da489cd246702bee5938545b18a6f710ed214bcJamie Gennis the previous token doesn't end with a space and the previous token is on the 1972da489cd246702bee5938545b18a6f710ed214bcJamie Gennis same line as the token. 1982da489cd246702bee5938545b18a6f710ed214bcJamie Gennis 1992da489cd246702bee5938545b18a6f710ed214bcJamie Gennis Args: 2002da489cd246702bee5938545b18a6f710ed214bcJamie Gennis token: The token being checked 2012da489cd246702bee5938545b18a6f710ed214bcJamie Gennis """ 2022da489cd246702bee5938545b18a6f710ed214bcJamie Gennis # TODO(user): Check if too many spaces? 2032da489cd246702bee5938545b18a6f710ed214bcJamie Gennis if (len(token.string) == len(token.string.lstrip()) and 2042da489cd246702bee5938545b18a6f710ed214bcJamie Gennis token.previous and token.line_number == token.previous.line_number and 2052da489cd246702bee5938545b18a6f710ed214bcJamie Gennis len(token.previous.string) - len(token.previous.string.rstrip()) == 0): 2062da489cd246702bee5938545b18a6f710ed214bcJamie Gennis self._HandleError( 2072da489cd246702bee5938545b18a6f710ed214bcJamie Gennis errors.MISSING_SPACE, 2082da489cd246702bee5938545b18a6f710ed214bcJamie Gennis 'Missing space before "%s"' % token.string, 2092da489cd246702bee5938545b18a6f710ed214bcJamie Gennis token, 2102da489cd246702bee5938545b18a6f710ed214bcJamie Gennis Position.AtBeginning()) 2112da489cd246702bee5938545b18a6f710ed214bcJamie Gennis 2122da489cd246702bee5938545b18a6f710ed214bcJamie Gennis def _ExpectSpaceBeforeOperator(self, token): 2132da489cd246702bee5938545b18a6f710ed214bcJamie Gennis """Returns whether a space should appear before the given operator token. 2142da489cd246702bee5938545b18a6f710ed214bcJamie Gennis 2152da489cd246702bee5938545b18a6f710ed214bcJamie Gennis Args: 2162da489cd246702bee5938545b18a6f710ed214bcJamie Gennis token: The operator token. 2172da489cd246702bee5938545b18a6f710ed214bcJamie Gennis 2182da489cd246702bee5938545b18a6f710ed214bcJamie Gennis Returns: 2192da489cd246702bee5938545b18a6f710ed214bcJamie Gennis Whether there should be a space before the token. 2202da489cd246702bee5938545b18a6f710ed214bcJamie Gennis """ 2212da489cd246702bee5938545b18a6f710ed214bcJamie Gennis if token.string == ',' or token.metadata.IsUnaryPostOperator(): 2222da489cd246702bee5938545b18a6f710ed214bcJamie Gennis return False 2232da489cd246702bee5938545b18a6f710ed214bcJamie Gennis 2242da489cd246702bee5938545b18a6f710ed214bcJamie Gennis # Colons should appear in labels, object literals, the case of a switch 2252da489cd246702bee5938545b18a6f710ed214bcJamie Gennis # statement, and ternary operator. Only want a space in the case of the 2262da489cd246702bee5938545b18a6f710ed214bcJamie Gennis # ternary operator. 2272da489cd246702bee5938545b18a6f710ed214bcJamie Gennis if (token.string == ':' and 2282da489cd246702bee5938545b18a6f710ed214bcJamie Gennis token.metadata.context.type in (Context.LITERAL_ELEMENT, 2292da489cd246702bee5938545b18a6f710ed214bcJamie Gennis Context.CASE_BLOCK, 2302da489cd246702bee5938545b18a6f710ed214bcJamie Gennis Context.STATEMENT)): 2312da489cd246702bee5938545b18a6f710ed214bcJamie Gennis return False 2322da489cd246702bee5938545b18a6f710ed214bcJamie Gennis 2332da489cd246702bee5938545b18a6f710ed214bcJamie Gennis if token.metadata.IsUnaryOperator() and token.IsFirstInLine(): 2342da489cd246702bee5938545b18a6f710ed214bcJamie Gennis return False 2352da489cd246702bee5938545b18a6f710ed214bcJamie Gennis 2362da489cd246702bee5938545b18a6f710ed214bcJamie Gennis return True 2372da489cd246702bee5938545b18a6f710ed214bcJamie Gennis 2382da489cd246702bee5938545b18a6f710ed214bcJamie Gennis def CheckToken(self, token, state): 2392da489cd246702bee5938545b18a6f710ed214bcJamie Gennis """Checks a token, given the current parser_state, for warnings and errors. 2402da489cd246702bee5938545b18a6f710ed214bcJamie Gennis 2412da489cd246702bee5938545b18a6f710ed214bcJamie Gennis Args: 2422da489cd246702bee5938545b18a6f710ed214bcJamie Gennis token: The current token under consideration 2432da489cd246702bee5938545b18a6f710ed214bcJamie Gennis state: parser_state object that indicates the current state in the page 2442da489cd246702bee5938545b18a6f710ed214bcJamie Gennis """ 2452da489cd246702bee5938545b18a6f710ed214bcJamie Gennis # Store some convenience variables 2462da489cd246702bee5938545b18a6f710ed214bcJamie Gennis first_in_line = token.IsFirstInLine() 2472da489cd246702bee5938545b18a6f710ed214bcJamie Gennis last_in_line = token.IsLastInLine() 2482da489cd246702bee5938545b18a6f710ed214bcJamie Gennis last_non_space_token = state.GetLastNonSpaceToken() 2492da489cd246702bee5938545b18a6f710ed214bcJamie Gennis 2502da489cd246702bee5938545b18a6f710ed214bcJamie Gennis type = token.type 2512da489cd246702bee5938545b18a6f710ed214bcJamie Gennis 2522da489cd246702bee5938545b18a6f710ed214bcJamie Gennis # Process the line change. 2532da489cd246702bee5938545b18a6f710ed214bcJamie Gennis if not self._is_html and error_check.ShouldCheck(Rule.INDENTATION): 2542da489cd246702bee5938545b18a6f710ed214bcJamie Gennis # TODO(robbyw): Support checking indentation in HTML files. 2552da489cd246702bee5938545b18a6f710ed214bcJamie Gennis indentation_errors = self._indentation.CheckToken(token, state) 2562da489cd246702bee5938545b18a6f710ed214bcJamie Gennis for indentation_error in indentation_errors: 2572da489cd246702bee5938545b18a6f710ed214bcJamie Gennis self._HandleError(*indentation_error) 2582da489cd246702bee5938545b18a6f710ed214bcJamie Gennis 2592da489cd246702bee5938545b18a6f710ed214bcJamie Gennis if last_in_line: 2602da489cd246702bee5938545b18a6f710ed214bcJamie Gennis self._CheckLineLength(token, state) 2612da489cd246702bee5938545b18a6f710ed214bcJamie Gennis 2622da489cd246702bee5938545b18a6f710ed214bcJamie Gennis if type == Type.PARAMETERS: 2632da489cd246702bee5938545b18a6f710ed214bcJamie Gennis # Find missing spaces in parameter lists. 2642da489cd246702bee5938545b18a6f710ed214bcJamie Gennis if self.MISSING_PARAMETER_SPACE.search(token.string): 2652da489cd246702bee5938545b18a6f710ed214bcJamie Gennis self._HandleError(errors.MISSING_SPACE, 'Missing space after ","', 2662da489cd246702bee5938545b18a6f710ed214bcJamie Gennis token) 2672da489cd246702bee5938545b18a6f710ed214bcJamie Gennis 2682da489cd246702bee5938545b18a6f710ed214bcJamie Gennis # Find extra spaces at the beginning of parameter lists. Make sure 2692da489cd246702bee5938545b18a6f710ed214bcJamie Gennis # we aren't at the beginning of a continuing multi-line list. 2702da489cd246702bee5938545b18a6f710ed214bcJamie Gennis if not first_in_line: 2712da489cd246702bee5938545b18a6f710ed214bcJamie Gennis space_count = len(token.string) - len(token.string.lstrip()) 2722da489cd246702bee5938545b18a6f710ed214bcJamie Gennis if space_count: 2732da489cd246702bee5938545b18a6f710ed214bcJamie Gennis self._HandleError(errors.EXTRA_SPACE, 'Extra space after "("', 2742da489cd246702bee5938545b18a6f710ed214bcJamie Gennis token, Position(0, space_count)) 2752da489cd246702bee5938545b18a6f710ed214bcJamie Gennis 2762da489cd246702bee5938545b18a6f710ed214bcJamie Gennis elif (type == Type.START_BLOCK and 2772da489cd246702bee5938545b18a6f710ed214bcJamie Gennis token.metadata.context.type == Context.BLOCK): 2782da489cd246702bee5938545b18a6f710ed214bcJamie Gennis self._CheckForMissingSpaceBeforeToken(token) 2792da489cd246702bee5938545b18a6f710ed214bcJamie Gennis 2802da489cd246702bee5938545b18a6f710ed214bcJamie Gennis elif type == Type.END_BLOCK: 2812da489cd246702bee5938545b18a6f710ed214bcJamie Gennis # This check is for object literal end block tokens, but there is no need 2822da489cd246702bee5938545b18a6f710ed214bcJamie Gennis # to test that condition since a comma at the end of any other kind of 2832da489cd246702bee5938545b18a6f710ed214bcJamie Gennis # block is undoubtedly a parse error. 2842da489cd246702bee5938545b18a6f710ed214bcJamie Gennis last_code = token.metadata.last_code 2852da489cd246702bee5938545b18a6f710ed214bcJamie Gennis if last_code.IsOperator(','): 2862da489cd246702bee5938545b18a6f710ed214bcJamie Gennis self._HandleError(errors.COMMA_AT_END_OF_LITERAL, 2872da489cd246702bee5938545b18a6f710ed214bcJamie Gennis 'Illegal comma at end of object literal', last_code, 2882da489cd246702bee5938545b18a6f710ed214bcJamie Gennis Position.All(last_code.string)) 2892da489cd246702bee5938545b18a6f710ed214bcJamie Gennis 2902da489cd246702bee5938545b18a6f710ed214bcJamie Gennis if state.InFunction() and state.IsFunctionClose(): 2912da489cd246702bee5938545b18a6f710ed214bcJamie Gennis is_immediately_called = (token.next and 2922da489cd246702bee5938545b18a6f710ed214bcJamie Gennis token.next.type == Type.START_PAREN) 2932da489cd246702bee5938545b18a6f710ed214bcJamie Gennis if state.InTopLevelFunction(): 2942da489cd246702bee5938545b18a6f710ed214bcJamie Gennis # When the function was top-level and not immediately called, check 2952da489cd246702bee5938545b18a6f710ed214bcJamie Gennis # that it's terminated by a semi-colon. 2962da489cd246702bee5938545b18a6f710ed214bcJamie Gennis if state.InAssignedFunction(): 2972da489cd246702bee5938545b18a6f710ed214bcJamie Gennis if not is_immediately_called and (last_in_line or 2982da489cd246702bee5938545b18a6f710ed214bcJamie Gennis not token.next.type == Type.SEMICOLON): 2992da489cd246702bee5938545b18a6f710ed214bcJamie Gennis self._HandleError(errors.MISSING_SEMICOLON_AFTER_FUNCTION, 3002da489cd246702bee5938545b18a6f710ed214bcJamie Gennis 'Missing semicolon after function assigned to a variable', 3012da489cd246702bee5938545b18a6f710ed214bcJamie Gennis token, Position.AtEnd(token.string)) 3022da489cd246702bee5938545b18a6f710ed214bcJamie Gennis else: 3032da489cd246702bee5938545b18a6f710ed214bcJamie Gennis if not last_in_line and token.next.type == Type.SEMICOLON: 3042da489cd246702bee5938545b18a6f710ed214bcJamie Gennis self._HandleError(errors.ILLEGAL_SEMICOLON_AFTER_FUNCTION, 3052da489cd246702bee5938545b18a6f710ed214bcJamie Gennis 'Illegal semicolon after function declaration', 3062da489cd246702bee5938545b18a6f710ed214bcJamie Gennis token.next, Position.All(token.next.string)) 3072da489cd246702bee5938545b18a6f710ed214bcJamie Gennis 3082da489cd246702bee5938545b18a6f710ed214bcJamie Gennis if (state.InInterfaceMethod() and last_code.type != Type.START_BLOCK): 3092da489cd246702bee5938545b18a6f710ed214bcJamie Gennis self._HandleError(errors.INTERFACE_METHOD_CANNOT_HAVE_CODE, 3102da489cd246702bee5938545b18a6f710ed214bcJamie Gennis 'Interface methods cannot contain code', last_code) 3112da489cd246702bee5938545b18a6f710ed214bcJamie Gennis 3122da489cd246702bee5938545b18a6f710ed214bcJamie Gennis elif (state.IsBlockClose() and 3132da489cd246702bee5938545b18a6f710ed214bcJamie Gennis token.next and token.next.type == Type.SEMICOLON): 3142da489cd246702bee5938545b18a6f710ed214bcJamie Gennis self._HandleError(errors.REDUNDANT_SEMICOLON, 3152da489cd246702bee5938545b18a6f710ed214bcJamie Gennis 'No semicolon is required to end a code block', 3162da489cd246702bee5938545b18a6f710ed214bcJamie Gennis token.next, Position.All(token.next.string)) 3172da489cd246702bee5938545b18a6f710ed214bcJamie Gennis 3182da489cd246702bee5938545b18a6f710ed214bcJamie Gennis elif type == Type.SEMICOLON: 3192da489cd246702bee5938545b18a6f710ed214bcJamie Gennis if token.previous and token.previous.type == Type.WHITESPACE: 3202da489cd246702bee5938545b18a6f710ed214bcJamie Gennis self._HandleError(errors.EXTRA_SPACE, 'Extra space before ";"', 3212da489cd246702bee5938545b18a6f710ed214bcJamie Gennis token.previous, Position.All(token.previous.string)) 3222da489cd246702bee5938545b18a6f710ed214bcJamie Gennis 3232da489cd246702bee5938545b18a6f710ed214bcJamie Gennis if token.next and token.next.line_number == token.line_number: 3242da489cd246702bee5938545b18a6f710ed214bcJamie Gennis if token.metadata.context.type != Context.FOR_GROUP_BLOCK: 3252da489cd246702bee5938545b18a6f710ed214bcJamie Gennis # TODO(robbyw): Error about no multi-statement lines. 3262da489cd246702bee5938545b18a6f710ed214bcJamie Gennis pass 3272da489cd246702bee5938545b18a6f710ed214bcJamie Gennis 3282da489cd246702bee5938545b18a6f710ed214bcJamie Gennis elif token.next.type not in ( 3292da489cd246702bee5938545b18a6f710ed214bcJamie Gennis Type.WHITESPACE, Type.SEMICOLON, Type.END_PAREN): 3302da489cd246702bee5938545b18a6f710ed214bcJamie Gennis self._HandleError(errors.MISSING_SPACE, 3312da489cd246702bee5938545b18a6f710ed214bcJamie Gennis 'Missing space after ";" in for statement', 3322da489cd246702bee5938545b18a6f710ed214bcJamie Gennis token.next, 3332da489cd246702bee5938545b18a6f710ed214bcJamie Gennis Position.AtBeginning()) 3342da489cd246702bee5938545b18a6f710ed214bcJamie Gennis 3352da489cd246702bee5938545b18a6f710ed214bcJamie Gennis last_code = token.metadata.last_code 3362da489cd246702bee5938545b18a6f710ed214bcJamie Gennis if last_code and last_code.type == Type.SEMICOLON: 3372da489cd246702bee5938545b18a6f710ed214bcJamie Gennis # Allow a single double semi colon in for loops for cases like: 3382da489cd246702bee5938545b18a6f710ed214bcJamie Gennis # for (;;) { }. 3392da489cd246702bee5938545b18a6f710ed214bcJamie Gennis # NOTE(user): This is not a perfect check, and will not throw an error 3402da489cd246702bee5938545b18a6f710ed214bcJamie Gennis # for cases like: for (var i = 0;; i < n; i++) {}, but then your code 3412da489cd246702bee5938545b18a6f710ed214bcJamie Gennis # probably won't work either. 3422da489cd246702bee5938545b18a6f710ed214bcJamie Gennis for_token = tokenutil.CustomSearch(last_code, 3432da489cd246702bee5938545b18a6f710ed214bcJamie Gennis lambda token: token.type == Type.KEYWORD and token.string == 'for', 3442da489cd246702bee5938545b18a6f710ed214bcJamie Gennis end_func=lambda token: token.type == Type.SEMICOLON, 3452da489cd246702bee5938545b18a6f710ed214bcJamie Gennis distance=None, 3462da489cd246702bee5938545b18a6f710ed214bcJamie Gennis reverse=True) 3472da489cd246702bee5938545b18a6f710ed214bcJamie Gennis 3482da489cd246702bee5938545b18a6f710ed214bcJamie Gennis if not for_token: 3492da489cd246702bee5938545b18a6f710ed214bcJamie Gennis self._HandleError(errors.REDUNDANT_SEMICOLON, 'Redundant semicolon', 3502da489cd246702bee5938545b18a6f710ed214bcJamie Gennis token, Position.All(token.string)) 3512da489cd246702bee5938545b18a6f710ed214bcJamie Gennis 3522da489cd246702bee5938545b18a6f710ed214bcJamie Gennis elif type == Type.START_PAREN: 3532da489cd246702bee5938545b18a6f710ed214bcJamie Gennis if token.previous and token.previous.type == Type.KEYWORD: 3542da489cd246702bee5938545b18a6f710ed214bcJamie Gennis self._HandleError(errors.MISSING_SPACE, 'Missing space before "("', 3552da489cd246702bee5938545b18a6f710ed214bcJamie Gennis token, Position.AtBeginning()) 3562da489cd246702bee5938545b18a6f710ed214bcJamie Gennis elif token.previous and token.previous.type == Type.WHITESPACE: 3572da489cd246702bee5938545b18a6f710ed214bcJamie Gennis before_space = token.previous.previous 3582da489cd246702bee5938545b18a6f710ed214bcJamie Gennis if (before_space and before_space.line_number == token.line_number and 3592da489cd246702bee5938545b18a6f710ed214bcJamie Gennis before_space.type == Type.IDENTIFIER): 3602da489cd246702bee5938545b18a6f710ed214bcJamie Gennis self._HandleError(errors.EXTRA_SPACE, 'Extra space before "("', 3612da489cd246702bee5938545b18a6f710ed214bcJamie Gennis token.previous, Position.All(token.previous.string)) 3622da489cd246702bee5938545b18a6f710ed214bcJamie Gennis 3632da489cd246702bee5938545b18a6f710ed214bcJamie Gennis elif type == Type.START_BRACKET: 3642da489cd246702bee5938545b18a6f710ed214bcJamie Gennis self._HandleStartBracket(token, last_non_space_token) 3652da489cd246702bee5938545b18a6f710ed214bcJamie Gennis elif type in (Type.END_PAREN, Type.END_BRACKET): 3662da489cd246702bee5938545b18a6f710ed214bcJamie Gennis # Ensure there is no space before closing parentheses, except when 3672da489cd246702bee5938545b18a6f710ed214bcJamie Gennis # it's in a for statement with an omitted section, or when it's at the 3682da489cd246702bee5938545b18a6f710ed214bcJamie Gennis # beginning of a line. 3692da489cd246702bee5938545b18a6f710ed214bcJamie Gennis if (token.previous and token.previous.type == Type.WHITESPACE and 3702da489cd246702bee5938545b18a6f710ed214bcJamie Gennis not token.previous.IsFirstInLine() and 3712da489cd246702bee5938545b18a6f710ed214bcJamie Gennis not (last_non_space_token and last_non_space_token.line_number == 3722da489cd246702bee5938545b18a6f710ed214bcJamie Gennis token.line_number and 3732da489cd246702bee5938545b18a6f710ed214bcJamie Gennis last_non_space_token.type == Type.SEMICOLON)): 3742da489cd246702bee5938545b18a6f710ed214bcJamie Gennis self._HandleError(errors.EXTRA_SPACE, 'Extra space before "%s"' % 3752da489cd246702bee5938545b18a6f710ed214bcJamie Gennis token.string, token.previous, Position.All(token.previous.string)) 3762da489cd246702bee5938545b18a6f710ed214bcJamie Gennis 3772da489cd246702bee5938545b18a6f710ed214bcJamie Gennis if token.type == Type.END_BRACKET: 3782da489cd246702bee5938545b18a6f710ed214bcJamie Gennis last_code = token.metadata.last_code 3792da489cd246702bee5938545b18a6f710ed214bcJamie Gennis if last_code.IsOperator(','): 3802da489cd246702bee5938545b18a6f710ed214bcJamie Gennis self._HandleError(errors.COMMA_AT_END_OF_LITERAL, 3812da489cd246702bee5938545b18a6f710ed214bcJamie Gennis 'Illegal comma at end of array literal', last_code, 3822da489cd246702bee5938545b18a6f710ed214bcJamie Gennis Position.All(last_code.string)) 3832da489cd246702bee5938545b18a6f710ed214bcJamie Gennis 3842da489cd246702bee5938545b18a6f710ed214bcJamie Gennis elif type == Type.WHITESPACE: 3852da489cd246702bee5938545b18a6f710ed214bcJamie Gennis if self.ILLEGAL_TAB.search(token.string): 3862da489cd246702bee5938545b18a6f710ed214bcJamie Gennis if token.IsFirstInLine(): 3872da489cd246702bee5938545b18a6f710ed214bcJamie Gennis if token.next: 3882da489cd246702bee5938545b18a6f710ed214bcJamie Gennis self._HandleError(errors.ILLEGAL_TAB, 3892da489cd246702bee5938545b18a6f710ed214bcJamie Gennis 'Illegal tab in whitespace before "%s"' % token.next.string, 3902da489cd246702bee5938545b18a6f710ed214bcJamie Gennis token, Position.All(token.string)) 3912da489cd246702bee5938545b18a6f710ed214bcJamie Gennis else: 3922da489cd246702bee5938545b18a6f710ed214bcJamie Gennis self._HandleError(errors.ILLEGAL_TAB, 3932da489cd246702bee5938545b18a6f710ed214bcJamie Gennis 'Illegal tab in whitespace', 3942da489cd246702bee5938545b18a6f710ed214bcJamie Gennis token, Position.All(token.string)) 3952da489cd246702bee5938545b18a6f710ed214bcJamie Gennis else: 3962da489cd246702bee5938545b18a6f710ed214bcJamie Gennis self._HandleError(errors.ILLEGAL_TAB, 3972da489cd246702bee5938545b18a6f710ed214bcJamie Gennis 'Illegal tab in whitespace after "%s"' % token.previous.string, 3982da489cd246702bee5938545b18a6f710ed214bcJamie Gennis token, Position.All(token.string)) 3992da489cd246702bee5938545b18a6f710ed214bcJamie Gennis 4002da489cd246702bee5938545b18a6f710ed214bcJamie Gennis # Check whitespace length if it's not the first token of the line and 4012da489cd246702bee5938545b18a6f710ed214bcJamie Gennis # if it's not immediately before a comment. 4022da489cd246702bee5938545b18a6f710ed214bcJamie Gennis if last_in_line: 4032da489cd246702bee5938545b18a6f710ed214bcJamie Gennis # Check for extra whitespace at the end of a line. 4042da489cd246702bee5938545b18a6f710ed214bcJamie Gennis self._HandleError(errors.EXTRA_SPACE, 'Extra space at end of line', 4052da489cd246702bee5938545b18a6f710ed214bcJamie Gennis token, Position.All(token.string)) 4062da489cd246702bee5938545b18a6f710ed214bcJamie Gennis elif not first_in_line and not token.next.IsComment(): 4072da489cd246702bee5938545b18a6f710ed214bcJamie Gennis if token.length > 1: 4082da489cd246702bee5938545b18a6f710ed214bcJamie Gennis self._HandleError(errors.EXTRA_SPACE, 'Extra space after "%s"' % 4092da489cd246702bee5938545b18a6f710ed214bcJamie Gennis token.previous.string, token, 4102da489cd246702bee5938545b18a6f710ed214bcJamie Gennis Position(1, len(token.string) - 1)) 4112da489cd246702bee5938545b18a6f710ed214bcJamie Gennis 4122da489cd246702bee5938545b18a6f710ed214bcJamie Gennis elif type == Type.OPERATOR: 4132da489cd246702bee5938545b18a6f710ed214bcJamie Gennis last_code = token.metadata.last_code 4142da489cd246702bee5938545b18a6f710ed214bcJamie Gennis 4152da489cd246702bee5938545b18a6f710ed214bcJamie Gennis if not self._ExpectSpaceBeforeOperator(token): 4162da489cd246702bee5938545b18a6f710ed214bcJamie Gennis if (token.previous and token.previous.type == Type.WHITESPACE and 4172da489cd246702bee5938545b18a6f710ed214bcJamie Gennis last_code and last_code.type in (Type.NORMAL, Type.IDENTIFIER)): 4182da489cd246702bee5938545b18a6f710ed214bcJamie Gennis self._HandleError(errors.EXTRA_SPACE, 4192da489cd246702bee5938545b18a6f710ed214bcJamie Gennis 'Extra space before "%s"' % token.string, token.previous, 4202da489cd246702bee5938545b18a6f710ed214bcJamie Gennis Position.All(token.previous.string)) 4212da489cd246702bee5938545b18a6f710ed214bcJamie Gennis 4222da489cd246702bee5938545b18a6f710ed214bcJamie Gennis elif (token.previous and 4232da489cd246702bee5938545b18a6f710ed214bcJamie Gennis not token.previous.IsComment() and 4242da489cd246702bee5938545b18a6f710ed214bcJamie Gennis token.previous.type in Type.EXPRESSION_ENDER_TYPES): 4252da489cd246702bee5938545b18a6f710ed214bcJamie Gennis self._HandleError(errors.MISSING_SPACE, 4262da489cd246702bee5938545b18a6f710ed214bcJamie Gennis 'Missing space before "%s"' % token.string, token, 4272da489cd246702bee5938545b18a6f710ed214bcJamie Gennis Position.AtBeginning()) 4282da489cd246702bee5938545b18a6f710ed214bcJamie Gennis 4292da489cd246702bee5938545b18a6f710ed214bcJamie Gennis # Check that binary operators are not used to start lines. 4302da489cd246702bee5938545b18a6f710ed214bcJamie Gennis if ((not last_code or last_code.line_number != token.line_number) and 4312da489cd246702bee5938545b18a6f710ed214bcJamie Gennis not token.metadata.IsUnaryOperator()): 4322da489cd246702bee5938545b18a6f710ed214bcJamie Gennis self._HandleError(errors.LINE_STARTS_WITH_OPERATOR, 4332da489cd246702bee5938545b18a6f710ed214bcJamie Gennis 'Binary operator should go on previous line "%s"' % token.string, 4342da489cd246702bee5938545b18a6f710ed214bcJamie Gennis token) 4352da489cd246702bee5938545b18a6f710ed214bcJamie Gennis 4362da489cd246702bee5938545b18a6f710ed214bcJamie Gennis elif type == Type.DOC_FLAG: 4372da489cd246702bee5938545b18a6f710ed214bcJamie Gennis flag = token.attached_object 4382da489cd246702bee5938545b18a6f710ed214bcJamie Gennis 4392da489cd246702bee5938545b18a6f710ed214bcJamie Gennis if flag.flag_type == 'bug': 4402da489cd246702bee5938545b18a6f710ed214bcJamie Gennis # TODO(robbyw): Check for exactly 1 space on the left. 4412da489cd246702bee5938545b18a6f710ed214bcJamie Gennis string = token.next.string.lstrip() 4422da489cd246702bee5938545b18a6f710ed214bcJamie Gennis string = string.split(' ', 1)[0] 4432da489cd246702bee5938545b18a6f710ed214bcJamie Gennis 4442da489cd246702bee5938545b18a6f710ed214bcJamie Gennis if not string.isdigit(): 4452da489cd246702bee5938545b18a6f710ed214bcJamie Gennis self._HandleError(errors.NO_BUG_NUMBER_AFTER_BUG_TAG, 4462da489cd246702bee5938545b18a6f710ed214bcJamie Gennis '@bug should be followed by a bug number', token) 4472da489cd246702bee5938545b18a6f710ed214bcJamie Gennis 4482da489cd246702bee5938545b18a6f710ed214bcJamie Gennis elif flag.flag_type == 'suppress': 4492da489cd246702bee5938545b18a6f710ed214bcJamie Gennis if flag.type is None: 4502da489cd246702bee5938545b18a6f710ed214bcJamie Gennis # A syntactically invalid suppress tag will get tokenized as a normal 4512da489cd246702bee5938545b18a6f710ed214bcJamie Gennis # flag, indicating an error. 4522da489cd246702bee5938545b18a6f710ed214bcJamie Gennis self._HandleError(errors.INCORRECT_SUPPRESS_SYNTAX, 4532da489cd246702bee5938545b18a6f710ed214bcJamie Gennis 'Invalid suppress syntax: should be @suppress {errortype}. ' 4542da489cd246702bee5938545b18a6f710ed214bcJamie Gennis 'Spaces matter.', token) 4552da489cd246702bee5938545b18a6f710ed214bcJamie Gennis else: 4562da489cd246702bee5938545b18a6f710ed214bcJamie Gennis for suppress_type in flag.type.split('|'): 4572da489cd246702bee5938545b18a6f710ed214bcJamie Gennis if suppress_type not in state.GetDocFlag().SUPPRESS_TYPES: 4582da489cd246702bee5938545b18a6f710ed214bcJamie Gennis self._HandleError(errors.INVALID_SUPPRESS_TYPE, 4592da489cd246702bee5938545b18a6f710ed214bcJamie Gennis 'Invalid suppression type: %s' % suppress_type, 4602da489cd246702bee5938545b18a6f710ed214bcJamie Gennis token) 4612da489cd246702bee5938545b18a6f710ed214bcJamie Gennis 4622da489cd246702bee5938545b18a6f710ed214bcJamie Gennis elif (error_check.ShouldCheck(Rule.WELL_FORMED_AUTHOR) and 4632da489cd246702bee5938545b18a6f710ed214bcJamie Gennis flag.flag_type == 'author'): 4642da489cd246702bee5938545b18a6f710ed214bcJamie Gennis # TODO(user): In non strict mode check the author tag for as much as 4652da489cd246702bee5938545b18a6f710ed214bcJamie Gennis # it exists, though the full form checked below isn't required. 4662da489cd246702bee5938545b18a6f710ed214bcJamie Gennis string = token.next.string 4672da489cd246702bee5938545b18a6f710ed214bcJamie Gennis result = self.AUTHOR_SPEC.match(string) 4682da489cd246702bee5938545b18a6f710ed214bcJamie Gennis if not result: 4692da489cd246702bee5938545b18a6f710ed214bcJamie Gennis self._HandleError(errors.INVALID_AUTHOR_TAG_DESCRIPTION, 4702da489cd246702bee5938545b18a6f710ed214bcJamie Gennis 'Author tag line should be of the form: ' 4712da489cd246702bee5938545b18a6f710ed214bcJamie Gennis '@author foo@somewhere.com (Your Name)', 4722da489cd246702bee5938545b18a6f710ed214bcJamie Gennis token.next) 4732da489cd246702bee5938545b18a6f710ed214bcJamie Gennis else: 4742da489cd246702bee5938545b18a6f710ed214bcJamie Gennis # Check spacing between email address and name. Do this before 4752da489cd246702bee5938545b18a6f710ed214bcJamie Gennis # checking earlier spacing so positions are easier to calculate for 4762da489cd246702bee5938545b18a6f710ed214bcJamie Gennis # autofixing. 4772da489cd246702bee5938545b18a6f710ed214bcJamie Gennis num_spaces = len(result.group(2)) 4782da489cd246702bee5938545b18a6f710ed214bcJamie Gennis if num_spaces < 1: 4792da489cd246702bee5938545b18a6f710ed214bcJamie Gennis self._HandleError(errors.MISSING_SPACE, 4802da489cd246702bee5938545b18a6f710ed214bcJamie Gennis 'Missing space after email address', 4812da489cd246702bee5938545b18a6f710ed214bcJamie Gennis token.next, Position(result.start(2), 0)) 4822da489cd246702bee5938545b18a6f710ed214bcJamie Gennis elif num_spaces > 1: 4832da489cd246702bee5938545b18a6f710ed214bcJamie Gennis self._HandleError(errors.EXTRA_SPACE, 4842da489cd246702bee5938545b18a6f710ed214bcJamie Gennis 'Extra space after email address', 4852da489cd246702bee5938545b18a6f710ed214bcJamie Gennis token.next, 4862da489cd246702bee5938545b18a6f710ed214bcJamie Gennis Position(result.start(2) + 1, num_spaces - 1)) 4872da489cd246702bee5938545b18a6f710ed214bcJamie Gennis 4882da489cd246702bee5938545b18a6f710ed214bcJamie Gennis # Check for extra spaces before email address. Can't be too few, if 4892da489cd246702bee5938545b18a6f710ed214bcJamie Gennis # not at least one we wouldn't match @author tag. 4902da489cd246702bee5938545b18a6f710ed214bcJamie Gennis num_spaces = len(result.group(1)) 4912da489cd246702bee5938545b18a6f710ed214bcJamie Gennis if num_spaces > 1: 4922da489cd246702bee5938545b18a6f710ed214bcJamie Gennis self._HandleError(errors.EXTRA_SPACE, 4932da489cd246702bee5938545b18a6f710ed214bcJamie Gennis 'Extra space before email address', 4942da489cd246702bee5938545b18a6f710ed214bcJamie Gennis token.next, Position(1, num_spaces - 1)) 4952da489cd246702bee5938545b18a6f710ed214bcJamie Gennis 4962da489cd246702bee5938545b18a6f710ed214bcJamie Gennis elif (flag.flag_type in state.GetDocFlag().HAS_DESCRIPTION and 4972da489cd246702bee5938545b18a6f710ed214bcJamie Gennis not self._limited_doc_checks): 4982da489cd246702bee5938545b18a6f710ed214bcJamie Gennis if flag.flag_type == 'param': 4992da489cd246702bee5938545b18a6f710ed214bcJamie Gennis if flag.name is None: 5002da489cd246702bee5938545b18a6f710ed214bcJamie Gennis self._HandleError(errors.MISSING_JSDOC_PARAM_NAME, 5012da489cd246702bee5938545b18a6f710ed214bcJamie Gennis 'Missing name in @param tag', token) 5022da489cd246702bee5938545b18a6f710ed214bcJamie Gennis 5032da489cd246702bee5938545b18a6f710ed214bcJamie Gennis if not flag.description or flag.description is None: 5042da489cd246702bee5938545b18a6f710ed214bcJamie Gennis flag_name = token.type 5052da489cd246702bee5938545b18a6f710ed214bcJamie Gennis if 'name' in token.values: 5062da489cd246702bee5938545b18a6f710ed214bcJamie Gennis flag_name = '@' + token.values['name'] 5072da489cd246702bee5938545b18a6f710ed214bcJamie Gennis self._HandleError(errors.MISSING_JSDOC_TAG_DESCRIPTION, 5082da489cd246702bee5938545b18a6f710ed214bcJamie Gennis 'Missing description in %s tag' % flag_name, token) 5092da489cd246702bee5938545b18a6f710ed214bcJamie Gennis else: 5102da489cd246702bee5938545b18a6f710ed214bcJamie Gennis self._CheckForMissingSpaceBeforeToken(flag.description_start_token) 5112da489cd246702bee5938545b18a6f710ed214bcJamie Gennis 5122da489cd246702bee5938545b18a6f710ed214bcJamie Gennis # We want punctuation to be inside of any tags ending a description, 5132da489cd246702bee5938545b18a6f710ed214bcJamie Gennis # so strip tags before checking description. See bug 1127192. Note 5142da489cd246702bee5938545b18a6f710ed214bcJamie Gennis # that depending on how lines break, the real description end token 5152da489cd246702bee5938545b18a6f710ed214bcJamie Gennis # may consist only of stripped html and the effective end token can 5162da489cd246702bee5938545b18a6f710ed214bcJamie Gennis # be different. 5172da489cd246702bee5938545b18a6f710ed214bcJamie Gennis end_token = flag.description_end_token 5182da489cd246702bee5938545b18a6f710ed214bcJamie Gennis end_string = htmlutil.StripTags(end_token.string).strip() 5192da489cd246702bee5938545b18a6f710ed214bcJamie Gennis while (end_string == '' and not 5202da489cd246702bee5938545b18a6f710ed214bcJamie Gennis end_token.type in Type.FLAG_ENDING_TYPES): 5212da489cd246702bee5938545b18a6f710ed214bcJamie Gennis end_token = end_token.previous 5222da489cd246702bee5938545b18a6f710ed214bcJamie Gennis if end_token.type in Type.FLAG_DESCRIPTION_TYPES: 5232da489cd246702bee5938545b18a6f710ed214bcJamie Gennis end_string = htmlutil.StripTags(end_token.string).rstrip() 5242da489cd246702bee5938545b18a6f710ed214bcJamie Gennis 5252da489cd246702bee5938545b18a6f710ed214bcJamie Gennis if not (end_string.endswith('.') or end_string.endswith('?') or 5262da489cd246702bee5938545b18a6f710ed214bcJamie Gennis end_string.endswith('!')): 5272da489cd246702bee5938545b18a6f710ed214bcJamie Gennis # Find the position for the missing punctuation, inside of any html 5282da489cd246702bee5938545b18a6f710ed214bcJamie Gennis # tags. 5292da489cd246702bee5938545b18a6f710ed214bcJamie Gennis desc_str = end_token.string.rstrip() 5302da489cd246702bee5938545b18a6f710ed214bcJamie Gennis while desc_str.endswith('>'): 5312da489cd246702bee5938545b18a6f710ed214bcJamie Gennis start_tag_index = desc_str.rfind('<') 5322da489cd246702bee5938545b18a6f710ed214bcJamie Gennis if start_tag_index < 0: 5332da489cd246702bee5938545b18a6f710ed214bcJamie Gennis break 5342da489cd246702bee5938545b18a6f710ed214bcJamie Gennis desc_str = desc_str[:start_tag_index].rstrip() 5352da489cd246702bee5938545b18a6f710ed214bcJamie Gennis end_position = Position(len(desc_str), 0) 5362da489cd246702bee5938545b18a6f710ed214bcJamie Gennis 5372da489cd246702bee5938545b18a6f710ed214bcJamie Gennis self._HandleError( 5382da489cd246702bee5938545b18a6f710ed214bcJamie Gennis errors.JSDOC_TAG_DESCRIPTION_ENDS_WITH_INVALID_CHARACTER, 5392da489cd246702bee5938545b18a6f710ed214bcJamie Gennis ('%s descriptions must end with valid punctuation such as a ' 5402da489cd246702bee5938545b18a6f710ed214bcJamie Gennis 'period.' % token.string), 5412da489cd246702bee5938545b18a6f710ed214bcJamie Gennis end_token, end_position) 5422da489cd246702bee5938545b18a6f710ed214bcJamie Gennis 5432da489cd246702bee5938545b18a6f710ed214bcJamie Gennis if flag.flag_type in state.GetDocFlag().HAS_TYPE: 5442da489cd246702bee5938545b18a6f710ed214bcJamie Gennis if flag.type_start_token is not None: 5452da489cd246702bee5938545b18a6f710ed214bcJamie Gennis self._CheckForMissingSpaceBeforeToken( 5462da489cd246702bee5938545b18a6f710ed214bcJamie Gennis token.attached_object.type_start_token) 5472da489cd246702bee5938545b18a6f710ed214bcJamie Gennis 5482da489cd246702bee5938545b18a6f710ed214bcJamie Gennis if flag.type and flag.type != '' and not flag.type.isspace(): 5492da489cd246702bee5938545b18a6f710ed214bcJamie Gennis self._CheckJsDocType(token) 5502da489cd246702bee5938545b18a6f710ed214bcJamie Gennis 5512da489cd246702bee5938545b18a6f710ed214bcJamie Gennis if type in (Type.DOC_FLAG, Type.DOC_INLINE_FLAG): 5522da489cd246702bee5938545b18a6f710ed214bcJamie Gennis if (token.values['name'] not in state.GetDocFlag().LEGAL_DOC and 5532da489cd246702bee5938545b18a6f710ed214bcJamie Gennis token.values['name'] not in FLAGS.custom_jsdoc_tags): 5542da489cd246702bee5938545b18a6f710ed214bcJamie Gennis self._HandleError(errors.INVALID_JSDOC_TAG, 5552da489cd246702bee5938545b18a6f710ed214bcJamie Gennis 'Invalid JsDoc tag: %s' % token.values['name'], token) 5562da489cd246702bee5938545b18a6f710ed214bcJamie Gennis 5572da489cd246702bee5938545b18a6f710ed214bcJamie Gennis if (error_check.ShouldCheck(Rule.NO_BRACES_AROUND_INHERIT_DOC) and 5582da489cd246702bee5938545b18a6f710ed214bcJamie Gennis token.values['name'] == 'inheritDoc' and 5592da489cd246702bee5938545b18a6f710ed214bcJamie Gennis type == Type.DOC_INLINE_FLAG): 5602da489cd246702bee5938545b18a6f710ed214bcJamie Gennis self._HandleError(errors.UNNECESSARY_BRACES_AROUND_INHERIT_DOC, 5612da489cd246702bee5938545b18a6f710ed214bcJamie Gennis 'Unnecessary braces around @inheritDoc', 5622da489cd246702bee5938545b18a6f710ed214bcJamie Gennis token) 5632da489cd246702bee5938545b18a6f710ed214bcJamie Gennis 5642da489cd246702bee5938545b18a6f710ed214bcJamie Gennis elif type == Type.SIMPLE_LVALUE: 5652da489cd246702bee5938545b18a6f710ed214bcJamie Gennis identifier = token.values['identifier'] 5662da489cd246702bee5938545b18a6f710ed214bcJamie Gennis 5672da489cd246702bee5938545b18a6f710ed214bcJamie Gennis if ((not state.InFunction() or state.InConstructor()) and 5682da489cd246702bee5938545b18a6f710ed214bcJamie Gennis not state.InParentheses() and not state.InObjectLiteralDescendant()): 5692da489cd246702bee5938545b18a6f710ed214bcJamie Gennis jsdoc = state.GetDocComment() 5702da489cd246702bee5938545b18a6f710ed214bcJamie Gennis if not state.HasDocComment(identifier): 5712da489cd246702bee5938545b18a6f710ed214bcJamie Gennis # Only test for documentation on identifiers with .s in them to 5722da489cd246702bee5938545b18a6f710ed214bcJamie Gennis # avoid checking things like simple variables. We don't require 5732da489cd246702bee5938545b18a6f710ed214bcJamie Gennis # documenting assignments to .prototype itself (bug 1880803). 5742da489cd246702bee5938545b18a6f710ed214bcJamie Gennis if (not state.InConstructor() and 5752da489cd246702bee5938545b18a6f710ed214bcJamie Gennis identifier.find('.') != -1 and not 5762da489cd246702bee5938545b18a6f710ed214bcJamie Gennis identifier.endswith('.prototype') and not 5772da489cd246702bee5938545b18a6f710ed214bcJamie Gennis self._limited_doc_checks): 5782da489cd246702bee5938545b18a6f710ed214bcJamie Gennis comment = state.GetLastComment() 5792da489cd246702bee5938545b18a6f710ed214bcJamie Gennis if not (comment and comment.lower().count('jsdoc inherited')): 5802da489cd246702bee5938545b18a6f710ed214bcJamie Gennis self._HandleError(errors.MISSING_MEMBER_DOCUMENTATION, 5812da489cd246702bee5938545b18a6f710ed214bcJamie Gennis "No docs found for member '%s'" % identifier, 5822da489cd246702bee5938545b18a6f710ed214bcJamie Gennis token); 5832da489cd246702bee5938545b18a6f710ed214bcJamie Gennis elif jsdoc and (not state.InConstructor() or 5842da489cd246702bee5938545b18a6f710ed214bcJamie Gennis identifier.startswith('this.')): 5852da489cd246702bee5938545b18a6f710ed214bcJamie Gennis # We are at the top level and the function/member is documented. 5862da489cd246702bee5938545b18a6f710ed214bcJamie Gennis if identifier.endswith('_') and not identifier.endswith('__'): 5872da489cd246702bee5938545b18a6f710ed214bcJamie Gennis # Can have a private class which inherits documentation from a 5882da489cd246702bee5938545b18a6f710ed214bcJamie Gennis # public superclass. 5892da489cd246702bee5938545b18a6f710ed214bcJamie Gennis # 5902da489cd246702bee5938545b18a6f710ed214bcJamie Gennis # @inheritDoc is deprecated in favor of using @override, and they 5912da489cd246702bee5938545b18a6f710ed214bcJamie Gennis if (jsdoc.HasFlag('override') and not jsdoc.HasFlag('constructor') 5922da489cd246702bee5938545b18a6f710ed214bcJamie Gennis and not ('accessControls' in jsdoc.suppressions)): 5932da489cd246702bee5938545b18a6f710ed214bcJamie Gennis self._HandleError(errors.INVALID_OVERRIDE_PRIVATE, 5942da489cd246702bee5938545b18a6f710ed214bcJamie Gennis '%s should not override a private member.' % identifier, 5952da489cd246702bee5938545b18a6f710ed214bcJamie Gennis jsdoc.GetFlag('override').flag_token) 5962da489cd246702bee5938545b18a6f710ed214bcJamie Gennis if (jsdoc.HasFlag('inheritDoc') and not jsdoc.HasFlag('constructor') 5972da489cd246702bee5938545b18a6f710ed214bcJamie Gennis and not ('accessControls' in jsdoc.suppressions)): 5982da489cd246702bee5938545b18a6f710ed214bcJamie Gennis self._HandleError(errors.INVALID_INHERIT_DOC_PRIVATE, 5992da489cd246702bee5938545b18a6f710ed214bcJamie Gennis '%s should not inherit from a private member.' % identifier, 6002da489cd246702bee5938545b18a6f710ed214bcJamie Gennis jsdoc.GetFlag('inheritDoc').flag_token) 6012da489cd246702bee5938545b18a6f710ed214bcJamie Gennis if (not jsdoc.HasFlag('private') and 6022da489cd246702bee5938545b18a6f710ed214bcJamie Gennis not ('underscore' in jsdoc.suppressions) and not 6032da489cd246702bee5938545b18a6f710ed214bcJamie Gennis ((jsdoc.HasFlag('inheritDoc') or jsdoc.HasFlag('override')) and 6042da489cd246702bee5938545b18a6f710ed214bcJamie Gennis ('accessControls' in jsdoc.suppressions))): 6052da489cd246702bee5938545b18a6f710ed214bcJamie Gennis self._HandleError(errors.MISSING_PRIVATE, 6062da489cd246702bee5938545b18a6f710ed214bcJamie Gennis 'Member "%s" must have @private JsDoc.' % 6072da489cd246702bee5938545b18a6f710ed214bcJamie Gennis identifier, token) 6082da489cd246702bee5938545b18a6f710ed214bcJamie Gennis if jsdoc.HasFlag('private') and 'underscore' in jsdoc.suppressions: 6092da489cd246702bee5938545b18a6f710ed214bcJamie Gennis self._HandleError(errors.UNNECESSARY_SUPPRESS, 6102da489cd246702bee5938545b18a6f710ed214bcJamie Gennis '@suppress {underscore} is not necessary with @private', 6112da489cd246702bee5938545b18a6f710ed214bcJamie Gennis jsdoc.suppressions['underscore']) 6122da489cd246702bee5938545b18a6f710ed214bcJamie Gennis elif (jsdoc.HasFlag('private') and 6132da489cd246702bee5938545b18a6f710ed214bcJamie Gennis not self.InExplicitlyTypedLanguage()): 6142da489cd246702bee5938545b18a6f710ed214bcJamie Gennis # It is convention to hide public fields in some ECMA 6152da489cd246702bee5938545b18a6f710ed214bcJamie Gennis # implementations from documentation using the @private tag. 6162da489cd246702bee5938545b18a6f710ed214bcJamie Gennis self._HandleError(errors.EXTRA_PRIVATE, 6172da489cd246702bee5938545b18a6f710ed214bcJamie Gennis 'Member "%s" must not have @private JsDoc' % 6182da489cd246702bee5938545b18a6f710ed214bcJamie Gennis identifier, token) 6192da489cd246702bee5938545b18a6f710ed214bcJamie Gennis 6202da489cd246702bee5938545b18a6f710ed214bcJamie Gennis # These flags are only legal on localizable message definitions; 6212da489cd246702bee5938545b18a6f710ed214bcJamie Gennis # such variables always begin with the prefix MSG_. 6222da489cd246702bee5938545b18a6f710ed214bcJamie Gennis for f in ('desc', 'hidden', 'meaning'): 6232da489cd246702bee5938545b18a6f710ed214bcJamie Gennis if (jsdoc.HasFlag(f) 6242da489cd246702bee5938545b18a6f710ed214bcJamie Gennis and not identifier.startswith('MSG_') 6252da489cd246702bee5938545b18a6f710ed214bcJamie Gennis and identifier.find('.MSG_') == -1): 6262da489cd246702bee5938545b18a6f710ed214bcJamie Gennis self._HandleError(errors.INVALID_USE_OF_DESC_TAG, 6272da489cd246702bee5938545b18a6f710ed214bcJamie Gennis 'Member "%s" should not have @%s JsDoc' % (identifier, f), 6282da489cd246702bee5938545b18a6f710ed214bcJamie Gennis token) 6292da489cd246702bee5938545b18a6f710ed214bcJamie Gennis 6302da489cd246702bee5938545b18a6f710ed214bcJamie Gennis # Check for illegaly assigning live objects as prototype property values. 6312da489cd246702bee5938545b18a6f710ed214bcJamie Gennis index = identifier.find('.prototype.') 6322da489cd246702bee5938545b18a6f710ed214bcJamie Gennis # Ignore anything with additional .s after the prototype. 6332da489cd246702bee5938545b18a6f710ed214bcJamie Gennis if index != -1 and identifier.find('.', index + 11) == -1: 6342da489cd246702bee5938545b18a6f710ed214bcJamie Gennis equal_operator = tokenutil.SearchExcept(token, Type.NON_CODE_TYPES) 6352da489cd246702bee5938545b18a6f710ed214bcJamie Gennis next_code = tokenutil.SearchExcept(equal_operator, Type.NON_CODE_TYPES) 6362da489cd246702bee5938545b18a6f710ed214bcJamie Gennis if next_code and ( 6372da489cd246702bee5938545b18a6f710ed214bcJamie Gennis next_code.type in (Type.START_BRACKET, Type.START_BLOCK) or 6382da489cd246702bee5938545b18a6f710ed214bcJamie Gennis next_code.IsOperator('new')): 6392da489cd246702bee5938545b18a6f710ed214bcJamie Gennis self._HandleError(errors.ILLEGAL_PROTOTYPE_MEMBER_VALUE, 6402da489cd246702bee5938545b18a6f710ed214bcJamie Gennis 'Member %s cannot have a non-primitive value' % identifier, 6412da489cd246702bee5938545b18a6f710ed214bcJamie Gennis token) 6422da489cd246702bee5938545b18a6f710ed214bcJamie Gennis 6432da489cd246702bee5938545b18a6f710ed214bcJamie Gennis elif type == Type.END_PARAMETERS: 6442da489cd246702bee5938545b18a6f710ed214bcJamie Gennis # Find extra space at the end of parameter lists. We check the token 6452da489cd246702bee5938545b18a6f710ed214bcJamie Gennis # prior to the current one when it is a closing paren. 6462da489cd246702bee5938545b18a6f710ed214bcJamie Gennis if (token.previous and token.previous.type == Type.PARAMETERS 6472da489cd246702bee5938545b18a6f710ed214bcJamie Gennis and self.ENDS_WITH_SPACE.search(token.previous.string)): 6482da489cd246702bee5938545b18a6f710ed214bcJamie Gennis self._HandleError(errors.EXTRA_SPACE, 'Extra space before ")"', 6492da489cd246702bee5938545b18a6f710ed214bcJamie Gennis token.previous) 6502da489cd246702bee5938545b18a6f710ed214bcJamie Gennis 6512da489cd246702bee5938545b18a6f710ed214bcJamie Gennis jsdoc = state.GetDocComment() 6522da489cd246702bee5938545b18a6f710ed214bcJamie Gennis if state.GetFunction().is_interface: 6532da489cd246702bee5938545b18a6f710ed214bcJamie Gennis if token.previous and token.previous.type == Type.PARAMETERS: 6542da489cd246702bee5938545b18a6f710ed214bcJamie Gennis self._HandleError(errors.INTERFACE_CONSTRUCTOR_CANNOT_HAVE_PARAMS, 6552da489cd246702bee5938545b18a6f710ed214bcJamie Gennis 'Interface constructor cannot have parameters', 6562da489cd246702bee5938545b18a6f710ed214bcJamie Gennis token.previous) 6572da489cd246702bee5938545b18a6f710ed214bcJamie Gennis elif (state.InTopLevel() and jsdoc and not jsdoc.HasFlag('see') 6582da489cd246702bee5938545b18a6f710ed214bcJamie Gennis and not jsdoc.InheritsDocumentation() 6592da489cd246702bee5938545b18a6f710ed214bcJamie Gennis and not state.InObjectLiteralDescendant() and not 6602da489cd246702bee5938545b18a6f710ed214bcJamie Gennis jsdoc.IsInvalidated()): 6612da489cd246702bee5938545b18a6f710ed214bcJamie Gennis distance, edit = jsdoc.CompareParameters(state.GetParams()) 6622da489cd246702bee5938545b18a6f710ed214bcJamie Gennis if distance: 6632da489cd246702bee5938545b18a6f710ed214bcJamie Gennis params_iter = iter(state.GetParams()) 6642da489cd246702bee5938545b18a6f710ed214bcJamie Gennis docs_iter = iter(jsdoc.ordered_params) 6652da489cd246702bee5938545b18a6f710ed214bcJamie Gennis 6662da489cd246702bee5938545b18a6f710ed214bcJamie Gennis for op in edit: 6672da489cd246702bee5938545b18a6f710ed214bcJamie Gennis if op == 'I': 6682da489cd246702bee5938545b18a6f710ed214bcJamie Gennis # Insertion. 6692da489cd246702bee5938545b18a6f710ed214bcJamie Gennis # Parsing doc comments is the same for all languages 6702da489cd246702bee5938545b18a6f710ed214bcJamie Gennis # but some languages care about parameters that don't have 6712da489cd246702bee5938545b18a6f710ed214bcJamie Gennis # doc comments and some languages don't care. 6722da489cd246702bee5938545b18a6f710ed214bcJamie Gennis # Languages that don't allow variables to by typed such as 6732da489cd246702bee5938545b18a6f710ed214bcJamie Gennis # JavaScript care but languages such as ActionScript or Java 6742da489cd246702bee5938545b18a6f710ed214bcJamie Gennis # that allow variables to be typed don't care. 6752da489cd246702bee5938545b18a6f710ed214bcJamie Gennis if not self._limited_doc_checks: 6762da489cd246702bee5938545b18a6f710ed214bcJamie Gennis self.HandleMissingParameterDoc(token, params_iter.next()) 6772da489cd246702bee5938545b18a6f710ed214bcJamie Gennis 6782da489cd246702bee5938545b18a6f710ed214bcJamie Gennis elif op == 'D': 6792da489cd246702bee5938545b18a6f710ed214bcJamie Gennis # Deletion 6802da489cd246702bee5938545b18a6f710ed214bcJamie Gennis self._HandleError(errors.EXTRA_PARAMETER_DOCUMENTATION, 6812da489cd246702bee5938545b18a6f710ed214bcJamie Gennis 'Found docs for non-existing parameter: "%s"' % 6822da489cd246702bee5938545b18a6f710ed214bcJamie Gennis docs_iter.next(), token) 6832da489cd246702bee5938545b18a6f710ed214bcJamie Gennis elif op == 'S': 6842da489cd246702bee5938545b18a6f710ed214bcJamie Gennis # Substitution 6852da489cd246702bee5938545b18a6f710ed214bcJamie Gennis if not self._limited_doc_checks: 6862da489cd246702bee5938545b18a6f710ed214bcJamie Gennis self._HandleError(errors.WRONG_PARAMETER_DOCUMENTATION, 6872da489cd246702bee5938545b18a6f710ed214bcJamie Gennis 'Parameter mismatch: got "%s", expected "%s"' % 6882da489cd246702bee5938545b18a6f710ed214bcJamie Gennis (params_iter.next(), docs_iter.next()), token) 6892da489cd246702bee5938545b18a6f710ed214bcJamie Gennis 6902da489cd246702bee5938545b18a6f710ed214bcJamie Gennis else: 6912da489cd246702bee5938545b18a6f710ed214bcJamie Gennis # Equality - just advance the iterators 6922da489cd246702bee5938545b18a6f710ed214bcJamie Gennis params_iter.next() 6932da489cd246702bee5938545b18a6f710ed214bcJamie Gennis docs_iter.next() 6942da489cd246702bee5938545b18a6f710ed214bcJamie Gennis 6952da489cd246702bee5938545b18a6f710ed214bcJamie Gennis elif type == Type.STRING_TEXT: 6962da489cd246702bee5938545b18a6f710ed214bcJamie Gennis # If this is the first token after the start of the string, but it's at 6972da489cd246702bee5938545b18a6f710ed214bcJamie Gennis # the end of a line, we know we have a multi-line string. 6982da489cd246702bee5938545b18a6f710ed214bcJamie Gennis if token.previous.type in (Type.SINGLE_QUOTE_STRING_START, 6992da489cd246702bee5938545b18a6f710ed214bcJamie Gennis Type.DOUBLE_QUOTE_STRING_START) and last_in_line: 7002da489cd246702bee5938545b18a6f710ed214bcJamie Gennis self._HandleError(errors.MULTI_LINE_STRING, 7012da489cd246702bee5938545b18a6f710ed214bcJamie Gennis 'Multi-line strings are not allowed', token) 7022da489cd246702bee5938545b18a6f710ed214bcJamie Gennis 7032da489cd246702bee5938545b18a6f710ed214bcJamie Gennis 7042da489cd246702bee5938545b18a6f710ed214bcJamie Gennis # This check is orthogonal to the ones above, and repeats some types, so 7052da489cd246702bee5938545b18a6f710ed214bcJamie Gennis # it is a plain if and not an elif. 7062da489cd246702bee5938545b18a6f710ed214bcJamie Gennis if token.type in Type.COMMENT_TYPES: 7072da489cd246702bee5938545b18a6f710ed214bcJamie Gennis if self.ILLEGAL_TAB.search(token.string): 7082da489cd246702bee5938545b18a6f710ed214bcJamie Gennis self._HandleError(errors.ILLEGAL_TAB, 7092da489cd246702bee5938545b18a6f710ed214bcJamie Gennis 'Illegal tab in comment "%s"' % token.string, token) 7102da489cd246702bee5938545b18a6f710ed214bcJamie Gennis 7112da489cd246702bee5938545b18a6f710ed214bcJamie Gennis trimmed = token.string.rstrip() 7122da489cd246702bee5938545b18a6f710ed214bcJamie Gennis if last_in_line and token.string != trimmed: 7132da489cd246702bee5938545b18a6f710ed214bcJamie Gennis # Check for extra whitespace at the end of a line. 7142da489cd246702bee5938545b18a6f710ed214bcJamie Gennis self._HandleError(errors.EXTRA_SPACE, 'Extra space at end of line', 7152da489cd246702bee5938545b18a6f710ed214bcJamie Gennis token, Position(len(trimmed), len(token.string) - len(trimmed))) 7162da489cd246702bee5938545b18a6f710ed214bcJamie Gennis 7172da489cd246702bee5938545b18a6f710ed214bcJamie Gennis # This check is also orthogonal since it is based on metadata. 7182da489cd246702bee5938545b18a6f710ed214bcJamie Gennis if token.metadata.is_implied_semicolon: 7192da489cd246702bee5938545b18a6f710ed214bcJamie Gennis self._HandleError(errors.MISSING_SEMICOLON, 7202da489cd246702bee5938545b18a6f710ed214bcJamie Gennis 'Missing semicolon at end of line', token) 7212da489cd246702bee5938545b18a6f710ed214bcJamie Gennis 7222da489cd246702bee5938545b18a6f710ed214bcJamie Gennis def _HandleStartBracket(self, token, last_non_space_token): 7232da489cd246702bee5938545b18a6f710ed214bcJamie Gennis """Handles a token that is an open bracket. 7242da489cd246702bee5938545b18a6f710ed214bcJamie Gennis 7252da489cd246702bee5938545b18a6f710ed214bcJamie Gennis Args: 7262da489cd246702bee5938545b18a6f710ed214bcJamie Gennis token: The token to handle. 7272da489cd246702bee5938545b18a6f710ed214bcJamie Gennis last_non_space_token: The last token that was not a space. 7282da489cd246702bee5938545b18a6f710ed214bcJamie Gennis """ 7292da489cd246702bee5938545b18a6f710ed214bcJamie Gennis if (not token.IsFirstInLine() and token.previous.type == Type.WHITESPACE and 7302da489cd246702bee5938545b18a6f710ed214bcJamie Gennis last_non_space_token and 7312da489cd246702bee5938545b18a6f710ed214bcJamie Gennis last_non_space_token.type in Type.EXPRESSION_ENDER_TYPES): 7322da489cd246702bee5938545b18a6f710ed214bcJamie Gennis self._HandleError(errors.EXTRA_SPACE, 'Extra space before "["', 7332da489cd246702bee5938545b18a6f710ed214bcJamie Gennis token.previous, Position.All(token.previous.string)) 7342da489cd246702bee5938545b18a6f710ed214bcJamie Gennis # If the [ token is the first token in a line we shouldn't complain 7352da489cd246702bee5938545b18a6f710ed214bcJamie Gennis # about a missing space before [. This is because some Ecma script 7362da489cd246702bee5938545b18a6f710ed214bcJamie Gennis # languages allow syntax like: 7372da489cd246702bee5938545b18a6f710ed214bcJamie Gennis # [Annotation] 7382da489cd246702bee5938545b18a6f710ed214bcJamie Gennis # class MyClass {...} 7392da489cd246702bee5938545b18a6f710ed214bcJamie Gennis # So we don't want to blindly warn about missing spaces before [. 7402da489cd246702bee5938545b18a6f710ed214bcJamie Gennis # In the the future, when rules for computing exactly how many spaces 7412da489cd246702bee5938545b18a6f710ed214bcJamie Gennis # lines should be indented are added, then we can return errors for 7422da489cd246702bee5938545b18a6f710ed214bcJamie Gennis # [ tokens that are improperly indented. 7432da489cd246702bee5938545b18a6f710ed214bcJamie Gennis # For example: 7442da489cd246702bee5938545b18a6f710ed214bcJamie Gennis # var someVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryLongVariableName = 7452da489cd246702bee5938545b18a6f710ed214bcJamie Gennis # [a,b,c]; 7462da489cd246702bee5938545b18a6f710ed214bcJamie Gennis # should trigger a proper indentation warning message as [ is not indented 7472da489cd246702bee5938545b18a6f710ed214bcJamie Gennis # by four spaces. 7482da489cd246702bee5938545b18a6f710ed214bcJamie Gennis elif (not token.IsFirstInLine() and token.previous and 7492da489cd246702bee5938545b18a6f710ed214bcJamie Gennis not token.previous.type in ( 7502da489cd246702bee5938545b18a6f710ed214bcJamie Gennis [Type.WHITESPACE, Type.START_PAREN, Type.START_BRACKET] + 7512da489cd246702bee5938545b18a6f710ed214bcJamie Gennis Type.EXPRESSION_ENDER_TYPES)): 7522da489cd246702bee5938545b18a6f710ed214bcJamie Gennis self._HandleError(errors.MISSING_SPACE, 'Missing space before "["', 7532da489cd246702bee5938545b18a6f710ed214bcJamie Gennis token, Position.AtBeginning()) 7542da489cd246702bee5938545b18a6f710ed214bcJamie Gennis 7552da489cd246702bee5938545b18a6f710ed214bcJamie Gennis def Finalize(self, state, tokenizer_mode): 7562da489cd246702bee5938545b18a6f710ed214bcJamie Gennis last_non_space_token = state.GetLastNonSpaceToken() 7572da489cd246702bee5938545b18a6f710ed214bcJamie Gennis # Check last line for ending with newline. 7582da489cd246702bee5938545b18a6f710ed214bcJamie Gennis if state.GetLastLine() and not (state.GetLastLine().isspace() or 7592da489cd246702bee5938545b18a6f710ed214bcJamie Gennis state.GetLastLine().rstrip('\n\r\f') != state.GetLastLine()): 7602da489cd246702bee5938545b18a6f710ed214bcJamie Gennis self._HandleError( 7612da489cd246702bee5938545b18a6f710ed214bcJamie Gennis errors.FILE_MISSING_NEWLINE, 7622da489cd246702bee5938545b18a6f710ed214bcJamie Gennis 'File does not end with new line. (%s)' % state.GetLastLine(), 7632da489cd246702bee5938545b18a6f710ed214bcJamie Gennis last_non_space_token) 7642da489cd246702bee5938545b18a6f710ed214bcJamie Gennis 7652da489cd246702bee5938545b18a6f710ed214bcJamie Gennis # Check that the mode is not mid comment, argument list, etc. 7662da489cd246702bee5938545b18a6f710ed214bcJamie Gennis if not tokenizer_mode == Modes.TEXT_MODE: 7672da489cd246702bee5938545b18a6f710ed214bcJamie Gennis self._HandleError( 7682da489cd246702bee5938545b18a6f710ed214bcJamie Gennis errors.FILE_IN_BLOCK, 7692da489cd246702bee5938545b18a6f710ed214bcJamie Gennis 'File ended in mode "%s".' % tokenizer_mode, 7702da489cd246702bee5938545b18a6f710ed214bcJamie Gennis last_non_space_token) 7712da489cd246702bee5938545b18a6f710ed214bcJamie Gennis 7722da489cd246702bee5938545b18a6f710ed214bcJamie Gennis try: 7732da489cd246702bee5938545b18a6f710ed214bcJamie Gennis self._indentation.Finalize() 7742da489cd246702bee5938545b18a6f710ed214bcJamie Gennis except Exception, e: 7752da489cd246702bee5938545b18a6f710ed214bcJamie Gennis self._HandleError( 7762da489cd246702bee5938545b18a6f710ed214bcJamie Gennis errors.FILE_DOES_NOT_PARSE, 7772da489cd246702bee5938545b18a6f710ed214bcJamie Gennis str(e), 7782da489cd246702bee5938545b18a6f710ed214bcJamie Gennis last_non_space_token) 7792da489cd246702bee5938545b18a6f710ed214bcJamie Gennis 7802da489cd246702bee5938545b18a6f710ed214bcJamie Gennis def GetLongLineExceptions(self): 7812da489cd246702bee5938545b18a6f710ed214bcJamie Gennis """Gets a list of regexps for lines which can be longer than the limit.""" 7822da489cd246702bee5938545b18a6f710ed214bcJamie Gennis return [] 7832da489cd246702bee5938545b18a6f710ed214bcJamie Gennis 7842da489cd246702bee5938545b18a6f710ed214bcJamie Gennis def InExplicitlyTypedLanguage(self): 7852da489cd246702bee5938545b18a6f710ed214bcJamie Gennis """Returns whether this ecma implementation is explicitly typed.""" 7862da489cd246702bee5938545b18a6f710ed214bcJamie Gennis return False 787