blob: ca7935ca8af13e512c848fa0b24fa8352f4ae4da [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',
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
151 regex = (r'(?P<file>.*):(?P<line>\d+):\s+instantiated from here\n'
152 r'.*gmock-actions\.h.*error: void value not ignored')
153 diagnosis = """%(file)s:%(line)s:
154You are using an action that returns void, but it needs to return
155*something*. Please tell it *what* to return."""
156 return _GenericDiagnoser('NRS', 'Need to Return Something',
157 regex, diagnosis, msg)
158
159
160def _NeedToReturnNothingDiagnoser(msg):
161 """Diagnoses the NRN disease, given the error messages by gcc."""
162
163 regex = (r'(?P<file>.*):(?P<line>\d+):\s+instantiated from here\n'
164 r'.*gmock-actions\.h.*error: return-statement with a value, '
165 r'in function returning \'void\'')
166 diagnosis = """%(file)s:%(line)s:
167You are using an action that returns *something*, but it needs to return
168void. Please use a void-returning action instead.
169
170All actions but the last in DoAll(...) must return void. Perhaps you need
171to re-arrange the order of actions in a DoAll(), if you are using one?"""
172 return _GenericDiagnoser('NRN', 'Need to Return Nothing',
173 regex, diagnosis, msg)
174
175
176def _IncompleteByReferenceArgumentDiagnoser(msg):
177 """Diagnoses the IBRA disease, given the error messages by gcc."""
178
179 regex = (r'(?P<file>.*):(?P<line>\d+):\s+instantiated from here\n'
180 r'.*gmock-printers\.h.*error: invalid application of '
181 r'\'sizeof\' to incomplete type \'(?P<type>.*)\'')
182 diagnosis = """%(file)s:%(line)s:
183In order to mock this function, Google Mock needs to see the definition
184of type "%(type)s" - declaration alone is not enough. Either #include
185the header that defines it, or change the argument to be passed
186by pointer."""
187 return _GenericDiagnoser('IBRA', 'Incomplete By-Reference Argument Type',
188 regex, diagnosis, msg)
189
190
191def _OverloadedFunctionMatcherDiagnoser(msg):
192 """Diagnoses the OFM disease, given the error messages by gcc."""
193
194 regex = (r'(?P<file>.*):(?P<line>\d+): error: no matching function for '
195 r'call to \'Truly\(<unresolved overloaded function type>\)')
196 diagnosis = """%(file)s:%(line)s:
197The argument you gave to Truly() is an overloaded function. Please tell
198gcc which overloaded version you want to use.
199
200For example, if you want to use the version whose signature is
201 bool Foo(int n);
202you should write
203 Truly(static_cast<bool (*)(int n)>(Foo))"""
204 return _GenericDiagnoser('OFM', 'Overloaded Function Matcher',
205 regex, diagnosis, msg)
206
207
208def _OverloadedFunctionActionDiagnoser(msg):
209 """Diagnoses the OFA disease, given the error messages by gcc."""
210
211 regex = (r'(?P<file>.*):(?P<line>\d+): error: '
212 r'no matching function for call to \'Invoke\('
213 r'<unresolved overloaded function type>')
214 diagnosis = """%(file)s:%(line)s:
215You are passing an overloaded function to Invoke(). Please tell gcc
216which overloaded version you want to use.
217
218For example, if you want to use the version whose signature is
219 bool MyFunction(int n, double x);
220you should write something like
221 Invoke(static_cast<bool (*)(int n, double x)>(MyFunction))"""
222 return _GenericDiagnoser('OFA', 'Overloaded Function Action',
223 regex, diagnosis, msg)
224
225
226def _OverloadedMethodActionDiagnoser1(msg):
227 """Diagnoses the OMA disease, given the error messages by gcc."""
228
229 regex = (r'(?P<file>.*):(?P<line>\d+): error: '
230 r'.*no matching function for call to \'Invoke\(.*, '
231 r'unresolved overloaded function type>')
232 diagnosis = """%(file)s:%(line)s:
233The second argument you gave to Invoke() is an overloaded method. Please
234tell gcc which overloaded version you want to use.
235
236For example, if you want to use the version whose signature is
237 class Foo {
238 ...
239 bool Bar(int n, double x);
240 };
241you should write something like
242 Invoke(foo, static_cast<bool (Foo::*)(int n, double x)>(&Foo::Bar))"""
243 return _GenericDiagnoser('OMA', 'Overloaded Method Action',
244 regex, diagnosis, msg)
245
246
247def _MockObjectPointerDiagnoser(msg):
248 """Diagnoses the MOP disease, given the error messages by gcc."""
249
250 regex = (r'(?P<file>.*):(?P<line>\d+): error: request for member '
251 r'\'gmock_(?P<method>.+)\' in \'(?P<mock_object>.+)\', '
252 r'which is of non-class type \'(.*::)*(?P<class_name>.+)\*\'')
253 diagnosis = """%(file)s:%(line)s:
254The first argument to ON_CALL() and EXPECT_CALL() must be a mock *object*,
255not a *pointer* to it. Please write '*(%(mock_object)s)' instead of
256'%(mock_object)s' as your first argument.
257
258For example, given the mock class:
259
260 class %(class_name)s : public ... {
261 ...
262 MOCK_METHOD0(%(method)s, ...);
263 };
264
265and the following mock instance:
266
267 %(class_name)s* mock_ptr = ...
268
269you should use the EXPECT_CALL like this:
270
271 EXPECT_CALL(*mock_ptr, %(method)s(...));"""
272 return _GenericDiagnoser('MOP', 'Mock Object Pointer',
273 regex, diagnosis, msg)
274
275
276def _OverloadedMethodActionDiagnoser2(msg):
277 """Diagnoses the OMA disease, given the error messages by gcc."""
278
279 regex = (r'(?P<file>.*):(?P<line>\d+): error: no matching function for '
280 r'call to \'Invoke\(.+, <unresolved overloaded function type>\)')
281 diagnosis = """%(file)s:%(line)s:
282The second argument you gave to Invoke() is an overloaded method. Please
283tell gcc which overloaded version you want to use.
284
285For example, if you want to use the version whose signature is
286 class Foo {
287 ...
288 bool Bar(int n, double x);
289 };
290you should write something like
291 Invoke(foo, static_cast<bool (Foo::*)(int n, double x)>(&Foo::Bar))"""
292 return _GenericDiagnoser('OMA', 'Overloaded Method Action',
293 regex, diagnosis, msg)
294
295
296def _NeedToUseSymbolDiagnoser(msg):
297 """Diagnoses the NUS disease, given the error messages by gcc."""
298
299 regex = (r'(?P<file>.*):(?P<line>\d+): error: \'(?P<symbol>.+)\' '
300 r'(was not declared in this scope|has not been declared)')
301 diagnosis = """%(file)s:%(line)s:
302'%(symbol)s' is defined by Google Mock in the testing namespace.
303Did you forget to write
304 using testing::%(symbol)s;
305?"""
306 for m in _FindAllMatches(regex, msg):
307 symbol = m.groupdict()['symbol']
308 if symbol in _COMMON_GMOCK_SYMBOLS:
309 yield ('NUS', 'Need to Use Symbol', diagnosis % m.groupdict())
310
311
zhanyong.wan5b95fa72009-01-27 22:28:45 +0000312def _NeedToUseReturnNullDiagnoser(msg):
313 """Diagnoses the NRNULL disease, given the error messages by gcc."""
314
315 regex = (r'(?P<file>.*):(?P<line>\d+):\s+instantiated from here\n'
316 r'.*gmock-actions\.h.*error: invalid conversion from '
317 r'\'long int\' to \'(?P<type>.+\*)')
318
319 diagnosis = """%(file)s:%(line)s:
320You are probably calling Return(NULL) and the compiler isn't sure how to turn
321NULL into a %(type)s*. Use ReturnNull() instead.
322Note: the line number may be off; please fix all instances of Return(NULL)."""
323 return _GenericDiagnoser('NRNULL', 'Need to use ReturnNull',
324 regex, diagnosis, msg)
325
326
327
shiqiane35fdd92008-12-10 05:08:54 +0000328_DIAGNOSERS = [
329 _IncompleteByReferenceArgumentDiagnoser,
330 _MockObjectPointerDiagnoser,
331 _NeedToReturnNothingDiagnoser,
332 _NeedToReturnReferenceDiagnoser,
333 _NeedToReturnSomethingDiagnoser,
zhanyong.wan5b95fa72009-01-27 22:28:45 +0000334 _NeedToUseReturnNullDiagnoser,
shiqiane35fdd92008-12-10 05:08:54 +0000335 _NeedToUseSymbolDiagnoser,
336 _OverloadedFunctionActionDiagnoser,
337 _OverloadedFunctionMatcherDiagnoser,
338 _OverloadedMethodActionDiagnoser1,
339 _OverloadedMethodActionDiagnoser2,
340 ]
341
342
343def Diagnose(msg):
344 """Generates all possible diagnoses given the gcc error message."""
345
346 for diagnoser in _DIAGNOSERS:
347 for diagnosis in diagnoser(msg):
348 yield '[%s - %s]\n%s' % diagnosis
349
350
351def main():
352 print ('Google Mock Doctor v%s - '
353 'diagnoses problems in code using Google Mock.' % _VERSION)
354
355 if sys.stdin.isatty():
356 print ('Please copy and paste the compiler errors here. Press c-D when '
357 'you are done:')
358 else:
359 print 'Waiting for compiler errors on stdin . . .'
360
361 msg = sys.stdin.read().strip()
362 diagnoses = list(Diagnose(msg))
363 count = len(diagnoses)
364 if not count:
365 print '\nGcc complained:'
366 print '8<------------------------------------------------------------'
367 print msg
368 print '------------------------------------------------------------>8'
369 print """
370Uh-oh, I'm not smart enough to figure out what the problem is. :-(
371However...
372If you send your source code and gcc's error messages to
373googlemock@googlegroups.com, you can be helped and I can get smarter --
374win-win for us!"""
375 else:
376 print '------------------------------------------------------------'
377 print 'Your code appears to have the following',
378 if count > 1:
379 print '%s diseases:' % (count,)
380 else:
381 print 'disease:'
382 i = 0
383 for d in diagnoses:
384 i += 1
385 if count > 1:
386 print '\n#%s:' % (i,)
387 print d
388 print """
389How did I do? If you think I'm wrong or unhelpful, please send your
390source code and gcc's error messages to googlemock@googlegroups.com. Then
391you can be helped and I can get smarter -- I promise I won't be upset!"""
392
393
394if __name__ == '__main__':
395 main()