blob: 1980977cb9c17638e7ccf88ba9100fe5538362a7 [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
zhanyong.wan9413f2f2009-05-29 19:50:06 +000039_VERSION = '1.0.1'
shiqiane35fdd92008-12-10 05:08:54 +000040
41_COMMON_GMOCK_SYMBOLS = [
42 # Matchers
43 '_',
44 'A',
45 'AddressSatisfies',
46 'AllOf',
47 'An',
48 'AnyOf',
zhanyong.wan9413f2f2009-05-29 19:50:06 +000049 'ContainerEq',
50 'Contains',
shiqiane35fdd92008-12-10 05:08:54 +000051 'ContainsRegex',
52 'DoubleEq',
zhanyong.wan9413f2f2009-05-29 19:50:06 +000053 'ElementsAre',
54 'ElementsAreArray',
shiqiane35fdd92008-12-10 05:08:54 +000055 'EndsWith',
56 'Eq',
57 'Field',
58 'FloatEq',
59 'Ge',
60 'Gt',
61 'HasSubstr',
zhanyong.wan5b95fa72009-01-27 22:28:45 +000062 'IsInitializedProto',
shiqiane35fdd92008-12-10 05:08:54 +000063 'Le',
64 'Lt',
65 'MatcherCast',
66 'MatchesRegex',
zhanyong.wan9413f2f2009-05-29 19:50:06 +000067 'NanSensitiveDoubleEq',
68 'NanSensitiveFloatEq',
shiqiane35fdd92008-12-10 05:08:54 +000069 'Ne',
70 'Not',
71 'NotNull',
72 'Pointee',
zhanyong.wan5b95fa72009-01-27 22:28:45 +000073 'PointeeIsInitializedProto',
shiqiane35fdd92008-12-10 05:08:54 +000074 'Property',
75 'Ref',
zhanyong.wan9413f2f2009-05-29 19:50:06 +000076 'ResultOf',
77 'SafeMatcherCast',
shiqiane35fdd92008-12-10 05:08:54 +000078 'StartsWith',
79 'StrCaseEq',
80 'StrCaseNe',
81 'StrEq',
82 'StrNe',
83 'Truly',
84 'TypedEq',
85
86 # Actions
zhanyong.wan9413f2f2009-05-29 19:50:06 +000087 'Assign',
shiqiane35fdd92008-12-10 05:08:54 +000088 'ByRef',
zhanyong.wan9413f2f2009-05-29 19:50:06 +000089 'DeleteArg',
shiqiane35fdd92008-12-10 05:08:54 +000090 'DoAll',
91 'DoDefault',
92 'IgnoreResult',
93 'Invoke',
94 'InvokeArgument',
95 'InvokeWithoutArgs',
96 'Return',
zhanyong.wan9413f2f2009-05-29 19:50:06 +000097 'ReturnNew',
shiqiane35fdd92008-12-10 05:08:54 +000098 'ReturnNull',
99 'ReturnRef',
zhanyong.wan9413f2f2009-05-29 19:50:06 +0000100 'SaveArg',
101 'SetArgReferee',
shiqiane35fdd92008-12-10 05:08:54 +0000102 'SetArgumentPointee',
103 'SetArrayArgument',
zhanyong.wan9413f2f2009-05-29 19:50:06 +0000104 'SetErrnoAndReturn',
105 'Throw',
106 'WithArg',
shiqiane35fdd92008-12-10 05:08:54 +0000107 'WithArgs',
zhanyong.wan9413f2f2009-05-29 19:50:06 +0000108 'WithoutArgs',
shiqiane35fdd92008-12-10 05:08:54 +0000109
110 # Cardinalities
111 'AnyNumber',
112 'AtLeast',
113 'AtMost',
114 'Between',
115 'Exactly',
116
117 # Sequences
118 'InSequence',
119 'Sequence',
120
121 # Misc
122 'DefaultValue',
123 'Mock',
124 ]
125
zhanyong.wan9413f2f2009-05-29 19:50:06 +0000126# Regex for matching source file path and line number in gcc's errors.
127_FILE_LINE_RE = r'(?P<file>.*):(?P<line>\d+):\s+'
128
shiqiane35fdd92008-12-10 05:08:54 +0000129
130def _FindAllMatches(regex, s):
131 """Generates all matches of regex in string s."""
132
133 r = re.compile(regex)
134 return r.finditer(s)
135
136
137def _GenericDiagnoser(short_name, long_name, regex, diagnosis, msg):
138 """Diagnoses the given disease by pattern matching.
139
140 Args:
141 short_name: Short name of the disease.
142 long_name: Long name of the disease.
143 regex: Regex for matching the symptoms.
144 diagnosis: Pattern for formatting the diagnosis.
145 msg: Gcc's error messages.
146 Yields:
147 Tuples of the form
148 (short name of disease, long name of disease, diagnosis).
149 """
150
zhanyong.wan9413f2f2009-05-29 19:50:06 +0000151 diagnosis = '%(file)s:%(line)s:' + diagnosis
shiqiane35fdd92008-12-10 05:08:54 +0000152 for m in _FindAllMatches(regex, msg):
153 yield (short_name, long_name, diagnosis % m.groupdict())
154
155
156def _NeedToReturnReferenceDiagnoser(msg):
157 """Diagnoses the NRR disease, given the error messages by gcc."""
158
159 regex = (r'In member function \'testing::internal::ReturnAction<R>.*\n'
zhanyong.wan9413f2f2009-05-29 19:50:06 +0000160 + _FILE_LINE_RE + r'instantiated from here\n'
shiqiane35fdd92008-12-10 05:08:54 +0000161 r'.*gmock-actions\.h.*error: creating array with negative size')
zhanyong.wan9413f2f2009-05-29 19:50:06 +0000162 diagnosis = """
shiqiane35fdd92008-12-10 05:08:54 +0000163You are using an Return() action in a function that returns a reference.
164Please use ReturnRef() instead."""
165 return _GenericDiagnoser('NRR', 'Need to Return Reference',
166 regex, diagnosis, msg)
167
168
169def _NeedToReturnSomethingDiagnoser(msg):
170 """Diagnoses the NRS disease, given the error messages by gcc."""
171
zhanyong.wan9413f2f2009-05-29 19:50:06 +0000172 regex = (_FILE_LINE_RE +
zhanyong.wan16cf4732009-05-14 20:55:30 +0000173 r'(instantiated from here\n.'
174 r'*gmock-actions\.h.*error: void value not ignored)'
175 r'|(error: control reaches end of non-void function)')
zhanyong.wan9413f2f2009-05-29 19:50:06 +0000176 diagnosis = """
shiqiane35fdd92008-12-10 05:08:54 +0000177You are using an action that returns void, but it needs to return
zhanyong.wan16cf4732009-05-14 20:55:30 +0000178*something*. Please tell it *what* to return. Perhaps you can use
179the pattern DoAll(some_action, Return(some_value))?"""
shiqiane35fdd92008-12-10 05:08:54 +0000180 return _GenericDiagnoser('NRS', 'Need to Return Something',
181 regex, diagnosis, msg)
182
183
184def _NeedToReturnNothingDiagnoser(msg):
185 """Diagnoses the NRN disease, given the error messages by gcc."""
186
zhanyong.wan9413f2f2009-05-29 19:50:06 +0000187 regex = (_FILE_LINE_RE + r'instantiated from here\n'
shiqiane35fdd92008-12-10 05:08:54 +0000188 r'.*gmock-actions\.h.*error: return-statement with a value, '
189 r'in function returning \'void\'')
zhanyong.wan9413f2f2009-05-29 19:50:06 +0000190 diagnosis = """
shiqiane35fdd92008-12-10 05:08:54 +0000191You are using an action that returns *something*, but it needs to return
192void. Please use a void-returning action instead.
193
194All actions but the last in DoAll(...) must return void. Perhaps you need
195to re-arrange the order of actions in a DoAll(), if you are using one?"""
196 return _GenericDiagnoser('NRN', 'Need to Return Nothing',
197 regex, diagnosis, msg)
198
199
200def _IncompleteByReferenceArgumentDiagnoser(msg):
201 """Diagnoses the IBRA disease, given the error messages by gcc."""
202
zhanyong.wan9413f2f2009-05-29 19:50:06 +0000203 regex = (_FILE_LINE_RE + r'instantiated from here\n'
shiqiane35fdd92008-12-10 05:08:54 +0000204 r'.*gmock-printers\.h.*error: invalid application of '
205 r'\'sizeof\' to incomplete type \'(?P<type>.*)\'')
zhanyong.wan9413f2f2009-05-29 19:50:06 +0000206 diagnosis = """
shiqiane35fdd92008-12-10 05:08:54 +0000207In order to mock this function, Google Mock needs to see the definition
208of type "%(type)s" - declaration alone is not enough. Either #include
209the header that defines it, or change the argument to be passed
210by pointer."""
211 return _GenericDiagnoser('IBRA', 'Incomplete By-Reference Argument Type',
212 regex, diagnosis, msg)
213
214
215def _OverloadedFunctionMatcherDiagnoser(msg):
216 """Diagnoses the OFM disease, given the error messages by gcc."""
217
zhanyong.wan9413f2f2009-05-29 19:50:06 +0000218 regex = (_FILE_LINE_RE + r'error: no matching function for '
shiqiane35fdd92008-12-10 05:08:54 +0000219 r'call to \'Truly\(<unresolved overloaded function type>\)')
zhanyong.wan9413f2f2009-05-29 19:50:06 +0000220 diagnosis = """
shiqiane35fdd92008-12-10 05:08:54 +0000221The argument you gave to Truly() is an overloaded function. Please tell
222gcc which overloaded version you want to use.
223
224For example, if you want to use the version whose signature is
225 bool Foo(int n);
226you should write
227 Truly(static_cast<bool (*)(int n)>(Foo))"""
228 return _GenericDiagnoser('OFM', 'Overloaded Function Matcher',
229 regex, diagnosis, msg)
230
231
232def _OverloadedFunctionActionDiagnoser(msg):
233 """Diagnoses the OFA disease, given the error messages by gcc."""
234
zhanyong.wan9413f2f2009-05-29 19:50:06 +0000235 regex = (_FILE_LINE_RE + r'error: no matching function for call to \'Invoke\('
shiqiane35fdd92008-12-10 05:08:54 +0000236 r'<unresolved overloaded function type>')
zhanyong.wan9413f2f2009-05-29 19:50:06 +0000237 diagnosis = """
shiqiane35fdd92008-12-10 05:08:54 +0000238You are passing an overloaded function to Invoke(). Please tell gcc
239which overloaded version you want to use.
240
241For example, if you want to use the version whose signature is
242 bool MyFunction(int n, double x);
243you should write something like
244 Invoke(static_cast<bool (*)(int n, double x)>(MyFunction))"""
245 return _GenericDiagnoser('OFA', 'Overloaded Function Action',
246 regex, diagnosis, msg)
247
248
249def _OverloadedMethodActionDiagnoser1(msg):
250 """Diagnoses the OMA disease, given the error messages by gcc."""
251
zhanyong.wan9413f2f2009-05-29 19:50:06 +0000252 regex = (_FILE_LINE_RE + r'error: '
shiqiane35fdd92008-12-10 05:08:54 +0000253 r'.*no matching function for call to \'Invoke\(.*, '
254 r'unresolved overloaded function type>')
zhanyong.wan9413f2f2009-05-29 19:50:06 +0000255 diagnosis = """
shiqiane35fdd92008-12-10 05:08:54 +0000256The second argument you gave to Invoke() is an overloaded method. Please
257tell gcc which overloaded version you want to use.
258
259For example, if you want to use the version whose signature is
260 class Foo {
261 ...
262 bool Bar(int n, double x);
263 };
264you should write something like
265 Invoke(foo, static_cast<bool (Foo::*)(int n, double x)>(&Foo::Bar))"""
266 return _GenericDiagnoser('OMA', 'Overloaded Method Action',
267 regex, diagnosis, msg)
268
269
270def _MockObjectPointerDiagnoser(msg):
271 """Diagnoses the MOP disease, given the error messages by gcc."""
272
zhanyong.wan9413f2f2009-05-29 19:50:06 +0000273 regex = (_FILE_LINE_RE + r'error: request for member '
shiqiane35fdd92008-12-10 05:08:54 +0000274 r'\'gmock_(?P<method>.+)\' in \'(?P<mock_object>.+)\', '
275 r'which is of non-class type \'(.*::)*(?P<class_name>.+)\*\'')
zhanyong.wan9413f2f2009-05-29 19:50:06 +0000276 diagnosis = """
shiqiane35fdd92008-12-10 05:08:54 +0000277The first argument to ON_CALL() and EXPECT_CALL() must be a mock *object*,
278not a *pointer* to it. Please write '*(%(mock_object)s)' instead of
279'%(mock_object)s' as your first argument.
280
281For example, given the mock class:
282
283 class %(class_name)s : public ... {
284 ...
285 MOCK_METHOD0(%(method)s, ...);
286 };
287
288and the following mock instance:
289
290 %(class_name)s* mock_ptr = ...
291
292you should use the EXPECT_CALL like this:
293
294 EXPECT_CALL(*mock_ptr, %(method)s(...));"""
295 return _GenericDiagnoser('MOP', 'Mock Object Pointer',
296 regex, diagnosis, msg)
297
298
299def _OverloadedMethodActionDiagnoser2(msg):
300 """Diagnoses the OMA disease, given the error messages by gcc."""
301
zhanyong.wan9413f2f2009-05-29 19:50:06 +0000302 regex = (_FILE_LINE_RE + r'error: no matching function for '
shiqiane35fdd92008-12-10 05:08:54 +0000303 r'call to \'Invoke\(.+, <unresolved overloaded function type>\)')
zhanyong.wan9413f2f2009-05-29 19:50:06 +0000304 diagnosis = """
shiqiane35fdd92008-12-10 05:08:54 +0000305The second argument you gave to Invoke() is an overloaded method. Please
306tell gcc which overloaded version you want to use.
307
308For example, if you want to use the version whose signature is
309 class Foo {
310 ...
311 bool Bar(int n, double x);
312 };
313you should write something like
314 Invoke(foo, static_cast<bool (Foo::*)(int n, double x)>(&Foo::Bar))"""
315 return _GenericDiagnoser('OMA', 'Overloaded Method Action',
316 regex, diagnosis, msg)
317
318
319def _NeedToUseSymbolDiagnoser(msg):
320 """Diagnoses the NUS disease, given the error messages by gcc."""
321
zhanyong.wan9413f2f2009-05-29 19:50:06 +0000322 regex = (_FILE_LINE_RE + r'error: \'(?P<symbol>.+)\' '
shiqiane35fdd92008-12-10 05:08:54 +0000323 r'(was not declared in this scope|has not been declared)')
zhanyong.wan9413f2f2009-05-29 19:50:06 +0000324 diagnosis = """
shiqiane35fdd92008-12-10 05:08:54 +0000325'%(symbol)s' is defined by Google Mock in the testing namespace.
326Did you forget to write
327 using testing::%(symbol)s;
328?"""
329 for m in _FindAllMatches(regex, msg):
330 symbol = m.groupdict()['symbol']
331 if symbol in _COMMON_GMOCK_SYMBOLS:
332 yield ('NUS', 'Need to Use Symbol', diagnosis % m.groupdict())
333
334
zhanyong.wan5b95fa72009-01-27 22:28:45 +0000335def _NeedToUseReturnNullDiagnoser(msg):
336 """Diagnoses the NRNULL disease, given the error messages by gcc."""
337
zhanyong.wan9413f2f2009-05-29 19:50:06 +0000338 regex = (_FILE_LINE_RE + r'instantiated from here\n'
zhanyong.wan5b95fa72009-01-27 22:28:45 +0000339 r'.*gmock-actions\.h.*error: invalid conversion from '
340 r'\'long int\' to \'(?P<type>.+\*)')
zhanyong.wan9413f2f2009-05-29 19:50:06 +0000341 diagnosis = """
zhanyong.wan5b95fa72009-01-27 22:28:45 +0000342You are probably calling Return(NULL) and the compiler isn't sure how to turn
343NULL into a %(type)s*. Use ReturnNull() instead.
344Note: the line number may be off; please fix all instances of Return(NULL)."""
345 return _GenericDiagnoser('NRNULL', 'Need to use ReturnNull',
346 regex, diagnosis, msg)
347
348
zhanyong.wan16cf4732009-05-14 20:55:30 +0000349def _WrongMockMethodMacroDiagnoser(msg):
350 """Diagnoses the WMM disease, given the error messages by gcc."""
351
zhanyong.wan9413f2f2009-05-29 19:50:06 +0000352 regex = (_FILE_LINE_RE +
zhanyong.wan16cf4732009-05-14 20:55:30 +0000353 r'.*this_method_does_not_take_(?P<wrong_args>\d+)_argument.*\n'
354 r'.*\n'
zhanyong.wan9413f2f2009-05-29 19:50:06 +0000355 r'.*candidates are.*FunctionMocker<[^>]+A(?P<args>\d+)\)>')
356 diagnosis = """
zhanyong.wan16cf4732009-05-14 20:55:30 +0000357You are using MOCK_METHOD%(wrong_args)s to define a mock method that has
358%(args)s arguments. Use MOCK_METHOD%(args)s (or MOCK_CONST_METHOD%(args)s,
359MOCK_METHOD%(args)s_T, MOCK_CONST_METHOD%(args)s_T as appropriate) instead."""
360 return _GenericDiagnoser('WMM', 'Wrong MOCK_METHODn macro',
361 regex, diagnosis, msg)
362
363
zhanyong.wan9413f2f2009-05-29 19:50:06 +0000364def _WrongParenPositionDiagnoser(msg):
365 """Diagnoses the WPP disease, given the error messages by gcc."""
366
367 regex = (_FILE_LINE_RE +
368 r'error:.*testing::internal::MockSpec<.* has no member named \''
369 r'(?P<method>\w+)\'')
370 diagnosis = """
371The closing parenthesis of ON_CALL or EXPECT_CALL should be *before*
372".%(method)s". For example, you should write:
373 EXPECT_CALL(my_mock, Foo(_)).%(method)s(...);
374instead of:
375 EXPECT_CALL(my_mock, Foo(_).%(method)s(...));"""
376 return _GenericDiagnoser('WPP', 'Wrong parenthesis position',
377 regex, diagnosis, msg)
378
zhanyong.wan5b95fa72009-01-27 22:28:45 +0000379
shiqiane35fdd92008-12-10 05:08:54 +0000380_DIAGNOSERS = [
381 _IncompleteByReferenceArgumentDiagnoser,
382 _MockObjectPointerDiagnoser,
383 _NeedToReturnNothingDiagnoser,
384 _NeedToReturnReferenceDiagnoser,
385 _NeedToReturnSomethingDiagnoser,
zhanyong.wan5b95fa72009-01-27 22:28:45 +0000386 _NeedToUseReturnNullDiagnoser,
shiqiane35fdd92008-12-10 05:08:54 +0000387 _NeedToUseSymbolDiagnoser,
388 _OverloadedFunctionActionDiagnoser,
389 _OverloadedFunctionMatcherDiagnoser,
390 _OverloadedMethodActionDiagnoser1,
391 _OverloadedMethodActionDiagnoser2,
zhanyong.wan16cf4732009-05-14 20:55:30 +0000392 _WrongMockMethodMacroDiagnoser,
zhanyong.wan9413f2f2009-05-29 19:50:06 +0000393 _WrongParenPositionDiagnoser,
shiqiane35fdd92008-12-10 05:08:54 +0000394 ]
395
396
397def Diagnose(msg):
398 """Generates all possible diagnoses given the gcc error message."""
399
400 for diagnoser in _DIAGNOSERS:
401 for diagnosis in diagnoser(msg):
402 yield '[%s - %s]\n%s' % diagnosis
403
404
405def main():
406 print ('Google Mock Doctor v%s - '
407 'diagnoses problems in code using Google Mock.' % _VERSION)
408
409 if sys.stdin.isatty():
410 print ('Please copy and paste the compiler errors here. Press c-D when '
411 'you are done:')
412 else:
413 print 'Waiting for compiler errors on stdin . . .'
414
415 msg = sys.stdin.read().strip()
416 diagnoses = list(Diagnose(msg))
417 count = len(diagnoses)
418 if not count:
419 print '\nGcc complained:'
420 print '8<------------------------------------------------------------'
421 print msg
422 print '------------------------------------------------------------>8'
423 print """
424Uh-oh, I'm not smart enough to figure out what the problem is. :-(
425However...
426If you send your source code and gcc's error messages to
427googlemock@googlegroups.com, you can be helped and I can get smarter --
428win-win for us!"""
429 else:
430 print '------------------------------------------------------------'
431 print 'Your code appears to have the following',
432 if count > 1:
433 print '%s diseases:' % (count,)
434 else:
435 print 'disease:'
436 i = 0
437 for d in diagnoses:
438 i += 1
439 if count > 1:
440 print '\n#%s:' % (i,)
441 print d
442 print """
443How did I do? If you think I'm wrong or unhelpful, please send your
444source code and gcc's error messages to googlemock@googlegroups.com. Then
445you can be helped and I can get smarter -- I promise I won't be upset!"""
446
447
448if __name__ == '__main__':
449 main()