1#!/usr/bin/env python
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 compiler's 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 = '1.0.3'
40
41_EMAIL = 'googlemock@googlegroups.com'
42
43_COMMON_GMOCK_SYMBOLS = [
44    # Matchers
45    '_',
46    'A',
47    'AddressSatisfies',
48    'AllOf',
49    'An',
50    'AnyOf',
51    'ContainerEq',
52    'Contains',
53    'ContainsRegex',
54    'DoubleEq',
55    'ElementsAre',
56    'ElementsAreArray',
57    'EndsWith',
58    'Eq',
59    'Field',
60    'FloatEq',
61    'Ge',
62    'Gt',
63    'HasSubstr',
64    'IsInitializedProto',
65    'Le',
66    'Lt',
67    'MatcherCast',
68    'Matches',
69    'MatchesRegex',
70    'NanSensitiveDoubleEq',
71    'NanSensitiveFloatEq',
72    'Ne',
73    'Not',
74    'NotNull',
75    'Pointee',
76    'Property',
77    'Ref',
78    'ResultOf',
79    'SafeMatcherCast',
80    'StartsWith',
81    'StrCaseEq',
82    'StrCaseNe',
83    'StrEq',
84    'StrNe',
85    'Truly',
86    'TypedEq',
87    'Value',
88
89    # Actions
90    'Assign',
91    'ByRef',
92    'DeleteArg',
93    'DoAll',
94    'DoDefault',
95    'IgnoreResult',
96    'Invoke',
97    'InvokeArgument',
98    'InvokeWithoutArgs',
99    'Return',
100    'ReturnNew',
101    'ReturnNull',
102    'ReturnRef',
103    'SaveArg',
104    'SetArgReferee',
105    'SetArgPointee',
106    'SetArgumentPointee',
107    'SetArrayArgument',
108    'SetErrnoAndReturn',
109    'Throw',
110    'WithArg',
111    'WithArgs',
112    'WithoutArgs',
113
114    # Cardinalities
115    'AnyNumber',
116    'AtLeast',
117    'AtMost',
118    'Between',
119    'Exactly',
120
121    # Sequences
122    'InSequence',
123    'Sequence',
124
125    # Misc
126    'DefaultValue',
127    'Mock',
128    ]
129
130# Regex for matching source file path and line number in the compiler's errors.
131_GCC_FILE_LINE_RE = r'(?P<file>.*):(?P<line>\d+):(\d+:)?\s+'
132_CLANG_FILE_LINE_RE = r'(?P<file>.*):(?P<line>\d+):(?P<column>\d+):\s+'
133_CLANG_NON_GMOCK_FILE_LINE_RE = (
134    r'(?P<file>.*[/\\^](?!gmock-)[^/\\]+):(?P<line>\d+):(?P<column>\d+):\s+')
135
136
137def _FindAllMatches(regex, s):
138  """Generates all matches of regex in string s."""
139
140  r = re.compile(regex)
141  return r.finditer(s)
142
143
144def _GenericDiagnoser(short_name, long_name, diagnoses, msg):
145  """Diagnoses the given disease by pattern matching.
146
147  Can provide different diagnoses for different patterns.
148
149  Args:
150    short_name: Short name of the disease.
151    long_name:  Long name of the disease.
152    diagnoses:  A list of pairs (regex, pattern for formatting the diagnosis
153                for matching regex).
154    msg:        Compiler's error messages.
155  Yields:
156    Tuples of the form
157      (short name of disease, long name of disease, diagnosis).
158  """
159  for regex, diagnosis in diagnoses:
160    if re.search(regex, msg):
161      diagnosis = '%(file)s:%(line)s:' + diagnosis
162      for m in _FindAllMatches(regex, msg):
163        yield (short_name, long_name, diagnosis % m.groupdict())
164
165
166def _NeedToReturnReferenceDiagnoser(msg):
167  """Diagnoses the NRR disease, given the error messages by the compiler."""
168
169  gcc_regex = (r'In member function \'testing::internal::ReturnAction<R>.*\n'
170               + _GCC_FILE_LINE_RE + r'instantiated from here\n'
171               r'.*gmock-actions\.h.*error: creating array with negative size')
172  clang_regex = (r'error:.*array.*negative.*\r?\n'
173                 r'(.*\n)*?' +
174                 _CLANG_NON_GMOCK_FILE_LINE_RE +
175                 r'note: in instantiation of function template specialization '
176                 r'\'testing::internal::ReturnAction<(?P<type>.*)>'
177                 r'::operator Action<.*>\' requested here')
178  clang11_re = (r'use_ReturnRef_instead_of_Return_to_return_a_reference.*'
179                r'(.*\n)*?' + _CLANG_NON_GMOCK_FILE_LINE_RE)
180
181  diagnosis = """
182You are using a Return() action in a function that returns a reference to
183%(type)s.  Please use ReturnRef() instead."""
184  return _GenericDiagnoser('NRR', 'Need to Return Reference',
185                           [(clang_regex, diagnosis),
186                            (clang11_re, diagnosis % {'type': 'a type'}),
187                            (gcc_regex, diagnosis % {'type': 'a type'})],
188                           msg)
189
190
191def _NeedToReturnSomethingDiagnoser(msg):
192  """Diagnoses the NRS disease, given the error messages by the compiler."""
193
194  gcc_regex = (_GCC_FILE_LINE_RE + r'(instantiated from here\n.'
195               r'*gmock.*actions\.h.*error: void value not ignored)'
196               r'|(error: control reaches end of non-void function)')
197  clang_regex1 = (_CLANG_FILE_LINE_RE +
198                  r'error: cannot initialize return object '
199                  r'of type \'Result\' \(aka \'(?P<return_type>.*)\'\) '
200                  r'with an rvalue of type \'void\'')
201  clang_regex2 = (_CLANG_FILE_LINE_RE +
202                  r'error: cannot initialize return object '
203                  r'of type \'(?P<return_type>.*)\' '
204                  r'with an rvalue of type \'void\'')
205  diagnosis = """
206You are using an action that returns void, but it needs to return
207%(return_type)s.  Please tell it *what* to return.  Perhaps you can use
208the pattern DoAll(some_action, Return(some_value))?"""
209  return _GenericDiagnoser(
210      'NRS',
211      'Need to Return Something',
212      [(gcc_regex, diagnosis % {'return_type': '*something*'}),
213       (clang_regex1, diagnosis),
214       (clang_regex2, diagnosis)],
215      msg)
216
217
218def _NeedToReturnNothingDiagnoser(msg):
219  """Diagnoses the NRN disease, given the error messages by the compiler."""
220
221  gcc_regex = (_GCC_FILE_LINE_RE + r'instantiated from here\n'
222               r'.*gmock-actions\.h.*error: instantiation of '
223               r'\'testing::internal::ReturnAction<R>::Impl<F>::value_\' '
224               r'as type \'void\'')
225  clang_regex1 = (r'error: field has incomplete type '
226                  r'\'Result\' \(aka \'void\'\)(\r)?\n'
227                  r'(.*\n)*?' +
228                  _CLANG_NON_GMOCK_FILE_LINE_RE + r'note: in instantiation '
229                  r'of function template specialization '
230                  r'\'testing::internal::ReturnAction<(?P<return_type>.*)>'
231                  r'::operator Action<void \(.*\)>\' requested here')
232  clang_regex2 = (r'error: field has incomplete type '
233                  r'\'Result\' \(aka \'void\'\)(\r)?\n'
234                  r'(.*\n)*?' +
235                  _CLANG_NON_GMOCK_FILE_LINE_RE + r'note: in instantiation '
236                  r'of function template specialization '
237                  r'\'testing::internal::DoBothAction<.*>'
238                  r'::operator Action<(?P<return_type>.*) \(.*\)>\' '
239                  r'requested here')
240  diagnosis = """
241You are using an action that returns %(return_type)s, but it needs to return
242void.  Please use a void-returning action instead.
243
244All actions but the last in DoAll(...) must return void.  Perhaps you need
245to re-arrange the order of actions in a DoAll(), if you are using one?"""
246  return _GenericDiagnoser(
247      'NRN',
248      'Need to Return Nothing',
249      [(gcc_regex, diagnosis % {'return_type': '*something*'}),
250       (clang_regex1, diagnosis),
251       (clang_regex2, diagnosis)],
252      msg)
253
254
255def _IncompleteByReferenceArgumentDiagnoser(msg):
256  """Diagnoses the IBRA disease, given the error messages by the compiler."""
257
258  gcc_regex = (_GCC_FILE_LINE_RE + r'instantiated from here\n'
259               r'.*gtest-printers\.h.*error: invalid application of '
260               r'\'sizeof\' to incomplete type \'(?P<type>.*)\'')
261
262  clang_regex = (r'.*gtest-printers\.h.*error: invalid application of '
263                 r'\'sizeof\' to an incomplete type '
264                 r'\'(?P<type>.*)( const)?\'\r?\n'
265                 r'(.*\n)*?' +
266                 _CLANG_NON_GMOCK_FILE_LINE_RE +
267                 r'note: in instantiation of member function '
268                 r'\'testing::internal2::TypeWithoutFormatter<.*>::'
269                 r'PrintValue\' requested here')
270  diagnosis = """
271In order to mock this function, Google Mock needs to see the definition
272of type "%(type)s" - declaration alone is not enough.  Either #include
273the header that defines it, or change the argument to be passed
274by pointer."""
275
276  return _GenericDiagnoser('IBRA', 'Incomplete By-Reference Argument Type',
277                           [(gcc_regex, diagnosis),
278                            (clang_regex, diagnosis)],
279                           msg)
280
281
282def _OverloadedFunctionMatcherDiagnoser(msg):
283  """Diagnoses the OFM disease, given the error messages by the compiler."""
284
285  gcc_regex = (_GCC_FILE_LINE_RE + r'error: no matching function for '
286               r'call to \'Truly\(<unresolved overloaded function type>\)')
287  clang_regex = (_CLANG_FILE_LINE_RE + r'error: no matching function for '
288                 r'call to \'Truly')
289  diagnosis = """
290The argument you gave to Truly() is an overloaded function.  Please tell
291your compiler which overloaded version you want to use.
292
293For example, if you want to use the version whose signature is
294  bool Foo(int n);
295you should write
296  Truly(static_cast<bool (*)(int n)>(Foo))"""
297  return _GenericDiagnoser('OFM', 'Overloaded Function Matcher',
298                           [(gcc_regex, diagnosis),
299                            (clang_regex, diagnosis)],
300                           msg)
301
302
303def _OverloadedFunctionActionDiagnoser(msg):
304  """Diagnoses the OFA disease, given the error messages by the compiler."""
305
306  gcc_regex = (_GCC_FILE_LINE_RE + r'error: no matching function for call to '
307               r'\'Invoke\(<unresolved overloaded function type>')
308  clang_regex = (_CLANG_FILE_LINE_RE + r'error: no matching '
309                 r'function for call to \'Invoke\'\r?\n'
310                 r'(.*\n)*?'
311                 r'.*\bgmock-generated-actions\.h:\d+:\d+:\s+'
312                 r'note: candidate template ignored:\s+'
313                 r'couldn\'t infer template argument \'FunctionImpl\'')
314  diagnosis = """
315Function you are passing to Invoke is overloaded.  Please tell your compiler
316which overloaded version you want to use.
317
318For example, if you want to use the version whose signature is
319  bool MyFunction(int n, double x);
320you should write something like
321  Invoke(static_cast<bool (*)(int n, double x)>(MyFunction))"""
322  return _GenericDiagnoser('OFA', 'Overloaded Function Action',
323                           [(gcc_regex, diagnosis),
324                            (clang_regex, diagnosis)],
325                           msg)
326
327
328def _OverloadedMethodActionDiagnoser(msg):
329  """Diagnoses the OMA disease, given the error messages by the compiler."""
330
331  gcc_regex = (_GCC_FILE_LINE_RE + r'error: no matching function for '
332               r'call to \'Invoke\(.+, <unresolved overloaded function '
333               r'type>\)')
334  clang_regex = (_CLANG_FILE_LINE_RE + r'error: no matching function '
335                 r'for call to \'Invoke\'\r?\n'
336                 r'(.*\n)*?'
337                 r'.*\bgmock-generated-actions\.h:\d+:\d+: '
338                 r'note: candidate function template not viable: '
339                 r'requires .*, but 2 (arguments )?were provided')
340  diagnosis = """
341The second argument you gave to Invoke() is an overloaded method.  Please
342tell your compiler which overloaded version you want to use.
343
344For example, if you want to use the version whose signature is
345  class Foo {
346    ...
347    bool Bar(int n, double x);
348  };
349you should write something like
350  Invoke(foo, static_cast<bool (Foo::*)(int n, double x)>(&Foo::Bar))"""
351  return _GenericDiagnoser('OMA', 'Overloaded Method Action',
352                           [(gcc_regex, diagnosis),
353                            (clang_regex, diagnosis)],
354                           msg)
355
356
357def _MockObjectPointerDiagnoser(msg):
358  """Diagnoses the MOP disease, given the error messages by the compiler."""
359
360  gcc_regex = (_GCC_FILE_LINE_RE + r'error: request for member '
361               r'\'gmock_(?P<method>.+)\' in \'(?P<mock_object>.+)\', '
362               r'which is of non-class type \'(.*::)*(?P<class_name>.+)\*\'')
363  clang_regex = (_CLANG_FILE_LINE_RE + r'error: member reference type '
364                 r'\'(?P<class_name>.*?) *\' is a pointer; '
365                 r'(did you mean|maybe you meant) to use \'->\'\?')
366  diagnosis = """
367The first argument to ON_CALL() and EXPECT_CALL() must be a mock *object*,
368not a *pointer* to it.  Please write '*(%(mock_object)s)' instead of
369'%(mock_object)s' as your first argument.
370
371For example, given the mock class:
372
373  class %(class_name)s : public ... {
374    ...
375    MOCK_METHOD0(%(method)s, ...);
376  };
377
378and the following mock instance:
379
380  %(class_name)s* mock_ptr = ...
381
382you should use the EXPECT_CALL like this:
383
384  EXPECT_CALL(*mock_ptr, %(method)s(...));"""
385
386  return _GenericDiagnoser(
387      'MOP',
388      'Mock Object Pointer',
389      [(gcc_regex, diagnosis),
390       (clang_regex, diagnosis % {'mock_object': 'mock_object',
391                                  'method': 'method',
392                                  'class_name': '%(class_name)s'})],
393       msg)
394
395
396def _NeedToUseSymbolDiagnoser(msg):
397  """Diagnoses the NUS disease, given the error messages by the compiler."""
398
399  gcc_regex = (_GCC_FILE_LINE_RE + r'error: \'(?P<symbol>.+)\' '
400               r'(was not declared in this scope|has not been declared)')
401  clang_regex = (_CLANG_FILE_LINE_RE +
402                 r'error: (use of undeclared identifier|unknown type name|'
403                 r'no template named) \'(?P<symbol>[^\']+)\'')
404  diagnosis = """
405'%(symbol)s' is defined by Google Mock in the testing namespace.
406Did you forget to write
407  using testing::%(symbol)s;
408?"""
409  for m in (list(_FindAllMatches(gcc_regex, msg)) +
410            list(_FindAllMatches(clang_regex, msg))):
411    symbol = m.groupdict()['symbol']
412    if symbol in _COMMON_GMOCK_SYMBOLS:
413      yield ('NUS', 'Need to Use Symbol', diagnosis % m.groupdict())
414
415
416def _NeedToUseReturnNullDiagnoser(msg):
417  """Diagnoses the NRNULL disease, given the error messages by the compiler."""
418
419  gcc_regex = ('instantiated from \'testing::internal::ReturnAction<R>'
420               '::operator testing::Action<Func>\(\) const.*\n' +
421               _GCC_FILE_LINE_RE + r'instantiated from here\n'
422               r'.*error: no matching function for call to \'ImplicitCast_\('
423               r'(:?long )?int&\)')
424  clang_regex = (r'\bgmock-actions.h:.* error: no matching function for '
425                 r'call to \'ImplicitCast_\'\r?\n'
426                 r'(.*\n)*?' +
427                 _CLANG_NON_GMOCK_FILE_LINE_RE + r'note: in instantiation '
428                 r'of function template specialization '
429                 r'\'testing::internal::ReturnAction<(int|long)>::operator '
430                 r'Action<(?P<type>.*)\(\)>\' requested here')
431  diagnosis = """
432You are probably calling Return(NULL) and the compiler isn't sure how to turn
433NULL into %(type)s. Use ReturnNull() instead.
434Note: the line number may be off; please fix all instances of Return(NULL)."""
435  return _GenericDiagnoser(
436      'NRNULL', 'Need to use ReturnNull',
437      [(clang_regex, diagnosis),
438       (gcc_regex, diagnosis % {'type': 'the right type'})],
439      msg)
440
441
442def _TypeInTemplatedBaseDiagnoser(msg):
443  """Diagnoses the TTB disease, given the error messages by the compiler."""
444
445  # This version works when the type is used as the mock function's return
446  # type.
447  gcc_4_3_1_regex_type_in_retval = (
448      r'In member function \'int .*\n' + _GCC_FILE_LINE_RE +
449      r'error: a function call cannot appear in a constant-expression')
450  gcc_4_4_0_regex_type_in_retval = (
451      r'error: a function call cannot appear in a constant-expression'
452      + _GCC_FILE_LINE_RE + r'error: template argument 1 is invalid\n')
453  # This version works when the type is used as the mock function's sole
454  # parameter type.
455  gcc_regex_type_of_sole_param = (
456      _GCC_FILE_LINE_RE +
457      r'error: \'(?P<type>.+)\' was not declared in this scope\n'
458      r'.*error: template argument 1 is invalid\n')
459  # This version works when the type is used as a parameter of a mock
460  # function that has multiple parameters.
461  gcc_regex_type_of_a_param = (
462      r'error: expected `;\' before \'::\' token\n'
463      + _GCC_FILE_LINE_RE +
464      r'error: \'(?P<type>.+)\' was not declared in this scope\n'
465      r'.*error: template argument 1 is invalid\n'
466      r'.*error: \'.+\' was not declared in this scope')
467  clang_regex_type_of_retval_or_sole_param = (
468      _CLANG_FILE_LINE_RE +
469      r'error: use of undeclared identifier \'(?P<type>.*)\'\n'
470      r'(.*\n)*?'
471      r'(?P=file):(?P=line):\d+: error: '
472      r'non-friend class member \'Result\' cannot have a qualified name'
473      )
474  clang_regex_type_of_a_param = (
475      _CLANG_FILE_LINE_RE +
476      r'error: C\+\+ requires a type specifier for all declarations\n'
477      r'(.*\n)*?'
478      r'(?P=file):(?P=line):(?P=column): error: '
479      r'C\+\+ requires a type specifier for all declarations'
480      )
481  clang_regex_unknown_type = (
482      _CLANG_FILE_LINE_RE +
483      r'error: unknown type name \'(?P<type>[^\']+)\''
484      )
485
486  diagnosis = """
487In a mock class template, types or typedefs defined in the base class
488template are *not* automatically visible.  This is how C++ works.  Before
489you can use a type or typedef named %(type)s defined in base class Base<T>, you
490need to make it visible.  One way to do it is:
491
492  typedef typename Base<T>::%(type)s %(type)s;"""
493
494  for diag in _GenericDiagnoser(
495      'TTB', 'Type in Template Base',
496      [(gcc_4_3_1_regex_type_in_retval, diagnosis % {'type': 'Foo'}),
497       (gcc_4_4_0_regex_type_in_retval, diagnosis % {'type': 'Foo'}),
498       (gcc_regex_type_of_sole_param, diagnosis),
499       (gcc_regex_type_of_a_param, diagnosis),
500       (clang_regex_type_of_retval_or_sole_param, diagnosis),
501       (clang_regex_type_of_a_param, diagnosis % {'type': 'Foo'})],
502      msg):
503    yield diag
504  # Avoid overlap with the NUS pattern.
505  for m in _FindAllMatches(clang_regex_unknown_type, msg):
506    type_ = m.groupdict()['type']
507    if type_ not in _COMMON_GMOCK_SYMBOLS:
508      yield ('TTB', 'Type in Template Base', diagnosis % m.groupdict())
509
510
511def _WrongMockMethodMacroDiagnoser(msg):
512  """Diagnoses the WMM disease, given the error messages by the compiler."""
513
514  gcc_regex = (_GCC_FILE_LINE_RE +
515               r'.*this_method_does_not_take_(?P<wrong_args>\d+)_argument.*\n'
516               r'.*\n'
517               r'.*candidates are.*FunctionMocker<[^>]+A(?P<args>\d+)\)>')
518  clang_regex = (_CLANG_NON_GMOCK_FILE_LINE_RE +
519                 r'error:.*array.*negative.*r?\n'
520                 r'(.*\n)*?'
521                 r'(?P=file):(?P=line):(?P=column): error: too few arguments '
522                 r'to function call, expected (?P<args>\d+), '
523                 r'have (?P<wrong_args>\d+)')
524  clang11_re = (_CLANG_NON_GMOCK_FILE_LINE_RE +
525                r'.*this_method_does_not_take_'
526                r'(?P<wrong_args>\d+)_argument.*')
527  diagnosis = """
528You are using MOCK_METHOD%(wrong_args)s to define a mock method that has
529%(args)s arguments. Use MOCK_METHOD%(args)s (or MOCK_CONST_METHOD%(args)s,
530MOCK_METHOD%(args)s_T, MOCK_CONST_METHOD%(args)s_T as appropriate) instead."""
531  return _GenericDiagnoser('WMM', 'Wrong MOCK_METHODn Macro',
532                           [(gcc_regex, diagnosis),
533                            (clang11_re, diagnosis % {'wrong_args': 'm',
534                                                      'args': 'n'}),
535                            (clang_regex, diagnosis)],
536                           msg)
537
538
539def _WrongParenPositionDiagnoser(msg):
540  """Diagnoses the WPP disease, given the error messages by the compiler."""
541
542  gcc_regex = (_GCC_FILE_LINE_RE +
543               r'error:.*testing::internal::MockSpec<.* has no member named \''
544               r'(?P<method>\w+)\'')
545  clang_regex = (_CLANG_NON_GMOCK_FILE_LINE_RE +
546                 r'error: no member named \'(?P<method>\w+)\' in '
547                 r'\'testing::internal::MockSpec<.*>\'')
548  diagnosis = """
549The closing parenthesis of ON_CALL or EXPECT_CALL should be *before*
550".%(method)s".  For example, you should write:
551  EXPECT_CALL(my_mock, Foo(_)).%(method)s(...);
552instead of:
553  EXPECT_CALL(my_mock, Foo(_).%(method)s(...));"""
554  return _GenericDiagnoser('WPP', 'Wrong Parenthesis Position',
555                           [(gcc_regex, diagnosis),
556                            (clang_regex, diagnosis)],
557                           msg)
558
559
560_DIAGNOSERS = [
561    _IncompleteByReferenceArgumentDiagnoser,
562    _MockObjectPointerDiagnoser,
563    _NeedToReturnNothingDiagnoser,
564    _NeedToReturnReferenceDiagnoser,
565    _NeedToReturnSomethingDiagnoser,
566    _NeedToUseReturnNullDiagnoser,
567    _NeedToUseSymbolDiagnoser,
568    _OverloadedFunctionActionDiagnoser,
569    _OverloadedFunctionMatcherDiagnoser,
570    _OverloadedMethodActionDiagnoser,
571    _TypeInTemplatedBaseDiagnoser,
572    _WrongMockMethodMacroDiagnoser,
573    _WrongParenPositionDiagnoser,
574    ]
575
576
577def Diagnose(msg):
578  """Generates all possible diagnoses given the compiler error message."""
579
580  msg = re.sub(r'\x1b\[[^m]*m', '', msg)  # Strips all color formatting.
581  # Assuming the string is using the UTF-8 encoding, replaces the left and
582  # the right single quote characters with apostrophes.
583  msg = re.sub(r'(\xe2\x80\x98|\xe2\x80\x99)', "'", msg)
584
585  diagnoses = []
586  for diagnoser in _DIAGNOSERS:
587    for diag in diagnoser(msg):
588      diagnosis = '[%s - %s]\n%s' % diag
589      if not diagnosis in diagnoses:
590        diagnoses.append(diagnosis)
591  return diagnoses
592
593
594def main():
595  print ('Google Mock Doctor v%s - '
596         'diagnoses problems in code using Google Mock.' % _VERSION)
597
598  if sys.stdin.isatty():
599    print ('Please copy and paste the compiler errors here.  Press c-D when '
600           'you are done:')
601  else:
602    print ('Waiting for compiler errors on stdin . . .')
603
604  msg = sys.stdin.read().strip()
605  diagnoses = Diagnose(msg)
606  count = len(diagnoses)
607  if not count:
608    print ("""
609Your compiler complained:
6108<------------------------------------------------------------
611%s
612------------------------------------------------------------>8
613
614Uh-oh, I'm not smart enough to figure out what the problem is. :-(
615However...
616If you send your source code and the compiler's error messages to
617%s, you can be helped and I can get smarter --
618win-win for us!""" % (msg, _EMAIL))
619  else:
620    print ('------------------------------------------------------------')
621    print ('Your code appears to have the following',)
622    if count > 1:
623      print ('%s diseases:' % (count,))
624    else:
625      print ('disease:')
626    i = 0
627    for d in diagnoses:
628      i += 1
629      if count > 1:
630        print ('\n#%s:' % (i,))
631      print (d)
632    print ("""
633How did I do?  If you think I'm wrong or unhelpful, please send your
634source code and the compiler's error messages to %s.
635Then you can be helped and I can get smarter -- I promise I won't be upset!""" %
636           _EMAIL)
637
638
639if __name__ == '__main__':
640  main()
641