cpplint_wrapper.py
Go to the documentation of this file.
00001 # Software License Agreement (BSD License)
00002 #
00003 # copyright (c) 2014-2015 roslint contributors
00004 # all rights reserved
00005 #
00006 # Redistribution and use in source and binary forms, with or without
00007 # modification, are permitted provided that the following conditions
00008 # are met:
00009 #
00010 #  * Redistributions of source code must retain the above copyright
00011 #    notice, this list of conditions and the following disclaimer.
00012 #  * Redistributions in binary form must reproduce the above
00013 #    copyright notice, this list of conditions and the following
00014 #    disclaimer in the documentation and/or other materials provided
00015 #    with the distribution.
00016 #  * Neither the name of Willow Garage, Inc. nor the names of its
00017 #    contributors may be used to endorse or promote products derived
00018 #    from this software without specific prior written permission.
00019 #
00020 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
00021 # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
00022 # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
00023 # FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
00024 # COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
00025 # INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
00026 # BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
00027 # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
00028 # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
00029 # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
00030 # ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
00031 # POSSIBILITY OF SUCH DAMAGE.
00032 
00033 from roslint import cpplint
00034 from roslint.cpplint import Match, IsBlankLine, main
00035 from functools import partial
00036 
00037 import os.path
00038 import re
00039 
00040 # Line length as per the ROS C++ Style Guide
00041 cpplint._line_length = 120
00042 
00043 
00044 def patch(original_module):
00045     """ Decorator to easily allow wrapping/overriding of the Check* functions in cpplint. Should
00046         decorate a function which matches the signature of the function it replaces expect with
00047         the addition of a fn parameter, which is a pass-through of the replaced function, in case
00048         the replacement would like call through to the original functionality. """
00049     def wrap(override_fn):
00050         original_fn = getattr(original_module, override_fn.__name__)
00051         setattr(original_module, override_fn.__name__, partial(override_fn, original_fn))
00052 
00053         # Don't actually modify the function being decorated.
00054         return override_fn
00055     return wrap
00056 
00057 
00058 def makeErrorFn(original_fn, suppress_categories, suppress_message_matches):
00059     """ Create a return a wrapped version of the error-report function which suppresses specific
00060         error categories. """
00061     def newError(filename, linenum, category, confidence, message):
00062         if category in suppress_categories:
00063             return
00064         if True in [bool(Match(r, message)) for r in suppress_message_matches]:
00065             return
00066         original_fn(filename, linenum, category, confidence, message)
00067     return newError
00068 
00069 
00070 @patch(cpplint)
00071 def GetHeaderGuardCPPVariable(fn, filename):
00072     """ Replacement for the function which determines the header guard variable, to pick one which
00073         matches ROS C++ Style. """
00074     var_parts = list()
00075     head = filename
00076     while head:
00077         head, tail = os.path.split(head)
00078         var_parts.insert(0, tail)
00079         if head.endswith('include') or tail == "":
00080             break
00081     return re.sub(r'[-./\s]', '_', "_".join(var_parts)).upper()
00082 
00083 
00084 @patch(cpplint)
00085 def CheckBraces(fn, filename, clean_lines, linenum, error):
00086     """ Complete replacement for cpplint.CheckBraces, since the brace rules for ROS C++ Style
00087         are completely different from the Google style guide ones. """
00088     line = clean_lines.elided[linenum]
00089     if Match(r'^(.*){(.*)}.?$', line):
00090         # Special case when both braces are on the same line together, as is the
00091         # case for one-line getters and setters, for example, or rows of a multi-
00092         # dimenstional array initializer.
00093         pass
00094     else:
00095         # Line does not contain both an opening and closing brace.
00096         m = Match(r'^(.*){(.*)$', line)
00097         if m and not (IsBlankLine(m.group(1))):
00098             # Line contains a starting brace and is not empty, uh oh.
00099             if "=" in line and Match(r'\)( *){$', line):
00100                 # Opening brace is permissable in case of an initializer.
00101                 pass
00102             else:
00103                 error(filename, linenum, 'whitespace/braces', 4,
00104                       'when starting a new scope, { should be on a line by itself')
00105         m = Match(r'^(.*)}(.*)$', line)
00106         if m and (not IsBlankLine(m.group(1)) or not IsBlankLine(m.group(2))):
00107             if m.group(2) != ";":
00108                 error(filename, linenum, 'whitespace/braces', 4,
00109                       '} should be on a line by itself')
00110     pass
00111 
00112 
00113 @patch(cpplint)
00114 def CheckIncludeLine(fn, filename, clean_lines, linenum, include_state, error):
00115     """ Run the function to get include state, but suppress all the errors, since
00116         ROS C++ Style is silent on include order, and contains no prohibition on use of streams. """
00117     fn(filename, clean_lines, linenum, include_state,
00118        makeErrorFn(error, ['build/include_order', 'build/include_alpha', 'readability/streams'], []))
00119 
00120 
00121 @patch(cpplint)
00122 def CheckSpacing(fn, filename, clean_lines, linenum, nesting_state, error):
00123     """ Do most of the original Spacing checks, but suppress the ones related to braces, since
00124         the ROS C++ Style rules are different. """
00125     fn(filename, clean_lines, linenum, nesting_state,
00126        makeErrorFn(error, ['readability/braces', 'whitespace/braces'], []))
00127 
00128 
00129 @patch(cpplint)
00130 def ProcessLine(fn, filename, file_extension, clean_lines, line,
00131                 include_state, function_state, nesting_state, error,
00132                 extra_check_functions=[]):
00133     """ Squelch the error about access control indents. """
00134     fn(filename, file_extension, clean_lines, line,
00135        include_state, function_state, nesting_state,
00136        makeErrorFn(error, [], [r'(.*)should be indented \+1 space inside(.*)']),
00137        extra_check_functions=[])
00138 
00139 
00140 @patch(cpplint)
00141 def CheckEmptyBlockBody(fn, filename, clean_lines, linenum, error):
00142     """ Look for empty loop/conditional body with only a single semicolon,
00143         but allow ros-style do while loops. """
00144     from cpplint import CloseExpression
00145 
00146     # Search for loop keywords at the beginning of the line.  Because only
00147     # whitespaces are allowed before the keywords, this will also ignore most
00148     # do-while-loops, since those lines should start with closing brace.
00149     #
00150     # We also check "if" blocks here, since an empty conditional block
00151     # is likely an error.
00152     line = clean_lines.elided[linenum]
00153     matched = Match(r'\s*(for|while|if)\s*\(', line)
00154     if matched:
00155         # Find the end of the conditional expression
00156         (end_line, end_linenum, end_pos) = CloseExpression(
00157             clean_lines, linenum, line.find('('))
00158 
00159         # Output warning if what follows the condition expression is a
00160         # semicolon.  No warning for all other cases, including
00161         # whitespace or newline, since we have a separate check for
00162         # semicolons preceded by whitespace.
00163         if end_pos >= 0 and Match(r';', end_line[end_pos:]):
00164             if matched.group(1) == 'if':
00165                 error(filename, end_linenum,
00166                       'whitespace/empty_conditional_body', 5,
00167                       'Empty conditional bodies should use {}')
00168             elif matched.group(1) == 'while' and linenum is not 0 \
00169                     and "}" in clean_lines.elided[linenum-1]:
00170                 # Don't report an error for ros style do-whiles. Works
00171                 # by checking for a closing brace on the previous
00172                 # line, since that means it's probably a do-while
00173                 # loop.
00174                 return
00175             else:
00176                 error(filename, end_linenum, 'whitespace/empty_loop_body', 5,
00177                       'Empty loop bodies should use {} or continue')


roslint
Author(s): Mike Purvis, Jack O'Quin
autogenerated on Sun Mar 19 2017 03:23:34