Home | History | Annotate | Download | only in style
      1 # Copyright (C) 2010 Chris Jerdonek (cjerdonek (at] 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 
     25 A style error handler is a function to call when a style error is
     26 found. Style error handlers can also have state. A class that represents
     27 a style error handler should implement the following methods.
     28 
     29 Methods:
     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 
     52 import sys
     53 
     54 
     55 class 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