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+):(\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 .*, but 2 (arguments )?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 +
    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.
    402 Did 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 
    412 def _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 = """
    428 You are probably calling Return(NULL) and the compiler isn't sure how to turn
    429 NULL into %(type)s. Use ReturnNull() instead.
    430 Note: 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 
    438 def _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 = """
    483 In a mock class template, types or typedefs defined in the base class
    484 template are *not* automatically visible.  This is how C++ works.  Before
    485 you can use a type or typedef named %(type)s defined in base class Base<T>, you
    486 need 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 
    507 def _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 = """
    521 You 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,
    523 MOCK_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 
    530 def _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 = """
    540 The 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(...);
    543 instead 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 
    568 def 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 
    585 def 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 ("""
    600 Your compiler complained:
    601 8<------------------------------------------------------------
    602 %s
    603 ------------------------------------------------------------>8
    604 
    605 Uh-oh, I'm not smart enough to figure out what the problem is. :-(
    606 However...
    607 If you send your source code and the compiler's error messages to
    608 %s, you can be helped and I can get smarter --
    609 win-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 ("""
    624 How did I do?  If you think I'm wrong or unhelpful, please send your
    625 source code and the compiler's error messages to %s.
    626 Then you can be helped and I can get smarter -- I promise I won't be upset!""" %
    627            _EMAIL)
    628 
    629 
    630 if __name__ == '__main__':
    631   main()
    632