blob: 907089e9cd5fa42534a0d64979ef114785c479a7 [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.wan16cf4732009-05-14 20:55:30 +000039_VERSION = '1.0.0'
shiqiane35fdd92008-12-10 05:08:54 +000040
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',
zhanyong.wan5b95fa72009-01-27 22:28:45 +000058 'IsInitializedProto',
shiqiane35fdd92008-12-10 05:08:54 +000059 'Le',
60 'Lt',
61 'MatcherCast',
62 'MatchesRegex',
63 'Ne',
64 'Not',
65 'NotNull',
66 'Pointee',
zhanyong.wan5b95fa72009-01-27 22:28:45 +000067 'PointeeIsInitializedProto',
shiqiane35fdd92008-12-10 05:08:54 +000068 'Property',
69 'Ref',
70 'StartsWith',
71 'StrCaseEq',
72 'StrCaseNe',
73 'StrEq',
74 'StrNe',
75 'Truly',
76 'TypedEq',
77
78 # Actions
79 'ByRef',
80 'DoAll',
81 'DoDefault',
82 'IgnoreResult',
83 'Invoke',
84 'InvokeArgument',
85 'InvokeWithoutArgs',
86 'Return',
87 'ReturnNull',
88 'ReturnRef',
89 'SetArgumentPointee',
90 'SetArrayArgument',
91 'WithArgs',
92
93 # Cardinalities
94 'AnyNumber',
95 'AtLeast',
96 'AtMost',
97 'Between',
98 'Exactly',
99
100 # Sequences
101 'InSequence',
102 'Sequence',
103
104 # Misc
105 'DefaultValue',
106 'Mock',
107 ]
108
109
110def _FindAllMatches(regex, s):
111 """Generates all matches of regex in string s."""
112
113 r = re.compile(regex)
114 return r.finditer(s)
115
116
117def _GenericDiagnoser(short_name, long_name, regex, diagnosis, msg):
118 """Diagnoses the given disease by pattern matching.
119
120 Args:
121 short_name: Short name of the disease.
122 long_name: Long name of the disease.
123 regex: Regex for matching the symptoms.
124 diagnosis: Pattern for formatting the diagnosis.
125 msg: Gcc's error messages.
126 Yields:
127 Tuples of the form
128 (short name of disease, long name of disease, diagnosis).
129 """
130
131 for m in _FindAllMatches(regex, msg):
132 yield (short_name, long_name, diagnosis % m.groupdict())
133
134
135def _NeedToReturnReferenceDiagnoser(msg):
136 """Diagnoses the NRR disease, given the error messages by gcc."""
137
138 regex = (r'In member function \'testing::internal::ReturnAction<R>.*\n'
139 r'(?P<file>.*):(?P<line>\d+):\s+instantiated from here\n'
140 r'.*gmock-actions\.h.*error: creating array with negative size')
141 diagnosis = """%(file)s:%(line)s:
142You are using an Return() action in a function that returns a reference.
143Please use ReturnRef() instead."""
144 return _GenericDiagnoser('NRR', 'Need to Return Reference',
145 regex, diagnosis, msg)
146
147
148def _NeedToReturnSomethingDiagnoser(msg):
149 """Diagnoses the NRS disease, given the error messages by gcc."""
150
zhanyong.wan16cf4732009-05-14 20:55:30 +0000151 regex = (r'(?P<file>.*):(?P<line>\d+):\s+'
152 r'(instantiated from here\n.'
153 r'*gmock-actions\.h.*error: void value not ignored)'
154 r'|(error: control reaches end of non-void function)')
shiqiane35fdd92008-12-10 05:08:54 +0000155 diagnosis = """%(file)s:%(line)s:
156You are using an action that returns void, but it needs to return
zhanyong.wan16cf4732009-05-14 20:55:30 +0000157*something*. Please tell it *what* to return. Perhaps you can use
158the pattern DoAll(some_action, Return(some_value))?"""
shiqiane35fdd92008-12-10 05:08:54 +0000159 return _GenericDiagnoser('NRS', 'Need to Return Something',
160 regex, diagnosis, msg)
161
162
163def _NeedToReturnNothingDiagnoser(msg):
164 """Diagnoses the NRN disease, given the error messages by gcc."""
165
166 regex = (r'(?P<file>.*):(?P<line>\d+):\s+instantiated from here\n'
167 r'.*gmock-actions\.h.*error: return-statement with a value, '
168 r'in function returning \'void\'')
169 diagnosis = """%(file)s:%(line)s:
170You are using an action that returns *something*, but it needs to return
171void. Please use a void-returning action instead.
172
173All actions but the last in DoAll(...) must return void. Perhaps you need
174to re-arrange the order of actions in a DoAll(), if you are using one?"""
175 return _GenericDiagnoser('NRN', 'Need to Return Nothing',
176 regex, diagnosis, msg)
177
178
179def _IncompleteByReferenceArgumentDiagnoser(msg):
180 """Diagnoses the IBRA disease, given the error messages by gcc."""
181
182 regex = (r'(?P<file>.*):(?P<line>\d+):\s+instantiated from here\n'
183 r'.*gmock-printers\.h.*error: invalid application of '
184 r'\'sizeof\' to incomplete type \'(?P<type>.*)\'')
185 diagnosis = """%(file)s:%(line)s:
186In order to mock this function, Google Mock needs to see the definition
187of type "%(type)s" - declaration alone is not enough. Either #include
188the header that defines it, or change the argument to be passed
189by pointer."""
190 return _GenericDiagnoser('IBRA', 'Incomplete By-Reference Argument Type',
191 regex, diagnosis, msg)
192
193
194def _OverloadedFunctionMatcherDiagnoser(msg):
195 """Diagnoses the OFM disease, given the error messages by gcc."""
196
197 regex = (r'(?P<file>.*):(?P<line>\d+): error: no matching function for '
198 r'call to \'Truly\(<unresolved overloaded function type>\)')
199 diagnosis = """%(file)s:%(line)s:
200The argument you gave to Truly() is an overloaded function. Please tell
201gcc which overloaded version you want to use.
202
203For example, if you want to use the version whose signature is
204 bool Foo(int n);
205you should write
206 Truly(static_cast<bool (*)(int n)>(Foo))"""
207 return _GenericDiagnoser('OFM', 'Overloaded Function Matcher',
208 regex, diagnosis, msg)
209
210
211def _OverloadedFunctionActionDiagnoser(msg):
212 """Diagnoses the OFA disease, given the error messages by gcc."""
213
214 regex = (r'(?P<file>.*):(?P<line>\d+): error: '
215 r'no matching function for call to \'Invoke\('
216 r'<unresolved overloaded function type>')
217 diagnosis = """%(file)s:%(line)s:
218You are passing an overloaded function to Invoke(). Please tell gcc
219which overloaded version you want to use.
220
221For example, if you want to use the version whose signature is
222 bool MyFunction(int n, double x);
223you should write something like
224 Invoke(static_cast<bool (*)(int n, double x)>(MyFunction))"""
225 return _GenericDiagnoser('OFA', 'Overloaded Function Action',
226 regex, diagnosis, msg)
227
228
229def _OverloadedMethodActionDiagnoser1(msg):
230 """Diagnoses the OMA disease, given the error messages by gcc."""
231
232 regex = (r'(?P<file>.*):(?P<line>\d+): error: '
233 r'.*no matching function for call to \'Invoke\(.*, '
234 r'unresolved overloaded function type>')
235 diagnosis = """%(file)s:%(line)s:
236The second argument you gave to Invoke() is an overloaded method. Please
237tell gcc which overloaded version you want to use.
238
239For example, if you want to use the version whose signature is
240 class Foo {
241 ...
242 bool Bar(int n, double x);
243 };
244you should write something like
245 Invoke(foo, static_cast<bool (Foo::*)(int n, double x)>(&Foo::Bar))"""
246 return _GenericDiagnoser('OMA', 'Overloaded Method Action',
247 regex, diagnosis, msg)
248
249
250def _MockObjectPointerDiagnoser(msg):
251 """Diagnoses the MOP disease, given the error messages by gcc."""
252
253 regex = (r'(?P<file>.*):(?P<line>\d+): error: request for member '
254 r'\'gmock_(?P<method>.+)\' in \'(?P<mock_object>.+)\', '
255 r'which is of non-class type \'(.*::)*(?P<class_name>.+)\*\'')
256 diagnosis = """%(file)s:%(line)s:
257The first argument to ON_CALL() and EXPECT_CALL() must be a mock *object*,
258not a *pointer* to it. Please write '*(%(mock_object)s)' instead of
259'%(mock_object)s' as your first argument.
260
261For example, given the mock class:
262
263 class %(class_name)s : public ... {
264 ...
265 MOCK_METHOD0(%(method)s, ...);
266 };
267
268and the following mock instance:
269
270 %(class_name)s* mock_ptr = ...
271
272you should use the EXPECT_CALL like this:
273
274 EXPECT_CALL(*mock_ptr, %(method)s(...));"""
275 return _GenericDiagnoser('MOP', 'Mock Object Pointer',
276 regex, diagnosis, msg)
277
278
279def _OverloadedMethodActionDiagnoser2(msg):
280 """Diagnoses the OMA disease, given the error messages by gcc."""
281
282 regex = (r'(?P<file>.*):(?P<line>\d+): error: no matching function for '
283 r'call to \'Invoke\(.+, <unresolved overloaded function type>\)')
284 diagnosis = """%(file)s:%(line)s:
285The second argument you gave to Invoke() is an overloaded method. Please
286tell gcc which overloaded version you want to use.
287
288For example, if you want to use the version whose signature is
289 class Foo {
290 ...
291 bool Bar(int n, double x);
292 };
293you should write something like
294 Invoke(foo, static_cast<bool (Foo::*)(int n, double x)>(&Foo::Bar))"""
295 return _GenericDiagnoser('OMA', 'Overloaded Method Action',
296 regex, diagnosis, msg)
297
298
299def _NeedToUseSymbolDiagnoser(msg):
300 """Diagnoses the NUS disease, given the error messages by gcc."""
301
302 regex = (r'(?P<file>.*):(?P<line>\d+): error: \'(?P<symbol>.+)\' '
303 r'(was not declared in this scope|has not been declared)')
304 diagnosis = """%(file)s:%(line)s:
305'%(symbol)s' is defined by Google Mock in the testing namespace.
306Did you forget to write
307 using testing::%(symbol)s;
308?"""
309 for m in _FindAllMatches(regex, msg):
310 symbol = m.groupdict()['symbol']
311 if symbol in _COMMON_GMOCK_SYMBOLS:
312 yield ('NUS', 'Need to Use Symbol', diagnosis % m.groupdict())
313
314
zhanyong.wan5b95fa72009-01-27 22:28:45 +0000315def _NeedToUseReturnNullDiagnoser(msg):
316 """Diagnoses the NRNULL disease, given the error messages by gcc."""
317
318 regex = (r'(?P<file>.*):(?P<line>\d+):\s+instantiated from here\n'
319 r'.*gmock-actions\.h.*error: invalid conversion from '
320 r'\'long int\' to \'(?P<type>.+\*)')
321
322 diagnosis = """%(file)s:%(line)s:
323You are probably calling Return(NULL) and the compiler isn't sure how to turn
324NULL into a %(type)s*. Use ReturnNull() instead.
325Note: the line number may be off; please fix all instances of Return(NULL)."""
326 return _GenericDiagnoser('NRNULL', 'Need to use ReturnNull',
327 regex, diagnosis, msg)
328
329
zhanyong.wan16cf4732009-05-14 20:55:30 +0000330def _WrongMockMethodMacroDiagnoser(msg):
331 """Diagnoses the WMM disease, given the error messages by gcc."""
332
333 regex = (r'(?P<file>.*):(?P<line>\d+):\s+'
334 r'.*this_method_does_not_take_(?P<wrong_args>\d+)_argument.*\n'
335 r'.*\n'
336 r'.*candidates are.*FunctionMocker<[^>]+A(?P<args>\d+)\)>'
337 )
338
339 diagnosis = """%(file)s:%(line)s:
340You are using MOCK_METHOD%(wrong_args)s to define a mock method that has
341%(args)s arguments. Use MOCK_METHOD%(args)s (or MOCK_CONST_METHOD%(args)s,
342MOCK_METHOD%(args)s_T, MOCK_CONST_METHOD%(args)s_T as appropriate) instead."""
343 return _GenericDiagnoser('WMM', 'Wrong MOCK_METHODn macro',
344 regex, diagnosis, msg)
345
346
zhanyong.wan5b95fa72009-01-27 22:28:45 +0000347
shiqiane35fdd92008-12-10 05:08:54 +0000348_DIAGNOSERS = [
349 _IncompleteByReferenceArgumentDiagnoser,
350 _MockObjectPointerDiagnoser,
351 _NeedToReturnNothingDiagnoser,
352 _NeedToReturnReferenceDiagnoser,
353 _NeedToReturnSomethingDiagnoser,
zhanyong.wan5b95fa72009-01-27 22:28:45 +0000354 _NeedToUseReturnNullDiagnoser,
shiqiane35fdd92008-12-10 05:08:54 +0000355 _NeedToUseSymbolDiagnoser,
356 _OverloadedFunctionActionDiagnoser,
357 _OverloadedFunctionMatcherDiagnoser,
358 _OverloadedMethodActionDiagnoser1,
359 _OverloadedMethodActionDiagnoser2,
zhanyong.wan16cf4732009-05-14 20:55:30 +0000360 _WrongMockMethodMacroDiagnoser,
shiqiane35fdd92008-12-10 05:08:54 +0000361 ]
362
363
364def Diagnose(msg):
365 """Generates all possible diagnoses given the gcc error message."""
366
367 for diagnoser in _DIAGNOSERS:
368 for diagnosis in diagnoser(msg):
369 yield '[%s - %s]\n%s' % diagnosis
370
371
372def main():
373 print ('Google Mock Doctor v%s - '
374 'diagnoses problems in code using Google Mock.' % _VERSION)
375
376 if sys.stdin.isatty():
377 print ('Please copy and paste the compiler errors here. Press c-D when '
378 'you are done:')
379 else:
380 print 'Waiting for compiler errors on stdin . . .'
381
382 msg = sys.stdin.read().strip()
383 diagnoses = list(Diagnose(msg))
384 count = len(diagnoses)
385 if not count:
386 print '\nGcc complained:'
387 print '8<------------------------------------------------------------'
388 print msg
389 print '------------------------------------------------------------>8'
390 print """
391Uh-oh, I'm not smart enough to figure out what the problem is. :-(
392However...
393If you send your source code and gcc's error messages to
394googlemock@googlegroups.com, you can be helped and I can get smarter --
395win-win for us!"""
396 else:
397 print '------------------------------------------------------------'
398 print 'Your code appears to have the following',
399 if count > 1:
400 print '%s diseases:' % (count,)
401 else:
402 print 'disease:'
403 i = 0
404 for d in diagnoses:
405 i += 1
406 if count > 1:
407 print '\n#%s:' % (i,)
408 print d
409 print """
410How did I do? If you think I'm wrong or unhelpful, please send your
411source code and gcc's error messages to googlemock@googlegroups.com. Then
412you can be helped and I can get smarter -- I promise I won't be upset!"""
413
414
415if __name__ == '__main__':
416 main()