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