blob: ce8ec498beefe45cbb8f785cbbf3e5aa73d0229f [file] [log] [blame]
shiqiane35fdd92008-12-10 05:08:54 +00001#!/usr/bin/python2.4
2#
3# Copyright 2008, Google Inc.
4# All rights reserved.
5#
6# Redistribution and use in source and binary forms, with or without
7# modification, are permitted provided that the following conditions are
8# met:
9#
10# * Redistributions of source code must retain the above copyright
11# notice, this list of conditions and the following disclaimer.
12# * Redistributions in binary form must reproduce the above
13# copyright notice, this list of conditions and the following disclaimer
14# in the documentation and/or other materials provided with the
15# distribution.
16# * Neither the name of Google Inc. nor the names of its
17# contributors may be used to endorse or promote products derived from
18# this software without specific prior written permission.
19#
20# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
23# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
24# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
25# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
26# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
30# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31
32"""Converts gcc errors in code using Google Mock to plain English."""
33
34__author__ = 'wan@google.com (Zhanyong Wan)'
35
36import re
37import sys
38
39_VERSION = '0.1.0.80421'
40
41_COMMON_GMOCK_SYMBOLS = [
42 # Matchers
43 '_',
44 'A',
45 'AddressSatisfies',
46 'AllOf',
47 'An',
48 'AnyOf',
49 'ContainsRegex',
50 'DoubleEq',
51 'EndsWith',
52 'Eq',
53 'Field',
54 'FloatEq',
55 'Ge',
56 'Gt',
57 'HasSubstr',
58 'Le',
59 'Lt',
60 'MatcherCast',
61 'MatchesRegex',
62 'Ne',
63 'Not',
64 'NotNull',
65 'Pointee',
66 'Property',
67 'Ref',
68 'StartsWith',
69 'StrCaseEq',
70 'StrCaseNe',
71 'StrEq',
72 'StrNe',
73 'Truly',
74 'TypedEq',
75
76 # Actions
77 'ByRef',
78 'DoAll',
79 'DoDefault',
80 'IgnoreResult',
81 'Invoke',
82 'InvokeArgument',
83 'InvokeWithoutArgs',
84 'Return',
85 'ReturnNull',
86 'ReturnRef',
87 'SetArgumentPointee',
88 'SetArrayArgument',
89 'WithArgs',
90
91 # Cardinalities
92 'AnyNumber',
93 'AtLeast',
94 'AtMost',
95 'Between',
96 'Exactly',
97
98 # Sequences
99 'InSequence',
100 'Sequence',
101
102 # Misc
103 'DefaultValue',
104 'Mock',
105 ]
106
107
108def _FindAllMatches(regex, s):
109 """Generates all matches of regex in string s."""
110
111 r = re.compile(regex)
112 return r.finditer(s)
113
114
115def _GenericDiagnoser(short_name, long_name, regex, diagnosis, msg):
116 """Diagnoses the given disease by pattern matching.
117
118 Args:
119 short_name: Short name of the disease.
120 long_name: Long name of the disease.
121 regex: Regex for matching the symptoms.
122 diagnosis: Pattern for formatting the diagnosis.
123 msg: Gcc's error messages.
124 Yields:
125 Tuples of the form
126 (short name of disease, long name of disease, diagnosis).
127 """
128
129 for m in _FindAllMatches(regex, msg):
130 yield (short_name, long_name, diagnosis % m.groupdict())
131
132
133def _NeedToReturnReferenceDiagnoser(msg):
134 """Diagnoses the NRR disease, given the error messages by gcc."""
135
136 regex = (r'In member function \'testing::internal::ReturnAction<R>.*\n'
137 r'(?P<file>.*):(?P<line>\d+):\s+instantiated from here\n'
138 r'.*gmock-actions\.h.*error: creating array with negative size')
139 diagnosis = """%(file)s:%(line)s:
140You are using an Return() action in a function that returns a reference.
141Please use ReturnRef() instead."""
142 return _GenericDiagnoser('NRR', 'Need to Return Reference',
143 regex, diagnosis, msg)
144
145
146def _NeedToReturnSomethingDiagnoser(msg):
147 """Diagnoses the NRS disease, given the error messages by gcc."""
148
149 regex = (r'(?P<file>.*):(?P<line>\d+):\s+instantiated from here\n'
150 r'.*gmock-actions\.h.*error: void value not ignored')
151 diagnosis = """%(file)s:%(line)s:
152You are using an action that returns void, but it needs to return
153*something*. Please tell it *what* to return."""
154 return _GenericDiagnoser('NRS', 'Need to Return Something',
155 regex, diagnosis, msg)
156
157
158def _NeedToReturnNothingDiagnoser(msg):
159 """Diagnoses the NRN disease, given the error messages by gcc."""
160
161 regex = (r'(?P<file>.*):(?P<line>\d+):\s+instantiated from here\n'
162 r'.*gmock-actions\.h.*error: return-statement with a value, '
163 r'in function returning \'void\'')
164 diagnosis = """%(file)s:%(line)s:
165You are using an action that returns *something*, but it needs to return
166void. Please use a void-returning action instead.
167
168All actions but the last in DoAll(...) must return void. Perhaps you need
169to re-arrange the order of actions in a DoAll(), if you are using one?"""
170 return _GenericDiagnoser('NRN', 'Need to Return Nothing',
171 regex, diagnosis, msg)
172
173
174def _IncompleteByReferenceArgumentDiagnoser(msg):
175 """Diagnoses the IBRA disease, given the error messages by gcc."""
176
177 regex = (r'(?P<file>.*):(?P<line>\d+):\s+instantiated from here\n'
178 r'.*gmock-printers\.h.*error: invalid application of '
179 r'\'sizeof\' to incomplete type \'(?P<type>.*)\'')
180 diagnosis = """%(file)s:%(line)s:
181In order to mock this function, Google Mock needs to see the definition
182of type "%(type)s" - declaration alone is not enough. Either #include
183the header that defines it, or change the argument to be passed
184by pointer."""
185 return _GenericDiagnoser('IBRA', 'Incomplete By-Reference Argument Type',
186 regex, diagnosis, msg)
187
188
189def _OverloadedFunctionMatcherDiagnoser(msg):
190 """Diagnoses the OFM disease, given the error messages by gcc."""
191
192 regex = (r'(?P<file>.*):(?P<line>\d+): error: no matching function for '
193 r'call to \'Truly\(<unresolved overloaded function type>\)')
194 diagnosis = """%(file)s:%(line)s:
195The argument you gave to Truly() is an overloaded function. Please tell
196gcc which overloaded version you want to use.
197
198For example, if you want to use the version whose signature is
199 bool Foo(int n);
200you should write
201 Truly(static_cast<bool (*)(int n)>(Foo))"""
202 return _GenericDiagnoser('OFM', 'Overloaded Function Matcher',
203 regex, diagnosis, msg)
204
205
206def _OverloadedFunctionActionDiagnoser(msg):
207 """Diagnoses the OFA disease, given the error messages by gcc."""
208
209 regex = (r'(?P<file>.*):(?P<line>\d+): error: '
210 r'no matching function for call to \'Invoke\('
211 r'<unresolved overloaded function type>')
212 diagnosis = """%(file)s:%(line)s:
213You are passing an overloaded function to Invoke(). Please tell gcc
214which overloaded version you want to use.
215
216For example, if you want to use the version whose signature is
217 bool MyFunction(int n, double x);
218you should write something like
219 Invoke(static_cast<bool (*)(int n, double x)>(MyFunction))"""
220 return _GenericDiagnoser('OFA', 'Overloaded Function Action',
221 regex, diagnosis, msg)
222
223
224def _OverloadedMethodActionDiagnoser1(msg):
225 """Diagnoses the OMA disease, given the error messages by gcc."""
226
227 regex = (r'(?P<file>.*):(?P<line>\d+): error: '
228 r'.*no matching function for call to \'Invoke\(.*, '
229 r'unresolved overloaded function type>')
230 diagnosis = """%(file)s:%(line)s:
231The second argument you gave to Invoke() is an overloaded method. Please
232tell gcc which overloaded version you want to use.
233
234For example, if you want to use the version whose signature is
235 class Foo {
236 ...
237 bool Bar(int n, double x);
238 };
239you should write something like
240 Invoke(foo, static_cast<bool (Foo::*)(int n, double x)>(&Foo::Bar))"""
241 return _GenericDiagnoser('OMA', 'Overloaded Method Action',
242 regex, diagnosis, msg)
243
244
245def _MockObjectPointerDiagnoser(msg):
246 """Diagnoses the MOP disease, given the error messages by gcc."""
247
248 regex = (r'(?P<file>.*):(?P<line>\d+): error: request for member '
249 r'\'gmock_(?P<method>.+)\' in \'(?P<mock_object>.+)\', '
250 r'which is of non-class type \'(.*::)*(?P<class_name>.+)\*\'')
251 diagnosis = """%(file)s:%(line)s:
252The first argument to ON_CALL() and EXPECT_CALL() must be a mock *object*,
253not a *pointer* to it. Please write '*(%(mock_object)s)' instead of
254'%(mock_object)s' as your first argument.
255
256For example, given the mock class:
257
258 class %(class_name)s : public ... {
259 ...
260 MOCK_METHOD0(%(method)s, ...);
261 };
262
263and the following mock instance:
264
265 %(class_name)s* mock_ptr = ...
266
267you should use the EXPECT_CALL like this:
268
269 EXPECT_CALL(*mock_ptr, %(method)s(...));"""
270 return _GenericDiagnoser('MOP', 'Mock Object Pointer',
271 regex, diagnosis, msg)
272
273
274def _OverloadedMethodActionDiagnoser2(msg):
275 """Diagnoses the OMA disease, given the error messages by gcc."""
276
277 regex = (r'(?P<file>.*):(?P<line>\d+): error: no matching function for '
278 r'call to \'Invoke\(.+, <unresolved overloaded function type>\)')
279 diagnosis = """%(file)s:%(line)s:
280The second argument you gave to Invoke() is an overloaded method. Please
281tell gcc which overloaded version you want to use.
282
283For example, if you want to use the version whose signature is
284 class Foo {
285 ...
286 bool Bar(int n, double x);
287 };
288you should write something like
289 Invoke(foo, static_cast<bool (Foo::*)(int n, double x)>(&Foo::Bar))"""
290 return _GenericDiagnoser('OMA', 'Overloaded Method Action',
291 regex, diagnosis, msg)
292
293
294def _NeedToUseSymbolDiagnoser(msg):
295 """Diagnoses the NUS disease, given the error messages by gcc."""
296
297 regex = (r'(?P<file>.*):(?P<line>\d+): error: \'(?P<symbol>.+)\' '
298 r'(was not declared in this scope|has not been declared)')
299 diagnosis = """%(file)s:%(line)s:
300'%(symbol)s' is defined by Google Mock in the testing namespace.
301Did you forget to write
302 using testing::%(symbol)s;
303?"""
304 for m in _FindAllMatches(regex, msg):
305 symbol = m.groupdict()['symbol']
306 if symbol in _COMMON_GMOCK_SYMBOLS:
307 yield ('NUS', 'Need to Use Symbol', diagnosis % m.groupdict())
308
309
310_DIAGNOSERS = [
311 _IncompleteByReferenceArgumentDiagnoser,
312 _MockObjectPointerDiagnoser,
313 _NeedToReturnNothingDiagnoser,
314 _NeedToReturnReferenceDiagnoser,
315 _NeedToReturnSomethingDiagnoser,
316 _NeedToUseSymbolDiagnoser,
317 _OverloadedFunctionActionDiagnoser,
318 _OverloadedFunctionMatcherDiagnoser,
319 _OverloadedMethodActionDiagnoser1,
320 _OverloadedMethodActionDiagnoser2,
321 ]
322
323
324def Diagnose(msg):
325 """Generates all possible diagnoses given the gcc error message."""
326
327 for diagnoser in _DIAGNOSERS:
328 for diagnosis in diagnoser(msg):
329 yield '[%s - %s]\n%s' % diagnosis
330
331
332def main():
333 print ('Google Mock Doctor v%s - '
334 'diagnoses problems in code using Google Mock.' % _VERSION)
335
336 if sys.stdin.isatty():
337 print ('Please copy and paste the compiler errors here. Press c-D when '
338 'you are done:')
339 else:
340 print 'Waiting for compiler errors on stdin . . .'
341
342 msg = sys.stdin.read().strip()
343 diagnoses = list(Diagnose(msg))
344 count = len(diagnoses)
345 if not count:
346 print '\nGcc complained:'
347 print '8<------------------------------------------------------------'
348 print msg
349 print '------------------------------------------------------------>8'
350 print """
351Uh-oh, I'm not smart enough to figure out what the problem is. :-(
352However...
353If you send your source code and gcc's error messages to
354googlemock@googlegroups.com, you can be helped and I can get smarter --
355win-win for us!"""
356 else:
357 print '------------------------------------------------------------'
358 print 'Your code appears to have the following',
359 if count > 1:
360 print '%s diseases:' % (count,)
361 else:
362 print 'disease:'
363 i = 0
364 for d in diagnoses:
365 i += 1
366 if count > 1:
367 print '\n#%s:' % (i,)
368 print d
369 print """
370How did I do? If you think I'm wrong or unhelpful, please send your
371source code and gcc's error messages to googlemock@googlegroups.com. Then
372you can be helped and I can get smarter -- I promise I won't be upset!"""
373
374
375if __name__ == '__main__':
376 main()