• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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  diagnosis = """
179You are using a Return() action in a function that returns a reference to
180%(type)s.  Please use ReturnRef() instead."""
181  return _GenericDiagnoser('NRR', 'Need to Return Reference',
182                           [(clang_regex, diagnosis),
183                            (gcc_regex, diagnosis % {'type': 'a type'})],
184                           msg)
185
186
187def _NeedToReturnSomethingDiagnoser(msg):
188  """Diagnoses the NRS disease, given the error messages by the compiler."""
189
190  gcc_regex = (_GCC_FILE_LINE_RE + r'(instantiated from here\n.'
191               r'*gmock.*actions\.h.*error: void value not ignored)'
192               r'|(error: control reaches end of non-void function)')
193  clang_regex1 = (_CLANG_FILE_LINE_RE +
194                  r'error: cannot initialize return object '
195                  r'of type \'Result\' \(aka \'(?P<return_type>.*)\'\) '
196                  r'with an rvalue of type \'void\'')
197  clang_regex2 = (_CLANG_FILE_LINE_RE +
198                  r'error: cannot initialize return object '
199                  r'of type \'(?P<return_type>.*)\' '
200                  r'with an rvalue of type \'void\'')
201  diagnosis = """
202You are using an action that returns void, but it needs to return
203%(return_type)s.  Please tell it *what* to return.  Perhaps you can use
204the pattern DoAll(some_action, Return(some_value))?"""
205  return _GenericDiagnoser(
206      'NRS',
207      'Need to Return Something',
208      [(gcc_regex, diagnosis % {'return_type': '*something*'}),
209       (clang_regex1, diagnosis),
210       (clang_regex2, diagnosis)],
211      msg)
212
213
214def _NeedToReturnNothingDiagnoser(msg):
215  """Diagnoses the NRN disease, given the error messages by the compiler."""
216
217  gcc_regex = (_GCC_FILE_LINE_RE + r'instantiated from here\n'
218               r'.*gmock-actions\.h.*error: instantiation of '
219               r'\'testing::internal::ReturnAction<R>::Impl<F>::value_\' '
220               r'as type \'void\'')
221  clang_regex1 = (r'error: field has incomplete type '
222                  r'\'Result\' \(aka \'void\'\)(\r)?\n'
223                  r'(.*\n)*?' +
224                  _CLANG_NON_GMOCK_FILE_LINE_RE + r'note: in instantiation '
225                  r'of function template specialization '
226                  r'\'testing::internal::ReturnAction<(?P<return_type>.*)>'
227                  r'::operator Action<void \(.*\)>\' requested here')
228  clang_regex2 = (r'error: field has incomplete type '
229                  r'\'Result\' \(aka \'void\'\)(\r)?\n'
230                  r'(.*\n)*?' +
231                  _CLANG_NON_GMOCK_FILE_LINE_RE + r'note: in instantiation '
232                  r'of function template specialization '
233                  r'\'testing::internal::DoBothAction<.*>'
234                  r'::operator Action<(?P<return_type>.*) \(.*\)>\' '
235                  r'requested here')
236  diagnosis = """
237You are using an action that returns %(return_type)s, but it needs to return
238void.  Please use a void-returning action instead.
239
240All actions but the last in DoAll(...) must return void.  Perhaps you need
241to re-arrange the order of actions in a DoAll(), if you are using one?"""
242  return _GenericDiagnoser(
243      'NRN',
244      'Need to Return Nothing',
245      [(gcc_regex, diagnosis % {'return_type': '*something*'}),
246       (clang_regex1, diagnosis),
247       (clang_regex2, diagnosis)],
248      msg)
249
250
251def _IncompleteByReferenceArgumentDiagnoser(msg):
252  """Diagnoses the IBRA disease, given the error messages by the compiler."""
253
254  gcc_regex = (_GCC_FILE_LINE_RE + r'instantiated from here\n'
255               r'.*gtest-printers\.h.*error: invalid application of '
256               r'\'sizeof\' to incomplete type \'(?P<type>.*)\'')
257
258  clang_regex = (r'.*gtest-printers\.h.*error: invalid application of '
259                 r'\'sizeof\' to an incomplete type '
260                 r'\'(?P<type>.*)( const)?\'\r?\n'
261                 r'(.*\n)*?' +
262                 _CLANG_NON_GMOCK_FILE_LINE_RE +
263                 r'note: in instantiation of member function '
264                 r'\'testing::internal2::TypeWithoutFormatter<.*>::'
265                 r'PrintValue\' requested here')
266  diagnosis = """
267In order to mock this function, Google Mock needs to see the definition
268of type "%(type)s" - declaration alone is not enough.  Either #include
269the header that defines it, or change the argument to be passed
270by pointer."""
271
272  return _GenericDiagnoser('IBRA', 'Incomplete By-Reference Argument Type',
273                           [(gcc_regex, diagnosis),
274                            (clang_regex, diagnosis)],
275                           msg)
276
277
278def _OverloadedFunctionMatcherDiagnoser(msg):
279  """Diagnoses the OFM disease, given the error messages by the compiler."""
280
281  gcc_regex = (_GCC_FILE_LINE_RE + r'error: no matching function for '
282               r'call to \'Truly\(<unresolved overloaded function type>\)')
283  clang_regex = (_CLANG_FILE_LINE_RE + r'error: no matching function for '
284                 r'call to \'Truly')
285  diagnosis = """
286The argument you gave to Truly() is an overloaded function.  Please tell
287your compiler which overloaded version you want to use.
288
289For example, if you want to use the version whose signature is
290  bool Foo(int n);
291you should write
292  Truly(static_cast<bool (*)(int n)>(Foo))"""
293  return _GenericDiagnoser('OFM', 'Overloaded Function Matcher',
294                           [(gcc_regex, diagnosis),
295                            (clang_regex, diagnosis)],
296                           msg)
297
298
299def _OverloadedFunctionActionDiagnoser(msg):
300  """Diagnoses the OFA disease, given the error messages by the compiler."""
301
302  gcc_regex = (_GCC_FILE_LINE_RE + r'error: no matching function for call to '
303               r'\'Invoke\(<unresolved overloaded function type>')
304  clang_regex = (_CLANG_FILE_LINE_RE + r'error: no matching '
305                 r'function for call to \'Invoke\'\r?\n'
306                 r'(.*\n)*?'
307                 r'.*\bgmock-\w+-actions\.h:\d+:\d+:\s+'
308                 r'note: candidate template ignored:\s+'
309                 r'couldn\'t infer template argument \'FunctionImpl\'')
310  diagnosis = """
311Function you are passing to Invoke is overloaded.  Please tell your compiler
312which overloaded version you want to use.
313
314For example, if you want to use the version whose signature is
315  bool MyFunction(int n, double x);
316you should write something like
317  Invoke(static_cast<bool (*)(int n, double x)>(MyFunction))"""
318  return _GenericDiagnoser('OFA', 'Overloaded Function Action',
319                           [(gcc_regex, diagnosis),
320                            (clang_regex, diagnosis)],
321                           msg)
322
323
324def _OverloadedMethodActionDiagnoser(msg):
325  """Diagnoses the OMA disease, given the error messages by the compiler."""
326
327  gcc_regex = (_GCC_FILE_LINE_RE + r'error: no matching function for '
328               r'call to \'Invoke\(.+, <unresolved overloaded function '
329               r'type>\)')
330  clang_regex = (_CLANG_FILE_LINE_RE + r'error: no matching function '
331                 r'for call to \'Invoke\'\r?\n'
332                 r'(.*\n)*?'
333                 r'.*\bgmock-\w+-actions\.h:\d+:\d+: '
334                 r'note: candidate function template not viable: '
335                 r'requires .*, but 2 (arguments )?were provided')
336  diagnosis = """
337The second argument you gave to Invoke() is an overloaded method.  Please
338tell your compiler which overloaded version you want to use.
339
340For example, if you want to use the version whose signature is
341  class Foo {
342    ...
343    bool Bar(int n, double x);
344  };
345you should write something like
346  Invoke(foo, static_cast<bool (Foo::*)(int n, double x)>(&Foo::Bar))"""
347  return _GenericDiagnoser('OMA', 'Overloaded Method Action',
348                           [(gcc_regex, diagnosis),
349                            (clang_regex, diagnosis)],
350                           msg)
351
352
353def _MockObjectPointerDiagnoser(msg):
354  """Diagnoses the MOP disease, given the error messages by the compiler."""
355
356  gcc_regex = (_GCC_FILE_LINE_RE + r'error: request for member '
357               r'\'gmock_(?P<method>.+)\' in \'(?P<mock_object>.+)\', '
358               r'which is of non-class type \'(.*::)*(?P<class_name>.+)\*\'')
359  clang_regex = (_CLANG_FILE_LINE_RE + r'error: member reference type '
360                 r'\'(?P<class_name>.*?) *\' is a pointer; '
361                 r'maybe you meant to use \'->\'\?')
362  diagnosis = """
363The first argument to ON_CALL() and EXPECT_CALL() must be a mock *object*,
364not a *pointer* to it.  Please write '*(%(mock_object)s)' instead of
365'%(mock_object)s' as your first argument.
366
367For example, given the mock class:
368
369  class %(class_name)s : public ... {
370    ...
371    MOCK_METHOD0(%(method)s, ...);
372  };
373
374and the following mock instance:
375
376  %(class_name)s* mock_ptr = ...
377
378you should use the EXPECT_CALL like this:
379
380  EXPECT_CALL(*mock_ptr, %(method)s(...));"""
381
382  return _GenericDiagnoser(
383      'MOP',
384      'Mock Object Pointer',
385      [(gcc_regex, diagnosis),
386       (clang_regex, diagnosis % {'mock_object': 'mock_object',
387                                  'method': 'method',
388                                  'class_name': '%(class_name)s'})],
389       msg)
390
391
392def _NeedToUseSymbolDiagnoser(msg):
393  """Diagnoses the NUS disease, given the error messages by the compiler."""
394
395  gcc_regex = (_GCC_FILE_LINE_RE + r'error: \'(?P<symbol>.+)\' '
396               r'(was not declared in this scope|has not been declared)')
397  clang_regex = (_CLANG_FILE_LINE_RE +
398                 r'error: (use of undeclared identifier|unknown type name|'
399                 r'no template named) \'(?P<symbol>[^\']+)\'')
400  diagnosis = """
401'%(symbol)s' is defined by Google Mock in the testing namespace.
402Did you forget to write
403  using testing::%(symbol)s;
404?"""
405  for m in (list(_FindAllMatches(gcc_regex, msg)) +
406            list(_FindAllMatches(clang_regex, msg))):
407    symbol = m.groupdict()['symbol']
408    if symbol in _COMMON_GMOCK_SYMBOLS:
409      yield ('NUS', 'Need to Use Symbol', diagnosis % m.groupdict())
410
411
412def _NeedToUseReturnNullDiagnoser(msg):
413  """Diagnoses the NRNULL disease, given the error messages by the compiler."""
414
415  gcc_regex = ('instantiated from \'testing::internal::ReturnAction<R>'
416               '::operator testing::Action<Func>\(\) const.*\n' +
417               _GCC_FILE_LINE_RE + r'instantiated from here\n'
418               r'.*error: no matching function for call to \'ImplicitCast_\('
419               r'(:?long )?int&\)')
420  clang_regex = (r'\bgmock-actions.h:.* error: no matching function for '
421                 r'call to \'ImplicitCast_\'\r?\n'
422                 r'(.*\n)*?' +
423                 _CLANG_NON_GMOCK_FILE_LINE_RE + r'note: in instantiation '
424                 r'of function template specialization '
425                 r'\'testing::internal::ReturnAction<(int|long)>::operator '
426                 r'Action<(?P<type>.*)\(\)>\' requested here')
427  diagnosis = """
428You are probably calling Return(NULL) and the compiler isn't sure how to turn
429NULL into %(type)s. Use ReturnNull() instead.
430Note: the line number may be off; please fix all instances of Return(NULL)."""
431  return _GenericDiagnoser(
432      'NRNULL', 'Need to use ReturnNull',
433      [(clang_regex, diagnosis),
434       (gcc_regex, diagnosis % {'type': 'the right type'})],
435      msg)
436
437
438def _TypeInTemplatedBaseDiagnoser(msg):
439  """Diagnoses the TTB disease, given the error messages by the compiler."""
440
441  # This version works when the type is used as the mock function's return
442  # type.
443  gcc_4_3_1_regex_type_in_retval = (
444      r'In member function \'int .*\n' + _GCC_FILE_LINE_RE +
445      r'error: a function call cannot appear in a constant-expression')
446  gcc_4_4_0_regex_type_in_retval = (
447      r'error: a function call cannot appear in a constant-expression'
448      + _GCC_FILE_LINE_RE + r'error: template argument 1 is invalid\n')
449  # This version works when the type is used as the mock function's sole
450  # parameter type.
451  gcc_regex_type_of_sole_param = (
452      _GCC_FILE_LINE_RE +
453      r'error: \'(?P<type>.+)\' was not declared in this scope\n'
454      r'.*error: template argument 1 is invalid\n')
455  # This version works when the type is used as a parameter of a mock
456  # function that has multiple parameters.
457  gcc_regex_type_of_a_param = (
458      r'error: expected `;\' before \'::\' token\n'
459      + _GCC_FILE_LINE_RE +
460      r'error: \'(?P<type>.+)\' was not declared in this scope\n'
461      r'.*error: template argument 1 is invalid\n'
462      r'.*error: \'.+\' was not declared in this scope')
463  clang_regex_type_of_retval_or_sole_param = (
464      _CLANG_FILE_LINE_RE +
465      r'error: use of undeclared identifier \'(?P<type>.*)\'\n'
466      r'(.*\n)*?'
467      r'(?P=file):(?P=line):\d+: error: '
468      r'non-friend class member \'Result\' cannot have a qualified name'
469      )
470  clang_regex_type_of_a_param = (
471      _CLANG_FILE_LINE_RE +
472      r'error: C\+\+ requires a type specifier for all declarations\n'
473      r'(.*\n)*?'
474      r'(?P=file):(?P=line):(?P=column): error: '
475      r'C\+\+ requires a type specifier for all declarations'
476      )
477  clang_regex_unknown_type = (
478      _CLANG_FILE_LINE_RE +
479      r'error: unknown type name \'(?P<type>[^\']+)\''
480      )
481
482  diagnosis = """
483In a mock class template, types or typedefs defined in the base class
484template are *not* automatically visible.  This is how C++ works.  Before
485you can use a type or typedef named %(type)s defined in base class Base<T>, you
486need to make it visible.  One way to do it is:
487
488  typedef typename Base<T>::%(type)s %(type)s;"""
489
490  for diag in _GenericDiagnoser(
491      'TTB', 'Type in Template Base',
492      [(gcc_4_3_1_regex_type_in_retval, diagnosis % {'type': 'Foo'}),
493       (gcc_4_4_0_regex_type_in_retval, diagnosis % {'type': 'Foo'}),
494       (gcc_regex_type_of_sole_param, diagnosis),
495       (gcc_regex_type_of_a_param, diagnosis),
496       (clang_regex_type_of_retval_or_sole_param, diagnosis),
497       (clang_regex_type_of_a_param, diagnosis % {'type': 'Foo'})],
498      msg):
499    yield diag
500  # Avoid overlap with the NUS pattern.
501  for m in _FindAllMatches(clang_regex_unknown_type, msg):
502    type_ = m.groupdict()['type']
503    if type_ not in _COMMON_GMOCK_SYMBOLS:
504      yield ('TTB', 'Type in Template Base', diagnosis % m.groupdict())
505
506
507def _WrongMockMethodMacroDiagnoser(msg):
508  """Diagnoses the WMM disease, given the error messages by the compiler."""
509
510  gcc_regex = (_GCC_FILE_LINE_RE +
511               r'.*this_method_does_not_take_(?P<wrong_args>\d+)_argument.*\n'
512               r'.*\n'
513               r'.*candidates are.*FunctionMocker<[^>]+A(?P<args>\d+)\)>')
514  clang_regex = (_CLANG_NON_GMOCK_FILE_LINE_RE +
515                 r'error:.*array.*negative.*r?\n'
516                 r'(.*\n)*?'
517                 r'(?P=file):(?P=line):(?P=column): error: too few arguments '
518                 r'to function call, expected (?P<args>\d+), '
519                 r'have (?P<wrong_args>\d+)')
520  diagnosis = """
521You are using MOCK_METHOD%(wrong_args)s to define a mock method that has
522%(args)s arguments. Use MOCK_METHOD%(args)s (or MOCK_CONST_METHOD%(args)s,
523MOCK_METHOD%(args)s_T, MOCK_CONST_METHOD%(args)s_T as appropriate) instead."""
524  return _GenericDiagnoser('WMM', 'Wrong MOCK_METHODn Macro',
525                           [(gcc_regex, diagnosis),
526                            (clang_regex, diagnosis)],
527                           msg)
528
529
530def _WrongParenPositionDiagnoser(msg):
531  """Diagnoses the WPP disease, given the error messages by the compiler."""
532
533  gcc_regex = (_GCC_FILE_LINE_RE +
534               r'error:.*testing::internal::MockSpec<.* has no member named \''
535               r'(?P<method>\w+)\'')
536  clang_regex = (_CLANG_NON_GMOCK_FILE_LINE_RE +
537                 r'error: no member named \'(?P<method>\w+)\' in '
538                 r'\'testing::internal::MockSpec<.*>\'')
539  diagnosis = """
540The closing parenthesis of ON_CALL or EXPECT_CALL should be *before*
541".%(method)s".  For example, you should write:
542  EXPECT_CALL(my_mock, Foo(_)).%(method)s(...);
543instead of:
544  EXPECT_CALL(my_mock, Foo(_).%(method)s(...));"""
545  return _GenericDiagnoser('WPP', 'Wrong Parenthesis Position',
546                           [(gcc_regex, diagnosis),
547                            (clang_regex, diagnosis)],
548                           msg)
549
550
551_DIAGNOSERS = [
552    _IncompleteByReferenceArgumentDiagnoser,
553    _MockObjectPointerDiagnoser,
554    _NeedToReturnNothingDiagnoser,
555    _NeedToReturnReferenceDiagnoser,
556    _NeedToReturnSomethingDiagnoser,
557    _NeedToUseReturnNullDiagnoser,
558    _NeedToUseSymbolDiagnoser,
559    _OverloadedFunctionActionDiagnoser,
560    _OverloadedFunctionMatcherDiagnoser,
561    _OverloadedMethodActionDiagnoser,
562    _TypeInTemplatedBaseDiagnoser,
563    _WrongMockMethodMacroDiagnoser,
564    _WrongParenPositionDiagnoser,
565    ]
566
567
568def Diagnose(msg):
569  """Generates all possible diagnoses given the compiler error message."""
570
571  msg = re.sub(r'\x1b\[[^m]*m', '', msg)  # Strips all color formatting.
572  # Assuming the string is using the UTF-8 encoding, replaces the left and
573  # the right single quote characters with apostrophes.
574  msg = re.sub(r'(\xe2\x80\x98|\xe2\x80\x99)', "'", msg)
575
576  diagnoses = []
577  for diagnoser in _DIAGNOSERS:
578    for diag in diagnoser(msg):
579      diagnosis = '[%s - %s]\n%s' % diag
580      if not diagnosis in diagnoses:
581        diagnoses.append(diagnosis)
582  return diagnoses
583
584
585def main():
586  print ('Google Mock Doctor v%s - '
587         'diagnoses problems in code using Google Mock.' % _VERSION)
588
589  if sys.stdin.isatty():
590    print ('Please copy and paste the compiler errors here.  Press c-D when '
591           'you are done:')
592  else:
593    print 'Waiting for compiler errors on stdin . . .'
594
595  msg = sys.stdin.read().strip()
596  diagnoses = Diagnose(msg)
597  count = len(diagnoses)
598  if not count:
599    print ("""
600Your compiler complained:
6018<------------------------------------------------------------
602%s
603------------------------------------------------------------>8
604
605Uh-oh, I'm not smart enough to figure out what the problem is. :-(
606However...
607If you send your source code and the compiler's error messages to
608%s, you can be helped and I can get smarter --
609win-win for us!""" % (msg, _EMAIL))
610  else:
611    print '------------------------------------------------------------'
612    print 'Your code appears to have the following',
613    if count > 1:
614      print '%s diseases:' % (count,)
615    else:
616      print 'disease:'
617    i = 0
618    for d in diagnoses:
619      i += 1
620      if count > 1:
621        print '\n#%s:' % (i,)
622      print d
623    print ("""
624How did I do?  If you think I'm wrong or unhelpful, please send your
625source code and the compiler's error messages to %s.
626Then you can be helped and I can get smarter -- I promise I won't be upset!""" %
627           _EMAIL)
628
629
630if __name__ == '__main__':
631  main()
632