• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# Copyright (C) 2010 Chris Jerdonek (cjerdonek@webkit.org)
2#
3# Redistribution and use in source and binary forms, with or without
4# modification, are permitted provided that the following conditions
5# are met:
6# 1.  Redistributions of source code must retain the above copyright
7#     notice, this list of conditions and the following disclaimer.
8# 2.  Redistributions in binary form must reproduce the above copyright
9#     notice, this list of conditions and the following disclaimer in the
10#     documentation and/or other materials provided with the distribution.
11#
12# THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' AND ANY
13# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
14# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
15# DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY
16# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
17# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
18# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
19# ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
20# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
21# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
22
23"""Defines style error handler classes.
24
25A style error handler is a function to call when a style error is
26found. Style error handlers can also have state. A class that represents
27a style error handler should implement the following methods.
28
29Methods:
30
31  __call__(self, line_number, category, confidence, message):
32
33    Handle the occurrence of a style error.
34
35    Check whether the error is reportable. If so, increment the total
36    error count and report the details. Note that error reporting can
37    be suppressed after reaching a certain number of reports.
38
39    Args:
40      line_number: The integer line number of the line containing the error.
41      category: The name of the category of the error, for example
42                "whitespace/newline".
43      confidence: An integer between 1 and 5 inclusive that represents the
44                  application's level of confidence in the error. The value
45                  5 means that we are certain of the problem, and the
46                  value 1 means that it could be a legitimate construct.
47      message: The error message to report.
48
49"""
50
51
52import sys
53
54
55class DefaultStyleErrorHandler(object):
56
57    """The default style error handler."""
58
59    def __init__(self, file_path, configuration, increment_error_count,
60                 line_numbers=None):
61        """Create a default style error handler.
62
63        Args:
64          file_path: The path to the file containing the error. This
65                     is used for reporting to the user.
66          configuration: A StyleProcessorConfiguration instance.
67          increment_error_count: A function that takes no arguments and
68                                 increments the total count of reportable
69                                 errors.
70          line_numbers: An array of line numbers of the lines for which
71                        style errors should be reported, or None if errors
72                        for all lines should be reported.  When it is not
73                        None, this array normally contains the line numbers
74                        corresponding to the modified lines of a patch.
75
76        """
77        if line_numbers is not None:
78            line_numbers = set(line_numbers)
79
80        self._file_path = file_path
81        self._configuration = configuration
82        self._increment_error_count = increment_error_count
83        self._line_numbers = line_numbers
84
85        # A string to integer dictionary cache of the number of reportable
86        # errors per category passed to this instance.
87        self._category_totals = {}
88
89    # Useful for unit testing.
90    def __eq__(self, other):
91        """Return whether this instance is equal to another."""
92        if self._configuration != other._configuration:
93            return False
94        if self._file_path != other._file_path:
95            return False
96        if self._increment_error_count != other._increment_error_count:
97            return False
98        if self._line_numbers != other._line_numbers:
99            return False
100
101        return True
102
103    # Useful for unit testing.
104    def __ne__(self, other):
105        # Python does not automatically deduce __ne__ from __eq__.
106        return not self.__eq__(other)
107
108    def _add_reportable_error(self, category):
109        """Increment the error count and return the new category total."""
110        self._increment_error_count() # Increment the total.
111
112        # Increment the category total.
113        if not category in self._category_totals:
114            self._category_totals[category] = 1
115        else:
116            self._category_totals[category] += 1
117
118        return self._category_totals[category]
119
120    def _max_reports(self, category):
121        """Return the maximum number of errors to report."""
122        if not category in self._configuration.max_reports_per_category:
123            return None
124        return self._configuration.max_reports_per_category[category]
125
126    def should_line_be_checked(self, line_number):
127        "Returns if a particular line should be checked"
128        # Was the line that was modified?
129        return self._line_numbers is None or line_number in self._line_numbers
130
131    def __call__(self, line_number, category, confidence, message):
132        """Handle the occurrence of a style error.
133
134        See the docstring of this module for more information.
135
136        """
137        if not self.should_line_be_checked(line_number):
138            return
139
140        if not self._configuration.is_reportable(category=category,
141                                                 confidence_in_error=confidence,
142                                                 file_path=self._file_path):
143            return
144
145        category_total = self._add_reportable_error(category)
146
147        max_reports = self._max_reports(category)
148
149        if (max_reports is not None) and (category_total > max_reports):
150            # Then suppress displaying the error.
151            return
152
153        self._configuration.write_style_error(category=category,
154                                              confidence_in_error=confidence,
155                                              file_path=self._file_path,
156                                              line_number=line_number,
157                                              message=message)
158
159        if category_total == max_reports:
160            self._configuration.stderr_write("Suppressing further [%s] reports "
161                                             "for this file.\n" % category)
162