2 # Copyright 2011 The Closure Linter Authors. All Rights Reserved.
4 # Licensed under the Apache License, Version 2.0 (the "License");
5 # you may not use this file except in compliance with the License.
6 # You may obtain a copy of the License at
8 # http://www.apache.org/licenses/LICENSE-2.0
10 # Unless required by applicable law or agreed to in writing, software
11 # distributed under the License is distributed on an "AS-IS" BASIS,
12 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 # See the License for the specific language governing permissions and
14 # limitations under the License.
16 """Methods for checking JS files for common style guide violations.
18 These style guide violations should only apply to JavaScript and not an Ecma
22 __author__ = ('robbyw@google.com (Robert Walker)',
23 'ajp@google.com (Andy Perelson)',
24 'jacobr@google.com (Jacob Richman)')
28 from closure_linter import ecmalintrules
29 from closure_linter import error_check
30 from closure_linter import errors
31 from closure_linter import javascripttokenizer
32 from closure_linter import javascripttokens
33 from closure_linter import requireprovidesorter
34 from closure_linter import tokenutil
35 from closure_linter.common import error
36 from closure_linter.common import position
40 Position = position.Position
41 Rule = error_check.Rule
42 Type = javascripttokens.JavaScriptTokenType
45 class JavaScriptLintRules(ecmalintrules.EcmaScriptLintRules):
46 """JavaScript lint rules that catch JavaScript specific style errors."""
48 def __init__(self, namespaces_info):
49 """Initializes a JavaScriptLintRules instance."""
50 ecmalintrules.EcmaScriptLintRules.__init__(self)
51 self._namespaces_info = namespaces_info
52 self._declared_private_member_tokens = {}
53 self._declared_private_members = set()
54 self._used_private_members = set()
55 # A stack of dictionaries, one for each function scope entered. Each
56 # dictionary is keyed by an identifier that defines a local variable and has
57 # a token as its value.
58 self._unused_local_variables_by_scope = []
60 def HandleMissingParameterDoc(self, token, param_name):
61 """Handle errors associated with a parameter missing a param tag."""
62 self._HandleError(errors.MISSING_PARAMETER_DOCUMENTATION,
63 'Missing docs for parameter: "%s"' % param_name, token)
65 def __ContainsRecordType(self, token):
66 """Check whether the given token contains a record type.
69 token: The token being checked
72 True if the token contains a record type, False otherwise.
74 # If we see more than one left-brace in the string of an annotation token,
75 # then there's a record type in there.
77 token and token.type == Type.DOC_FLAG and
78 token.attached_object.type is not None and
79 token.attached_object.type.find('{') != token.string.rfind('{'))
81 # pylint: disable=too-many-statements
82 def CheckToken(self, token, state):
83 """Checks a token, given the current parser_state, for warnings and errors.
86 token: The current token under consideration
87 state: parser_state object that indicates the current state in the page
90 # For @param don't ignore record type.
91 if (self.__ContainsRecordType(token) and
92 token.attached_object.flag_type != 'param'):
93 # We should bail out and not emit any warnings for this annotation.
94 # TODO(nicksantos): Support record types for real.
95 state.GetDocComment().Invalidate()
98 # Call the base class's CheckToken function.
99 super(JavaScriptLintRules, self).CheckToken(token, state)
101 # Store some convenience variables
102 namespaces_info = self._namespaces_info
104 if error_check.ShouldCheck(Rule.UNUSED_LOCAL_VARIABLES):
105 self._CheckUnusedLocalVariables(token, state)
107 if error_check.ShouldCheck(Rule.UNUSED_PRIVATE_MEMBERS):
108 # Find all assignments to private members.
109 if token.type == Type.SIMPLE_LVALUE:
110 identifier = token.string
111 if identifier.endswith('_') and not identifier.endswith('__'):
112 doc_comment = state.GetDocComment()
113 suppressed = (doc_comment and doc_comment.HasFlag('suppress') and
114 (doc_comment.GetFlag('suppress').type == 'underscore' or
115 doc_comment.GetFlag('suppress').type ==
116 'unusedPrivateMembers'))
118 # Look for static members defined on a provided namespace.
120 namespace = namespaces_info.GetClosurizedNamespace(identifier)
121 provided_namespaces = namespaces_info.GetProvidedNamespaces()
124 provided_namespaces = set()
126 # Skip cases of this.something_.somethingElse_.
127 regex = re.compile(r'^this\.[a-zA-Z_]+$')
128 if namespace in provided_namespaces or regex.match(identifier):
129 variable = identifier.split('.')[-1]
130 self._declared_private_member_tokens[variable] = token
131 self._declared_private_members.add(variable)
132 elif not identifier.endswith('__'):
133 # Consider setting public members of private members to be a usage.
134 for piece in identifier.split('.'):
135 if piece.endswith('_'):
136 self._used_private_members.add(piece)
138 # Find all usages of private members.
139 if token.type == Type.IDENTIFIER:
140 for piece in token.string.split('.'):
141 if piece.endswith('_'):
142 self._used_private_members.add(piece)
144 if token.type == Type.DOC_FLAG:
145 flag = token.attached_object
147 if flag.flag_type == 'param' and flag.name_token is not None:
148 self._CheckForMissingSpaceBeforeToken(
149 token.attached_object.name_token)
151 if flag.type is not None and flag.name is not None:
152 if error_check.ShouldCheck(Rule.VARIABLE_ARG_MARKER):
153 # Check for variable arguments marker in type.
154 if (flag.type.startswith('...') and
155 flag.name != 'var_args'):
156 self._HandleError(errors.JSDOC_MISSING_VAR_ARGS_NAME,
157 'Variable length argument %s must be renamed '
158 'to var_args.' % flag.name,
160 elif (not flag.type.startswith('...') and
161 flag.name == 'var_args'):
162 self._HandleError(errors.JSDOC_MISSING_VAR_ARGS_TYPE,
163 'Variable length argument %s type must start '
164 'with \'...\'.' % flag.name,
167 if error_check.ShouldCheck(Rule.OPTIONAL_TYPE_MARKER):
168 # Check for optional marker in type.
169 if (flag.type.endswith('=') and
170 not flag.name.startswith('opt_')):
171 self._HandleError(errors.JSDOC_MISSING_OPTIONAL_PREFIX,
172 'Optional parameter name %s must be prefixed '
173 'with opt_.' % flag.name,
175 elif (not flag.type.endswith('=') and
176 flag.name.startswith('opt_')):
177 self._HandleError(errors.JSDOC_MISSING_OPTIONAL_TYPE,
178 'Optional parameter %s type must end with =.' %
182 if flag.flag_type in state.GetDocFlag().HAS_TYPE:
183 # Check for both missing type token and empty type braces '{}'
184 # Missing suppress types are reported separately and we allow enums,
185 # const, private, public and protected without types.
186 allowed_flags = set(['suppress']).union(
187 state.GetDocFlag().CAN_OMIT_TYPE)
189 if (flag.flag_type not in allowed_flags and
190 (not flag.type or flag.type.isspace())):
191 self._HandleError(errors.MISSING_JSDOC_TAG_TYPE,
192 'Missing type in %s tag' % token.string, token)
194 elif flag.name_token and flag.type_end_token and tokenutil.Compare(
195 flag.type_end_token, flag.name_token) > 0:
197 errors.OUT_OF_ORDER_JSDOC_TAG_TYPE,
198 'Type should be immediately after %s tag' % token.string,
201 elif token.type == Type.DOUBLE_QUOTE_STRING_START:
202 next_token = token.next
203 while next_token.type == Type.STRING_TEXT:
204 if javascripttokenizer.JavaScriptTokenizer.SINGLE_QUOTE.search(
207 next_token = next_token.next
210 errors.UNNECESSARY_DOUBLE_QUOTED_STRING,
211 'Single-quoted string preferred over double-quoted string.',
213 position=Position.All(token.string))
215 elif token.type == Type.END_DOC_COMMENT:
216 doc_comment = state.GetDocComment()
218 # When @externs appears in a @fileoverview comment, it should trigger
219 # the same limited doc checks as a special filename like externs.js.
220 if doc_comment.HasFlag('fileoverview') and doc_comment.HasFlag('externs'):
221 self._SetLimitedDocChecks(True)
223 if (error_check.ShouldCheck(Rule.BLANK_LINES_AT_TOP_LEVEL) and
224 not self._is_html and
225 state.InTopLevel() and
226 not state.InNonScopeBlock()):
228 # Check if we're in a fileoverview or constructor JsDoc.
230 doc_comment.HasFlag('constructor') or
231 doc_comment.HasFlag('interface'))
232 # @fileoverview is an optional tag so if the dosctring is the first
233 # token in the file treat it as a file level docstring.
234 is_file_level_comment = (
235 doc_comment.HasFlag('fileoverview') or
236 not doc_comment.start_token.previous)
238 # If the comment is not a file overview, and it does not immediately
239 # precede some code, skip it.
240 # NOTE: The tokenutil methods are not used here because of their
241 # behavior at the top of a file.
242 next_token = token.next
243 if (not next_token or
244 (not is_file_level_comment and
245 next_token.type in Type.NON_CODE_TYPES)):
248 # Don't require extra blank lines around suppression of extra
249 # goog.require errors.
250 if (doc_comment.SuppressionOnly() and
251 next_token.type == Type.IDENTIFIER and
252 next_token.string in ['goog.provide', 'goog.require']):
255 # Find the start of this block (include comments above the block, unless
256 # this is a file overview).
257 block_start = doc_comment.start_token
258 if not is_file_level_comment:
259 token = block_start.previous
260 while token and token.type in Type.COMMENT_TYPES:
262 token = token.previous
264 # Count the number of blank lines before this block.
266 token = block_start.previous
267 while token and token.type in [Type.WHITESPACE, Type.BLANK_LINE]:
268 if token.type == Type.BLANK_LINE:
271 elif token.type == Type.WHITESPACE and not token.line.strip():
272 # A line with only whitespace on it.
274 token = token.previous
277 error_message = False
278 expected_blank_lines = 0
280 # Only need blank line before file overview if it is not the beginning
281 # of the file, e.g. copyright is first.
282 if is_file_level_comment and blank_lines == 0 and block_start.previous:
283 error_message = 'Should have a blank line before a file overview.'
284 expected_blank_lines = 1
285 elif is_constructor and blank_lines != 3:
287 'Should have 3 blank lines before a constructor/interface.')
288 expected_blank_lines = 3
289 elif (not is_file_level_comment and not is_constructor and
291 error_message = 'Should have 2 blank lines between top-level blocks.'
292 expected_blank_lines = 2
296 errors.WRONG_BLANK_LINE_COUNT, error_message,
297 block_start, position=Position.AtBeginning(),
298 fix_data=expected_blank_lines - blank_lines)
300 elif token.type == Type.END_BLOCK:
301 if state.InFunction() and state.IsFunctionClose():
302 is_immediately_called = (token.next and
303 token.next.type == Type.START_PAREN)
305 function = state.GetFunction()
306 if not self._limited_doc_checks:
307 if (function.has_return and function.doc and
308 not is_immediately_called and
309 not function.doc.HasFlag('return') and
310 not function.doc.InheritsDocumentation() and
311 not function.doc.HasFlag('constructor')):
312 # Check for proper documentation of return value.
314 errors.MISSING_RETURN_DOCUMENTATION,
315 'Missing @return JsDoc in function with non-trivial return',
316 function.doc.end_token, position=Position.AtBeginning())
317 elif (not function.has_return and
318 not function.has_throw and
320 function.doc.HasFlag('return') and
321 not state.InInterfaceMethod()):
322 return_flag = function.doc.GetFlag('return')
323 if (return_flag.type is None or (
324 'undefined' not in return_flag.type and
325 'void' not in return_flag.type and
326 '*' not in return_flag.type)):
328 errors.UNNECESSARY_RETURN_DOCUMENTATION,
329 'Found @return JsDoc on function that returns nothing',
330 return_flag.flag_token, position=Position.AtBeginning())
332 # b/4073735. Method in object literal definition of prototype can
333 # safely reference 'this'.
334 prototype_object_literal = False
337 previous_previous_code = None
339 # Search for cases where prototype is defined as object literal.
340 # previous_previous_code
350 # If in object literal, find first token of block so to find previous
351 # tokens to check above condition.
352 if state.InObjectLiteral():
353 block_start = state.GetCurrentBlockStart()
355 # If an object literal then get previous token (code type). For above
356 # case it should be '='.
358 previous_code = tokenutil.SearchExcept(block_start,
362 # If previous token to block is '=' then get its previous token.
363 if previous_code and previous_code.IsOperator('='):
364 previous_previous_code = tokenutil.SearchExcept(previous_code,
368 # If variable/token before '=' ends with '.prototype' then its above
369 # case of prototype defined with object literal.
370 prototype_object_literal = (previous_previous_code and
371 previous_previous_code.string.endswith(
374 if (function.has_this and function.doc and
375 not function.doc.HasFlag('this') and
376 not function.is_constructor and
377 not function.is_interface and
378 '.prototype.' not in function.name and
379 not prototype_object_literal):
381 errors.MISSING_JSDOC_TAG_THIS,
382 'Missing @this JsDoc in function referencing "this". ('
383 'this usually means you are trying to reference "this" in '
384 'a static function, or you have forgotten to mark a '
385 'constructor with @constructor)',
386 function.doc.end_token, position=Position.AtBeginning())
388 elif token.type == Type.IDENTIFIER:
389 if token.string == 'goog.inherits' and not state.InFunction():
390 if state.GetLastNonSpaceToken().line_number == token.line_number:
393 'Missing newline between constructor and goog.inherits',
395 position=Position.AtBeginning())
397 extra_space = state.GetLastNonSpaceToken().next
398 while extra_space != token:
399 if extra_space.type == Type.BLANK_LINE:
402 'Extra line between constructor and goog.inherits',
404 extra_space = extra_space.next
406 # TODO(robbyw): Test the last function was a constructor.
407 # TODO(robbyw): Test correct @extends and @implements documentation.
409 elif (token.string == 'goog.provide' and
410 not state.InFunction() and
411 namespaces_info is not None):
412 namespace = tokenutil.GetStringAfterToken(token)
414 # Report extra goog.provide statement.
415 if not namespace or namespaces_info.IsExtraProvide(token):
417 msg = 'Empty namespace in goog.provide'
419 msg = 'Unnecessary goog.provide: ' + namespace
421 # Hint to user if this is a Test namespace.
422 if namespace.endswith('Test'):
423 msg += (' *Test namespaces must be mentioned in the '
424 'goog.setTestOnly() call')
427 errors.EXTRA_GOOG_PROVIDE,
429 token, position=Position.AtBeginning())
431 if namespaces_info.IsLastProvide(token):
432 # Report missing provide statements after the last existing provide.
433 missing_provides = namespaces_info.GetMissingProvides()
435 self._ReportMissingProvides(
437 tokenutil.GetLastTokenInSameLine(token).next,
440 # If there are no require statements, missing requires should be
441 # reported after the last provide.
442 if not namespaces_info.GetRequiredNamespaces():
443 missing_requires = namespaces_info.GetMissingRequires()
445 self._ReportMissingRequires(
447 tokenutil.GetLastTokenInSameLine(token).next,
450 elif (token.string == 'goog.require' and
451 not state.InFunction() and
452 namespaces_info is not None):
453 namespace = tokenutil.GetStringAfterToken(token)
455 # If there are no provide statements, missing provides should be
456 # reported before the first require.
457 if (namespaces_info.IsFirstRequire(token) and
458 not namespaces_info.GetProvidedNamespaces()):
459 missing_provides = namespaces_info.GetMissingProvides()
461 self._ReportMissingProvides(
463 tokenutil.GetFirstTokenInSameLine(token),
466 # Report extra goog.require statement.
467 if not namespace or namespaces_info.IsExtraRequire(token):
469 msg = 'Empty namespace in goog.require'
471 msg = 'Unnecessary goog.require: ' + namespace
474 errors.EXTRA_GOOG_REQUIRE,
476 token, position=Position.AtBeginning())
478 # Report missing goog.require statements.
479 if namespaces_info.IsLastRequire(token):
480 missing_requires = namespaces_info.GetMissingRequires()
482 self._ReportMissingRequires(
484 tokenutil.GetLastTokenInSameLine(token).next,
487 elif token.type == Type.OPERATOR:
488 last_in_line = token.IsLastInLine()
489 # If the token is unary and appears to be used in a unary context
490 # it's ok. Otherwise, if it's at the end of the line or immediately
491 # before a comment, it's ok.
492 # Don't report an error before a start bracket - it will be reported
493 # by that token's space checks.
494 if (not token.metadata.IsUnaryOperator() and not last_in_line
495 and not token.next.IsComment()
496 and not token.next.IsOperator(',')
497 and token.next.type not in (Type.WHITESPACE, Type.END_PAREN,
498 Type.END_BRACKET, Type.SEMICOLON,
499 Type.START_BRACKET)):
501 errors.MISSING_SPACE,
502 'Missing space after "%s"' % token.string,
504 position=Position.AtEnd(token.string))
505 elif token.type == Type.WHITESPACE:
506 first_in_line = token.IsFirstInLine()
507 last_in_line = token.IsLastInLine()
508 # Check whitespace length if it's not the first token of the line and
509 # if it's not immediately before a comment.
510 if not last_in_line and not first_in_line and not token.next.IsComment():
511 # Ensure there is no space after opening parentheses.
512 if (token.previous.type in (Type.START_PAREN, Type.START_BRACKET,
514 or token.next.type == Type.START_PARAMETERS):
517 'Extra space after "%s"' % token.previous.string,
519 position=Position.All(token.string))
520 elif token.type == Type.SEMICOLON:
521 previous_token = tokenutil.SearchExcept(token, Type.NON_CODE_TYPES,
523 if not previous_token:
525 errors.REDUNDANT_SEMICOLON,
526 'Semicolon without any statement',
528 position=Position.AtEnd(token.string))
529 elif (previous_token.type == Type.KEYWORD and
530 previous_token.string not in ['break', 'continue', 'return']):
532 errors.REDUNDANT_SEMICOLON,
533 ('Semicolon after \'%s\' without any statement.'
534 ' Looks like an error.' % previous_token.string),
536 position=Position.AtEnd(token.string))
538 def _CheckUnusedLocalVariables(self, token, state):
539 """Checks for unused local variables in function blocks.
542 token: The token to check.
543 state: The state tracker.
545 # We don't use state.InFunction because that disregards scope functions.
546 in_function = state.FunctionDepth() > 0
547 if token.type == Type.SIMPLE_LVALUE or token.type == Type.IDENTIFIER:
549 identifier = token.string
550 # Check whether the previous token was var.
551 previous_code_token = tokenutil.CustomSearch(
553 lambda t: t.type not in Type.NON_CODE_TYPES,
555 if previous_code_token and previous_code_token.IsKeyword('var'):
556 # Add local variable declaration to the top of the unused locals
558 self._unused_local_variables_by_scope[-1][identifier] = token
559 elif token.type == Type.IDENTIFIER:
560 # This covers most cases where the variable is used as an identifier.
561 self._MarkLocalVariableUsed(token)
562 elif token.type == Type.SIMPLE_LVALUE and '.' in identifier:
563 # This covers cases where a value is assigned to a property of the
565 self._MarkLocalVariableUsed(token)
566 elif token.type == Type.START_BLOCK:
567 if in_function and state.IsFunctionOpen():
568 # Push a new map onto the stack
569 self._unused_local_variables_by_scope.append({})
570 elif token.type == Type.END_BLOCK:
571 if state.IsFunctionClose():
572 # Pop the stack and report any remaining locals as unused.
573 unused_local_variables = self._unused_local_variables_by_scope.pop()
574 for unused_token in unused_local_variables.values():
576 errors.UNUSED_LOCAL_VARIABLE,
577 'Unused local variable: %s.' % unused_token.string,
580 def _MarkLocalVariableUsed(self, token):
581 """Marks the local variable as used in the relevant scope.
583 Marks the local variable as used in the scope nearest to the current
584 scope that matches the given token.
587 token: The token representing the potential usage of a local variable.
590 identifier = token.string.split('.')[0]
591 # Find the first instance of the identifier in the stack of function scopes
593 for unused_local_variables in reversed(
594 self._unused_local_variables_by_scope):
595 if identifier in unused_local_variables:
596 del unused_local_variables[identifier]
599 def _ReportMissingProvides(self, missing_provides, token, need_blank_line):
600 """Reports missing provide statements to the error handler.
603 missing_provides: A dictionary of string(key) and integer(value) where
604 each string(key) is a namespace that should be provided, but is not
605 and integer(value) is first line number where it's required.
606 token: The token where the error was detected (also where the new provides
608 need_blank_line: Whether a blank line needs to be inserted after the new
609 provides are inserted. May be True, False, or None, where None
610 indicates that the insert location is unknown.
613 missing_provides_msg = 'Missing the following goog.provide statements:\n'
614 missing_provides_msg += '\n'.join(['goog.provide(\'%s\');' % x for x in
615 sorted(missing_provides)])
616 missing_provides_msg += '\n'
618 missing_provides_msg += '\nFirst line where provided: \n'
619 missing_provides_msg += '\n'.join(
620 [' %s : line %d' % (x, missing_provides[x]) for x in
621 sorted(missing_provides)])
622 missing_provides_msg += '\n'
625 errors.MISSING_GOOG_PROVIDE,
626 missing_provides_msg,
627 token, position=Position.AtBeginning(),
628 fix_data=(missing_provides.keys(), need_blank_line))
630 def _ReportMissingRequires(self, missing_requires, token, need_blank_line):
631 """Reports missing require statements to the error handler.
634 missing_requires: A dictionary of string(key) and integer(value) where
635 each string(key) is a namespace that should be required, but is not
636 and integer(value) is first line number where it's required.
637 token: The token where the error was detected (also where the new requires
639 need_blank_line: Whether a blank line needs to be inserted before the new
640 requires are inserted. May be True, False, or None, where None
641 indicates that the insert location is unknown.
644 missing_requires_msg = 'Missing the following goog.require statements:\n'
645 missing_requires_msg += '\n'.join(['goog.require(\'%s\');' % x for x in
646 sorted(missing_requires)])
647 missing_requires_msg += '\n'
649 missing_requires_msg += '\nFirst line where required: \n'
650 missing_requires_msg += '\n'.join(
651 [' %s : line %d' % (x, missing_requires[x]) for x in
652 sorted(missing_requires)])
653 missing_requires_msg += '\n'
656 errors.MISSING_GOOG_REQUIRE,
657 missing_requires_msg,
658 token, position=Position.AtBeginning(),
659 fix_data=(missing_requires.keys(), need_blank_line))
661 def Finalize(self, state):
662 """Perform all checks that need to occur after all lines are processed."""
663 # Call the base class's Finalize function.
664 super(JavaScriptLintRules, self).Finalize(state)
666 if error_check.ShouldCheck(Rule.UNUSED_PRIVATE_MEMBERS):
667 # Report an error for any declared private member that was never used.
668 unused_private_members = (self._declared_private_members -
669 self._used_private_members)
671 for variable in unused_private_members:
672 token = self._declared_private_member_tokens[variable]
673 self._HandleError(errors.UNUSED_PRIVATE_MEMBER,
674 'Unused private member: %s.' % token.string,
677 # Clear state to prepare for the next file.
678 self._declared_private_member_tokens = {}
679 self._declared_private_members = set()
680 self._used_private_members = set()
682 namespaces_info = self._namespaces_info
683 if namespaces_info is not None:
684 # If there are no provide or require statements, missing provides and
685 # requires should be reported on line 1.
686 if (not namespaces_info.GetProvidedNamespaces() and
687 not namespaces_info.GetRequiredNamespaces()):
688 missing_provides = namespaces_info.GetMissingProvides()
690 self._ReportMissingProvides(
691 missing_provides, state.GetFirstToken(), None)
693 missing_requires = namespaces_info.GetMissingRequires()
695 self._ReportMissingRequires(
696 missing_requires, state.GetFirstToken(), None)
698 self._CheckSortedRequiresProvides(state.GetFirstToken())
700 def _CheckSortedRequiresProvides(self, token):
701 """Checks that all goog.require and goog.provide statements are sorted.
703 Note that this method needs to be run after missing statements are added to
704 preserve alphabetical order.
707 token: The first token in the token stream.
709 sorter = requireprovidesorter.RequireProvideSorter()
710 first_provide_token = sorter.CheckProvides(token)
711 if first_provide_token:
712 new_order = sorter.GetFixedProvideString(first_provide_token)
714 errors.GOOG_PROVIDES_NOT_ALPHABETIZED,
715 'goog.provide classes must be alphabetized. The correct code is:\n' +
718 position=Position.AtBeginning(),
719 fix_data=first_provide_token)
721 first_require_token = sorter.CheckRequires(token)
722 if first_require_token:
723 new_order = sorter.GetFixedRequireString(first_require_token)
725 errors.GOOG_REQUIRES_NOT_ALPHABETIZED,
726 'goog.require classes must be alphabetized. The correct code is:\n' +
729 position=Position.AtBeginning(),
730 fix_data=first_require_token)
732 def GetLongLineExceptions(self):
733 """Gets a list of regexps for lines which can be longer than the limit.
736 A list of regexps, used as matches (rather than searches).
739 re.compile(r'goog\.require\(.+\);?\s*$'),
740 re.compile(r'goog\.provide\(.+\);?\s*$'),
741 re.compile(r'goog\.setTestOnly\(.+\);?\s*$'),
742 re.compile(r'[\s/*]*@visibility\s*{.*}[\s*/]*$'),