• 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+):\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 1 argument, but 2 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 + r'error: use of undeclared identifier '
398                 r'\'(?P<symbol>.+)\'')
399  diagnosis = """
400'%(symbol)s' is defined by Google Mock in the testing namespace.
401Did you forget to write
402  using testing::%(symbol)s;
403?"""
404  for m in (list(_FindAllMatches(gcc_regex, msg)) +
405            list(_FindAllMatches(clang_regex, msg))):
406    symbol = m.groupdict()['symbol']
407    if symbol in _COMMON_GMOCK_SYMBOLS:
408      yield ('NUS', 'Need to Use Symbol', diagnosis % m.groupdict())
409
410
411def _NeedToUseReturnNullDiagnoser(msg):
412  """Diagnoses the NRNULL disease, given the error messages by the compiler."""
413
414  gcc_regex = ('instantiated from \'testing::internal::ReturnAction<R>'
415               '::operator testing::Action<Func>\(\) const.*\n' +
416               _GCC_FILE_LINE_RE + r'instantiated from here\n'
417               r'.*error: no matching function for call to \'ImplicitCast_\('
418               r'long int&\)')
419  clang_regex = (r'\bgmock-actions.h:.* error: no matching function for '
420                 r'call to \'ImplicitCast_\'\r?\n'
421                 r'(.*\n)*?' +
422                 _CLANG_NON_GMOCK_FILE_LINE_RE + r'note: in instantiation '
423                 r'of function template specialization '
424                 r'\'testing::internal::ReturnAction<long>::operator '
425                 r'Action<(?P<type>.*)\(\)>\' requested here')
426  diagnosis = """
427You are probably calling Return(NULL) and the compiler isn't sure how to turn
428NULL into %(type)s. Use ReturnNull() instead.
429Note: the line number may be off; please fix all instances of Return(NULL)."""
430  return _GenericDiagnoser(
431      'NRNULL', 'Need to use ReturnNull',
432      [(clang_regex, diagnosis),
433       (gcc_regex, diagnosis % {'type': 'the right type'})],
434      msg)
435
436
437def _TypeInTemplatedBaseDiagnoser(msg):
438  """Diagnoses the TTB disease, given the error messages by the compiler."""
439
440  # This version works when the type is used as the mock function's return
441  # type.
442  gcc_4_3_1_regex_type_in_retval = (
443      r'In member function \'int .*\n' + _GCC_FILE_LINE_RE +
444      r'error: a function call cannot appear in a constant-expression')
445  gcc_4_4_0_regex_type_in_retval = (
446      r'error: a function call cannot appear in a constant-expression'
447      + _GCC_FILE_LINE_RE + r'error: template argument 1 is invalid\n')
448  # This version works when the type is used as the mock function's sole
449  # parameter type.
450  gcc_regex_type_of_sole_param = (
451      _GCC_FILE_LINE_RE +
452      r'error: \'(?P<type>.+)\' was not declared in this scope\n'
453      r'.*error: template argument 1 is invalid\n')
454  # This version works when the type is used as a parameter of a mock
455  # function that has multiple parameters.
456  gcc_regex_type_of_a_param = (
457      r'error: expected `;\' before \'::\' token\n'
458      + _GCC_FILE_LINE_RE +
459      r'error: \'(?P<type>.+)\' was not declared in this scope\n'
460      r'.*error: template argument 1 is invalid\n'
461      r'.*error: \'.+\' was not declared in this scope')
462  clang_regex_type_of_retval_or_sole_param = (
463      _CLANG_FILE_LINE_RE +
464      r'error: use of undeclared identifier \'(?P<type>.*)\'\n'
465      r'(.*\n)*?'
466      r'(?P=file):(?P=line):\d+: error: '
467      r'non-friend class member \'Result\' cannot have a qualified name'
468      )
469  clang_regex_type_of_a_param = (
470      _CLANG_FILE_LINE_RE +
471      r'error: C\+\+ requires a type specifier for all declarations\n'
472      r'(.*\n)*?'
473      r'(?P=file):(?P=line):(?P=column): error: '
474      r'C\+\+ requires a type specifier for all declarations'
475      )
476
477  diagnosis = """
478In a mock class template, types or typedefs defined in the base class
479template are *not* automatically visible.  This is how C++ works.  Before
480you can use a type or typedef named %(type)s defined in base class Base<T>, you
481need to make it visible.  One way to do it is:
482
483  typedef typename Base<T>::%(type)s %(type)s;"""
484
485  return _GenericDiagnoser(
486      'TTB', 'Type in Template Base',
487      [(gcc_4_3_1_regex_type_in_retval, diagnosis % {'type': 'Foo'}),
488       (gcc_4_4_0_regex_type_in_retval, diagnosis % {'type': 'Foo'}),
489       (gcc_regex_type_of_sole_param, diagnosis),
490       (gcc_regex_type_of_a_param, diagnosis),
491       (clang_regex_type_of_retval_or_sole_param, diagnosis),
492       (clang_regex_type_of_a_param, diagnosis % {'type': 'Foo'})],
493      msg)
494
495
496def _WrongMockMethodMacroDiagnoser(msg):
497  """Diagnoses the WMM disease, given the error messages by the compiler."""
498
499  gcc_regex = (_GCC_FILE_LINE_RE +
500               r'.*this_method_does_not_take_(?P<wrong_args>\d+)_argument.*\n'
501               r'.*\n'
502               r'.*candidates are.*FunctionMocker<[^>]+A(?P<args>\d+)\)>')
503  clang_regex = (_CLANG_NON_GMOCK_FILE_LINE_RE +
504                 r'error:.*array.*negative.*r?\n'
505                 r'(.*\n)*?'
506                 r'(?P=file):(?P=line):(?P=column): error: too few arguments '
507                 r'to function call, expected (?P<args>\d+), '
508                 r'have (?P<wrong_args>\d+)')
509  diagnosis = """
510You are using MOCK_METHOD%(wrong_args)s to define a mock method that has
511%(args)s arguments. Use MOCK_METHOD%(args)s (or MOCK_CONST_METHOD%(args)s,
512MOCK_METHOD%(args)s_T, MOCK_CONST_METHOD%(args)s_T as appropriate) instead."""
513  return _GenericDiagnoser('WMM', 'Wrong MOCK_METHODn Macro',
514                           [(gcc_regex, diagnosis),
515                            (clang_regex, diagnosis)],
516                           msg)
517
518
519def _WrongParenPositionDiagnoser(msg):
520  """Diagnoses the WPP disease, given the error messages by the compiler."""
521
522  gcc_regex = (_GCC_FILE_LINE_RE +
523               r'error:.*testing::internal::MockSpec<.* has no member named \''
524               r'(?P<method>\w+)\'')
525  clang_regex = (_CLANG_NON_GMOCK_FILE_LINE_RE +
526                 r'error: no member named \'(?P<method>\w+)\' in '
527                 r'\'testing::internal::MockSpec<.*>\'')
528  diagnosis = """
529The closing parenthesis of ON_CALL or EXPECT_CALL should be *before*
530".%(method)s".  For example, you should write:
531  EXPECT_CALL(my_mock, Foo(_)).%(method)s(...);
532instead of:
533  EXPECT_CALL(my_mock, Foo(_).%(method)s(...));"""
534  return _GenericDiagnoser('WPP', 'Wrong Parenthesis Position',
535                           [(gcc_regex, diagnosis),
536                            (clang_regex, diagnosis)],
537                           msg)
538
539
540_DIAGNOSERS = [
541    _IncompleteByReferenceArgumentDiagnoser,
542    _MockObjectPointerDiagnoser,
543    _NeedToReturnNothingDiagnoser,
544    _NeedToReturnReferenceDiagnoser,
545    _NeedToReturnSomethingDiagnoser,
546    _NeedToUseReturnNullDiagnoser,
547    _NeedToUseSymbolDiagnoser,
548    _OverloadedFunctionActionDiagnoser,
549    _OverloadedFunctionMatcherDiagnoser,
550    _OverloadedMethodActionDiagnoser,
551    _TypeInTemplatedBaseDiagnoser,
552    _WrongMockMethodMacroDiagnoser,
553    _WrongParenPositionDiagnoser,
554    ]
555
556
557def Diagnose(msg):
558  """Generates all possible diagnoses given the compiler error message."""
559
560  msg = re.sub(r'\x1b\[[^m]*m', '', msg)  # Strips all color formatting.
561
562  diagnoses = []
563  for diagnoser in _DIAGNOSERS:
564    for diag in diagnoser(msg):
565      diagnosis = '[%s - %s]\n%s' % diag
566      if not diagnosis in diagnoses:
567        diagnoses.append(diagnosis)
568  return diagnoses
569
570
571def main():
572  print ('Google Mock Doctor v%s - '
573         'diagnoses problems in code using Google Mock.' % _VERSION)
574
575  if sys.stdin.isatty():
576    print ('Please copy and paste the compiler errors here.  Press c-D when '
577           'you are done:')
578  else:
579    print 'Waiting for compiler errors on stdin . . .'
580
581  msg = sys.stdin.read().strip()
582  diagnoses = Diagnose(msg)
583  count = len(diagnoses)
584  if not count:
585    print ("""
586Your compiler complained:
5878<------------------------------------------------------------
588%s
589------------------------------------------------------------>8
590
591Uh-oh, I'm not smart enough to figure out what the problem is. :-(
592However...
593If you send your source code and the compiler's error messages to
594%s, you can be helped and I can get smarter --
595win-win for us!""" % (msg, _EMAIL))
596  else:
597    print '------------------------------------------------------------'
598    print 'Your code appears to have the following',
599    if count > 1:
600      print '%s diseases:' % (count,)
601    else:
602      print 'disease:'
603    i = 0
604    for d in diagnoses:
605      i += 1
606      if count > 1:
607        print '\n#%s:' % (i,)
608      print d
609    print ("""
610How did I do?  If you think I'm wrong or unhelpful, please send your
611source code and the compiler's error messages to %s.
612Then you can be helped and I can get smarter -- I promise I won't be upset!""" %
613           _EMAIL)
614
615
616if __name__ == '__main__':
617  main()
618