00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031
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
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
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
00091
00092
00093 pass
00094 else:
00095
00096 m = Match(r'^(.*){(.*)$', line)
00097 if m and not (IsBlankLine(m.group(1))):
00098
00099 if "=" in line and Match(r'\)( *){$', line):
00100
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
00147
00148
00149
00150
00151
00152 line = clean_lines.elided[linenum]
00153 matched = Match(r'\s*(for|while|if)\s*\(', line)
00154 if matched:
00155
00156 (end_line, end_linenum, end_pos) = CloseExpression(
00157 clean_lines, linenum, line.find('('))
00158
00159
00160
00161
00162
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
00171
00172
00173
00174 return
00175 else:
00176 error(filename, end_linenum, 'whitespace/empty_loop_body', 5,
00177 'Empty loop bodies should use {} or continue')