1#!/usr/bin/env python
2#
3# Copyright 2007 The Closure Linter Authors. All Rights Reserved.
4#
5# Licensed under the Apache License, Version 2.0 (the "License");
6# you may not use this file except in compliance with the License.
7# You may obtain a copy of the License at
8#
9#      http://www.apache.org/licenses/LICENSE-2.0
10#
11# Unless required by applicable law or agreed to in writing, software
12# distributed under the License is distributed on an "AS-IS" BASIS,
13# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14# See the License for the specific language governing permissions and
15# limitations under the License.
16
17"""Core methods for checking JS files for common style guide violations."""
18
19__author__ = ('robbyw@google.com (Robert Walker)',
20              'ajp@google.com (Andy Perelson)')
21
22import gflags as flags
23
24from closure_linter import checkerbase
25from closure_linter import closurizednamespacesinfo
26from closure_linter import ecmametadatapass
27from closure_linter import javascriptlintrules
28from closure_linter import javascriptstatetracker
29from closure_linter.common import lintrunner
30
31flags.DEFINE_list('limited_doc_files', ['dummy.js', 'externs.js'],
32                  'List of files with relaxed documentation checks. Will not '
33                  'report errors for missing documentation, some missing '
34                  'descriptions, or methods whose @return tags don\'t have a '
35                  'matching return statement.')
36flags.DEFINE_list('closurized_namespaces', '',
37                  'Namespace prefixes, used for testing of'
38                  'goog.provide/require')
39flags.DEFINE_list('ignored_extra_namespaces', '',
40                  'Fully qualified namespaces that should be not be reported '
41                  'as extra by the linter.')
42
43
44class JavaScriptStyleChecker(checkerbase.CheckerBase):
45  """Checker that applies JavaScriptLintRules."""
46
47  def __init__(self, error_handler):
48    """Initialize an JavaScriptStyleChecker object.
49
50    Args:
51      error_handler: Error handler to pass all errors to.
52    """
53    self._namespaces_info = None
54    if flags.FLAGS.closurized_namespaces:
55      self._namespaces_info = (
56          closurizednamespacesinfo.ClosurizedNamespacesInfo(
57              flags.FLAGS.closurized_namespaces,
58              flags.FLAGS.ignored_extra_namespaces))
59
60    checkerbase.CheckerBase.__init__(
61        self,
62        error_handler=error_handler,
63        lint_rules=javascriptlintrules.JavaScriptLintRules(
64            self._namespaces_info),
65        state_tracker=javascriptstatetracker.JavaScriptStateTracker(),
66        metadata_pass=ecmametadatapass.EcmaMetaDataPass(),
67        limited_doc_files=flags.FLAGS.limited_doc_files)
68
69  def _CheckTokens(self, token, parse_error, debug_tokens):
70    """Checks a token stream for lint warnings/errors.
71
72    Adds a separate pass for computing dependency information based on
73    goog.require and goog.provide statements prior to the main linting pass.
74
75    Args:
76      token: The first token in the token stream.
77      parse_error: A ParseError if any errors occurred.
78      debug_tokens: Whether every token should be printed as it is encountered
79          during the pass.
80
81    Returns:
82      A boolean indicating whether the full token stream could be checked or if
83      checking failed prematurely.
84    """
85    # To maximize the amount of errors that get reported before a parse error
86    # is displayed, don't run the dependency pass if a parse error exists.
87    if self._namespaces_info and not parse_error:
88      self._namespaces_info.Reset()
89      result = (self._ExecutePass(token, self._DependencyPass) and
90                self._ExecutePass(token, self._LintPass,
91                                  debug_tokens=debug_tokens))
92    else:
93      result = self._ExecutePass(token, self._LintPass, parse_error,
94                                 debug_tokens)
95
96    if not result:
97      return False
98
99    self._lint_rules.Finalize(self._state_tracker, self._tokenizer.mode)
100
101    self._error_handler.FinishFile()
102    return True
103
104  def _DependencyPass(self, token):
105    """Processes an invidual token for dependency information.
106
107    Used to encapsulate the logic needed to process an individual token so that
108    it can be passed to _ExecutePass.
109
110    Args:
111      token: The token to process.
112    """
113    self._namespaces_info.ProcessToken(token, self._state_tracker)
114
115
116class GJsLintRunner(lintrunner.LintRunner):
117  """Wrapper class to run GJsLint."""
118
119  def Run(self, filenames, error_handler):
120    """Run GJsLint on the given filenames.
121
122    Args:
123      filenames: The filenames to check
124      error_handler: An ErrorHandler object.
125    """
126    checker = JavaScriptStyleChecker(error_handler)
127
128    # Check the list of files.
129    for filename in filenames:
130      checker.Check(filename)
131