Upstream version 9.38.198.0
[platform/framework/web/crosswalk.git] / src / third_party / closure_linter / closure_linter / javascriptlintrules.py
1 #!/usr/bin/env python
2 # Copyright 2011 The Closure Linter Authors. All Rights Reserved.
3 #
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
7 #
8 #      http://www.apache.org/licenses/LICENSE-2.0
9 #
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.
15
16 """Methods for checking JS files for common style guide violations.
17
18 These style guide violations should only apply to JavaScript and not an Ecma
19 scripting languages.
20 """
21
22 __author__ = ('robbyw@google.com (Robert Walker)',
23               'ajp@google.com (Andy Perelson)',
24               'jacobr@google.com (Jacob Richman)')
25
26 import re
27
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
37
38 # Shorthand
39 Error = error.Error
40 Position = position.Position
41 Rule = error_check.Rule
42 Type = javascripttokens.JavaScriptTokenType
43
44
45 class JavaScriptLintRules(ecmalintrules.EcmaScriptLintRules):
46   """JavaScript lint rules that catch JavaScript specific style errors."""
47
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 = []
59
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)
64
65   def __ContainsRecordType(self, token):
66     """Check whether the given token contains a record type.
67
68     Args:
69       token: The token being checked
70
71     Returns:
72       True if the token contains a record type, False otherwise.
73     """
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.
76     return (
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('{'))
80
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.
84
85     Args:
86       token: The current token under consideration
87       state: parser_state object that indicates the current state in the page
88     """
89
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()
96       return
97
98     # Call the base class's CheckToken function.
99     super(JavaScriptLintRules, self).CheckToken(token, state)
100
101     # Store some convenience variables
102     namespaces_info = self._namespaces_info
103
104     if error_check.ShouldCheck(Rule.UNUSED_LOCAL_VARIABLES):
105       self._CheckUnusedLocalVariables(token, state)
106
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'))
117           if not suppressed:
118             # Look for static members defined on a provided namespace.
119             if namespaces_info:
120               namespace = namespaces_info.GetClosurizedNamespace(identifier)
121               provided_namespaces = namespaces_info.GetProvidedNamespaces()
122             else:
123               namespace = None
124               provided_namespaces = set()
125
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)
137
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)
143
144     if token.type == Type.DOC_FLAG:
145       flag = token.attached_object
146
147       if flag.flag_type == 'param' and flag.name_token is not None:
148         self._CheckForMissingSpaceBeforeToken(
149             token.attached_object.name_token)
150
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,
159                                 token)
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,
165                                 token)
166
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,
174                                 token)
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 =.' %
179                                 flag.name,
180                                 token)
181
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)
188
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)
193
194         elif flag.name_token and flag.type_end_token and tokenutil.Compare(
195             flag.type_end_token, flag.name_token) > 0:
196           self._HandleError(
197               errors.OUT_OF_ORDER_JSDOC_TAG_TYPE,
198               'Type should be immediately after %s tag' % token.string,
199               token)
200
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(
205             next_token.string):
206           break
207         next_token = next_token.next
208       else:
209         self._HandleError(
210             errors.UNNECESSARY_DOUBLE_QUOTED_STRING,
211             'Single-quoted string preferred over double-quoted string.',
212             token,
213             position=Position.All(token.string))
214
215     elif token.type == Type.END_DOC_COMMENT:
216       doc_comment = state.GetDocComment()
217
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)
222
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()):
227
228         # Check if we're in a fileoverview or constructor JsDoc.
229         is_constructor = (
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)
237
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)):
246           return
247
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']):
253           return
254
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:
261             block_start = token
262             token = token.previous
263
264         # Count the number of blank lines before this block.
265         blank_lines = 0
266         token = block_start.previous
267         while token and token.type in [Type.WHITESPACE, Type.BLANK_LINE]:
268           if token.type == Type.BLANK_LINE:
269             # A blank line.
270             blank_lines += 1
271           elif token.type == Type.WHITESPACE and not token.line.strip():
272             # A line with only whitespace on it.
273             blank_lines += 1
274           token = token.previous
275
276         # Log errors.
277         error_message = False
278         expected_blank_lines = 0
279
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:
286           error_message = (
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
290               blank_lines != 2):
291           error_message = 'Should have 2 blank lines between top-level blocks.'
292           expected_blank_lines = 2
293
294         if error_message:
295           self._HandleError(
296               errors.WRONG_BLANK_LINE_COUNT, error_message,
297               block_start, position=Position.AtBeginning(),
298               fix_data=expected_blank_lines - blank_lines)
299
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)
304
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.
313             self._HandleError(
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
319                 function.doc 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)):
327               self._HandleError(
328                   errors.UNNECESSARY_RETURN_DOCUMENTATION,
329                   'Found @return JsDoc on function that returns nothing',
330                   return_flag.flag_token, position=Position.AtBeginning())
331
332         # b/4073735. Method in object literal definition of prototype can
333         # safely reference 'this'.
334         prototype_object_literal = False
335         block_start = None
336         previous_code = None
337         previous_previous_code = None
338
339         # Search for cases where prototype is defined as object literal.
340         #       previous_previous_code
341         #       |       previous_code
342         #       |       | block_start
343         #       |       | |
344         # a.b.prototype = {
345         #   c : function() {
346         #     this.d = 1;
347         #   }
348         # }
349
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()
354
355         # If an object literal then get previous token (code type). For above
356         # case it should be '='.
357         if block_start:
358           previous_code = tokenutil.SearchExcept(block_start,
359                                                  Type.NON_CODE_TYPES,
360                                                  reverse=True)
361
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,
365                                                           Type.NON_CODE_TYPES,
366                                                           reverse=True)
367
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(
372                                         '.prototype'))
373
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):
380           self._HandleError(
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())
387
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:
391           self._HandleError(
392               errors.MISSING_LINE,
393               'Missing newline between constructor and goog.inherits',
394               token,
395               position=Position.AtBeginning())
396
397         extra_space = state.GetLastNonSpaceToken().next
398         while extra_space != token:
399           if extra_space.type == Type.BLANK_LINE:
400             self._HandleError(
401                 errors.EXTRA_LINE,
402                 'Extra line between constructor and goog.inherits',
403                 extra_space)
404           extra_space = extra_space.next
405
406         # TODO(robbyw): Test the last function was a constructor.
407         # TODO(robbyw): Test correct @extends and @implements documentation.
408
409       elif (token.string == 'goog.provide' and
410             not state.InFunction() and
411             namespaces_info is not None):
412         namespace = tokenutil.GetStringAfterToken(token)
413
414         # Report extra goog.provide statement.
415         if not namespace or namespaces_info.IsExtraProvide(token):
416           if not namespace:
417             msg = 'Empty namespace in goog.provide'
418           else:
419             msg = 'Unnecessary goog.provide: ' +  namespace
420
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')
425
426           self._HandleError(
427               errors.EXTRA_GOOG_PROVIDE,
428               msg,
429               token, position=Position.AtBeginning())
430
431         if namespaces_info.IsLastProvide(token):
432           # Report missing provide statements after the last existing provide.
433           missing_provides = namespaces_info.GetMissingProvides()
434           if missing_provides:
435             self._ReportMissingProvides(
436                 missing_provides,
437                 tokenutil.GetLastTokenInSameLine(token).next,
438                 False)
439
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()
444             if missing_requires:
445               self._ReportMissingRequires(
446                   missing_requires,
447                   tokenutil.GetLastTokenInSameLine(token).next,
448                   True)
449
450       elif (token.string == 'goog.require' and
451             not state.InFunction() and
452             namespaces_info is not None):
453         namespace = tokenutil.GetStringAfterToken(token)
454
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()
460           if missing_provides:
461             self._ReportMissingProvides(
462                 missing_provides,
463                 tokenutil.GetFirstTokenInSameLine(token),
464                 True)
465
466         # Report extra goog.require statement.
467         if not namespace or namespaces_info.IsExtraRequire(token):
468           if not namespace:
469             msg = 'Empty namespace in goog.require'
470           else:
471             msg = 'Unnecessary goog.require: ' + namespace
472
473           self._HandleError(
474               errors.EXTRA_GOOG_REQUIRE,
475               msg,
476               token, position=Position.AtBeginning())
477
478         # Report missing goog.require statements.
479         if namespaces_info.IsLastRequire(token):
480           missing_requires = namespaces_info.GetMissingRequires()
481           if missing_requires:
482             self._ReportMissingRequires(
483                 missing_requires,
484                 tokenutil.GetLastTokenInSameLine(token).next,
485                 False)
486
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)):
500         self._HandleError(
501             errors.MISSING_SPACE,
502             'Missing space after "%s"' % token.string,
503             token,
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,
513                                     Type.FUNCTION_NAME)
514             or token.next.type == Type.START_PARAMETERS):
515           self._HandleError(
516               errors.EXTRA_SPACE,
517               'Extra space after "%s"' % token.previous.string,
518               token,
519               position=Position.All(token.string))
520     elif token.type == Type.SEMICOLON:
521       previous_token = tokenutil.SearchExcept(token, Type.NON_CODE_TYPES,
522                                               reverse=True)
523       if not previous_token:
524         self._HandleError(
525             errors.REDUNDANT_SEMICOLON,
526             'Semicolon without any statement',
527             token,
528             position=Position.AtEnd(token.string))
529       elif (previous_token.type == Type.KEYWORD and
530             previous_token.string not in ['break', 'continue', 'return']):
531         self._HandleError(
532             errors.REDUNDANT_SEMICOLON,
533             ('Semicolon after \'%s\' without any statement.'
534              ' Looks like an error.' % previous_token.string),
535             token,
536             position=Position.AtEnd(token.string))
537
538   def _CheckUnusedLocalVariables(self, token, state):
539     """Checks for unused local variables in function blocks.
540
541     Args:
542       token: The token to check.
543       state: The state tracker.
544     """
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:
548       if in_function:
549         identifier = token.string
550         # Check whether the previous token was var.
551         previous_code_token = tokenutil.CustomSearch(
552             token,
553             lambda t: t.type not in Type.NON_CODE_TYPES,
554             reverse=True)
555         if previous_code_token and previous_code_token.IsKeyword('var'):
556           # Add local variable declaration to the top of the unused locals
557           # stack.
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
564           # variable.
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():
575           self._HandleError(
576               errors.UNUSED_LOCAL_VARIABLE,
577               'Unused local variable: %s.' % unused_token.string,
578               unused_token)
579
580   def _MarkLocalVariableUsed(self, token):
581     """Marks the local variable as used in the relevant scope.
582
583     Marks the local variable as used in the scope nearest to the current
584     scope that matches the given token.
585
586     Args:
587       token: The token representing the potential usage of a local variable.
588     """
589
590     identifier = token.string.split('.')[0]
591     # Find the first instance of the identifier in the stack of function scopes
592     # and mark it used.
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]
597         break
598
599   def _ReportMissingProvides(self, missing_provides, token, need_blank_line):
600     """Reports missing provide statements to the error handler.
601
602     Args:
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
607           will be inserted.
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.
611     """
612
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'
617
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'
623
624     self._HandleError(
625         errors.MISSING_GOOG_PROVIDE,
626         missing_provides_msg,
627         token, position=Position.AtBeginning(),
628         fix_data=(missing_provides.keys(), need_blank_line))
629
630   def _ReportMissingRequires(self, missing_requires, token, need_blank_line):
631     """Reports missing require statements to the error handler.
632
633     Args:
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
638           will be inserted.
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.
642     """
643
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'
648
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'
654
655     self._HandleError(
656         errors.MISSING_GOOG_REQUIRE,
657         missing_requires_msg,
658         token, position=Position.AtBeginning(),
659         fix_data=(missing_requires.keys(), need_blank_line))
660
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)
665
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)
670
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,
675                           token)
676
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()
681
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()
689         if missing_provides:
690           self._ReportMissingProvides(
691               missing_provides, state.GetFirstToken(), None)
692
693         missing_requires = namespaces_info.GetMissingRequires()
694         if missing_requires:
695           self._ReportMissingRequires(
696               missing_requires, state.GetFirstToken(), None)
697
698     self._CheckSortedRequiresProvides(state.GetFirstToken())
699
700   def _CheckSortedRequiresProvides(self, token):
701     """Checks that all goog.require and goog.provide statements are sorted.
702
703     Note that this method needs to be run after missing statements are added to
704     preserve alphabetical order.
705
706     Args:
707       token: The first token in the token stream.
708     """
709     sorter = requireprovidesorter.RequireProvideSorter()
710     first_provide_token = sorter.CheckProvides(token)
711     if first_provide_token:
712       new_order = sorter.GetFixedProvideString(first_provide_token)
713       self._HandleError(
714           errors.GOOG_PROVIDES_NOT_ALPHABETIZED,
715           'goog.provide classes must be alphabetized.  The correct code is:\n' +
716           new_order,
717           first_provide_token,
718           position=Position.AtBeginning(),
719           fix_data=first_provide_token)
720
721     first_require_token = sorter.CheckRequires(token)
722     if first_require_token:
723       new_order = sorter.GetFixedRequireString(first_require_token)
724       self._HandleError(
725           errors.GOOG_REQUIRES_NOT_ALPHABETIZED,
726           'goog.require classes must be alphabetized.  The correct code is:\n' +
727           new_order,
728           first_require_token,
729           position=Position.AtBeginning(),
730           fix_data=first_require_token)
731
732   def GetLongLineExceptions(self):
733     """Gets a list of regexps for lines which can be longer than the limit.
734
735     Returns:
736       A list of regexps, used as matches (rather than searches).
737     """
738     return [
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*/]*$'),
743         ]