Home | History | Annotate | Download | only in scripts
      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 (at] google.com (Zhanyong Wan)'
     35 
     36 import re
     37 import sys
     38 
     39 _VERSION = '1.0.3'
     40 
     41 _EMAIL = 'googlemock (at] 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 
    137 def _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 
    144 def _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 
    166 def _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 = """
    179 You 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 
    187 def _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 = """
    202 You 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
    204 the 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 
    214 def _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 = """
    237 You are using an action that returns %(return_type)s, but it needs to return
    238 void.  Please use a void-returning action instead.
    239 
    240 All actions but the last in DoAll(...) must return void.  Perhaps you need
    241 to 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 
    251 def _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 = """
    267 In order to mock this function, Google Mock needs to see the definition
    268 of type "%(type)s" - declaration alone is not enough.  Either #include
    269 the header that defines it, or change the argument to be passed
    270 by pointer."""
    271 
    272   return _GenericDiagnoser('IBRA', 'Incomplete By-Reference Argument Type',
    273                            [(gcc_regex, diagnosis),
    274                             (clang_regex, diagnosis)],
    275                            msg)
    276 
    277 
    278 def _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 = """
    286 The argument you gave to Truly() is an overloaded function.  Please tell
    287 your compiler which overloaded version you want to use.
    288 
    289 For example, if you want to use the version whose signature is
    290   bool Foo(int n);
    291 you 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 
    299 def _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 = """
    311 Function you are passing to Invoke is overloaded.  Please tell your compiler
    312 which overloaded version you want to use.
    313 
    314 For example, if you want to use the version whose signature is
    315   bool MyFunction(int n, double x);
    316 you 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 
    324 def _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 = """
    337 The second argument you gave to Invoke() is an overloaded method.  Please
    338 tell your compiler which overloaded version you want to use.
    339 
    340 For example, if you want to use the version whose signature is
    341   class Foo {
    342     ...
    343     bool Bar(int n, double x);
    344   };
    345 you 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 
    353 def _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 = """
    363 The first argument to ON_CALL() and EXPECT_CALL() must be a mock *object*,
    364 not a *pointer* to it.  Please write '*(%(mock_object)s)' instead of
    365 '%(mock_object)s' as your first argument.
    366 
    367 For example, given the mock class:
    368 
    369   class %(class_name)s : public ... {
    370     ...
    371     MOCK_METHOD0(%(method)s, ...);
    372   };
    373 
    374 and the following mock instance:
    375 
    376   %(class_name)s* mock_ptr = ...
    377 
    378 you 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 
    392 def _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.
    401 Did 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 
    411 def _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 = """
    427 You are probably calling Return(NULL) and the compiler isn't sure how to turn
    428 NULL into %(type)s. Use ReturnNull() instead.
    429 Note: 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 
    437 def _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 = """
    478 In a mock class template, types or typedefs defined in the base class
    479 template are *not* automatically visible.  This is how C++ works.  Before
    480 you can use a type or typedef named %(type)s defined in base class Base<T>, you
    481 need 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 
    496 def _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 = """
    510 You 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,
    512 MOCK_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 
    519 def _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 = """
    529 The 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(...);
    532 instead 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 
    557 def 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 
    571 def 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 ("""
    586 Your compiler complained:
    587 8<------------------------------------------------------------
    588 %s
    589 ------------------------------------------------------------>8
    590 
    591 Uh-oh, I'm not smart enough to figure out what the problem is. :-(
    592 However...
    593 If you send your source code and the compiler's error messages to
    594 %s, you can be helped and I can get smarter --
    595 win-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 ("""
    610 How did I do?  If you think I'm wrong or unhelpful, please send your
    611 source code and the compiler's error messages to %s.
    612 Then you can be helped and I can get smarter -- I promise I won't be upset!""" %
    613            _EMAIL)
    614 
    615 
    616 if __name__ == '__main__':
    617   main()
    618