Home | History | Annotate | Download | only in plugins
      1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #include "FindBadConstructsConsumer.h"
      6 
      7 #include "clang/Frontend/CompilerInstance.h"
      8 #include "clang/Lex/Lexer.h"
      9 #include "llvm/Support/raw_ostream.h"
     10 
     11 using namespace clang;
     12 
     13 namespace chrome_checker {
     14 
     15 namespace {
     16 
     17 const char kMethodRequiresOverride[] =
     18     "[chromium-style] Overriding method must be marked with OVERRIDE.";
     19 const char kMethodRequiresVirtual[] =
     20     "[chromium-style] Overriding method must have \"virtual\" keyword.";
     21 const char kNoExplicitDtor[] =
     22     "[chromium-style] Classes that are ref-counted should have explicit "
     23     "destructors that are declared protected or private.";
     24 const char kPublicDtor[] =
     25     "[chromium-style] Classes that are ref-counted should have "
     26     "destructors that are declared protected or private.";
     27 const char kProtectedNonVirtualDtor[] =
     28     "[chromium-style] Classes that are ref-counted and have non-private "
     29     "destructors should declare their destructor virtual.";
     30 const char kWeakPtrFactoryOrder[] =
     31     "[chromium-style] WeakPtrFactory members which refer to their outer class "
     32     "must be the last member in the outer class definition.";
     33 const char kBadLastEnumValue[] =
     34     "[chromium-style] _LAST/Last constants of enum types must have the maximal "
     35     "value for any constant of that type.";
     36 const char kNoteInheritance[] = "[chromium-style] %0 inherits from %1 here";
     37 const char kNoteImplicitDtor[] =
     38     "[chromium-style] No explicit destructor for %0 defined";
     39 const char kNotePublicDtor[] =
     40     "[chromium-style] Public destructor declared here";
     41 const char kNoteProtectedNonVirtualDtor[] =
     42     "[chromium-style] Protected non-virtual destructor declared here";
     43 
     44 bool TypeHasNonTrivialDtor(const Type* type) {
     45   if (const CXXRecordDecl* cxx_r = type->getPointeeCXXRecordDecl())
     46     return !cxx_r->hasTrivialDestructor();
     47 
     48   return false;
     49 }
     50 
     51 // Returns the underlying Type for |type| by expanding typedefs and removing
     52 // any namespace qualifiers. This is similar to desugaring, except that for
     53 // ElaboratedTypes, desugar will unwrap too much.
     54 const Type* UnwrapType(const Type* type) {
     55   if (const ElaboratedType* elaborated = dyn_cast<ElaboratedType>(type))
     56     return UnwrapType(elaborated->getNamedType().getTypePtr());
     57   if (const TypedefType* typedefed = dyn_cast<TypedefType>(type))
     58     return UnwrapType(typedefed->desugar().getTypePtr());
     59   return type;
     60 }
     61 
     62 }  // namespace
     63 
     64 FindBadConstructsConsumer::FindBadConstructsConsumer(CompilerInstance& instance,
     65                                                      const Options& options)
     66     : ChromeClassTester(instance), options_(options) {
     67   // Register warning/error messages.
     68   diag_method_requires_override_ =
     69       diagnostic().getCustomDiagID(getErrorLevel(), kMethodRequiresOverride);
     70   diag_method_requires_virtual_ =
     71       diagnostic().getCustomDiagID(getErrorLevel(), kMethodRequiresVirtual);
     72   diag_no_explicit_dtor_ =
     73       diagnostic().getCustomDiagID(getErrorLevel(), kNoExplicitDtor);
     74   diag_public_dtor_ =
     75       diagnostic().getCustomDiagID(getErrorLevel(), kPublicDtor);
     76   diag_protected_non_virtual_dtor_ =
     77       diagnostic().getCustomDiagID(getErrorLevel(), kProtectedNonVirtualDtor);
     78   diag_weak_ptr_factory_order_ =
     79       diagnostic().getCustomDiagID(getErrorLevel(), kWeakPtrFactoryOrder);
     80   diag_bad_enum_last_value_ =
     81       diagnostic().getCustomDiagID(getErrorLevel(), kBadLastEnumValue);
     82 
     83   // Registers notes to make it easier to interpret warnings.
     84   diag_note_inheritance_ =
     85       diagnostic().getCustomDiagID(DiagnosticsEngine::Note, kNoteInheritance);
     86   diag_note_implicit_dtor_ =
     87       diagnostic().getCustomDiagID(DiagnosticsEngine::Note, kNoteImplicitDtor);
     88   diag_note_public_dtor_ =
     89       diagnostic().getCustomDiagID(DiagnosticsEngine::Note, kNotePublicDtor);
     90   diag_note_protected_non_virtual_dtor_ = diagnostic().getCustomDiagID(
     91       DiagnosticsEngine::Note, kNoteProtectedNonVirtualDtor);
     92 }
     93 
     94 void FindBadConstructsConsumer::CheckChromeClass(SourceLocation record_location,
     95                                                  CXXRecordDecl* record) {
     96   bool implementation_file = InImplementationFile(record_location);
     97 
     98   if (!implementation_file) {
     99     // Only check for "heavy" constructors/destructors in header files;
    100     // within implementation files, there is no performance cost.
    101     CheckCtorDtorWeight(record_location, record);
    102   }
    103 
    104   bool warn_on_inline_bodies = !implementation_file;
    105 
    106   // Check that all virtual methods are marked accordingly with both
    107   // virtual and OVERRIDE.
    108   CheckVirtualMethods(record_location, record, warn_on_inline_bodies);
    109 
    110   CheckRefCountedDtors(record_location, record);
    111 
    112   if (options_.check_weak_ptr_factory_order)
    113     CheckWeakPtrFactoryMembers(record_location, record);
    114 }
    115 
    116 void FindBadConstructsConsumer::CheckChromeEnum(SourceLocation enum_location,
    117                                                 EnumDecl* enum_decl) {
    118   if (!options_.check_enum_last_value)
    119     return;
    120 
    121   bool got_one = false;
    122   bool is_signed = false;
    123   llvm::APSInt max_so_far;
    124   EnumDecl::enumerator_iterator iter;
    125   for (iter = enum_decl->enumerator_begin();
    126        iter != enum_decl->enumerator_end();
    127        ++iter) {
    128     llvm::APSInt current_value = iter->getInitVal();
    129     if (!got_one) {
    130       max_so_far = current_value;
    131       is_signed = current_value.isSigned();
    132       got_one = true;
    133     } else {
    134       if (is_signed != current_value.isSigned()) {
    135         // This only happens in some cases when compiling C (not C++) files,
    136         // so it is OK to bail out here.
    137         return;
    138       }
    139       if (current_value > max_so_far)
    140         max_so_far = current_value;
    141     }
    142   }
    143   for (iter = enum_decl->enumerator_begin();
    144        iter != enum_decl->enumerator_end();
    145        ++iter) {
    146     std::string name = iter->getNameAsString();
    147     if (((name.size() > 4 && name.compare(name.size() - 4, 4, "Last") == 0) ||
    148          (name.size() > 5 && name.compare(name.size() - 5, 5, "_LAST") == 0)) &&
    149         iter->getInitVal() < max_so_far) {
    150       diagnostic().Report(iter->getLocation(), diag_bad_enum_last_value_);
    151     }
    152   }
    153 }
    154 
    155 void FindBadConstructsConsumer::CheckCtorDtorWeight(
    156     SourceLocation record_location,
    157     CXXRecordDecl* record) {
    158   // We don't handle anonymous structs. If this record doesn't have a
    159   // name, it's of the form:
    160   //
    161   // struct {
    162   //   ...
    163   // } name_;
    164   if (record->getIdentifier() == NULL)
    165     return;
    166 
    167   // Count the number of templated base classes as a feature of whether the
    168   // destructor can be inlined.
    169   int templated_base_classes = 0;
    170   for (CXXRecordDecl::base_class_const_iterator it = record->bases_begin();
    171        it != record->bases_end();
    172        ++it) {
    173     if (it->getTypeSourceInfo()->getTypeLoc().getTypeLocClass() ==
    174         TypeLoc::TemplateSpecialization) {
    175       ++templated_base_classes;
    176     }
    177   }
    178 
    179   // Count the number of trivial and non-trivial member variables.
    180   int trivial_member = 0;
    181   int non_trivial_member = 0;
    182   int templated_non_trivial_member = 0;
    183   for (RecordDecl::field_iterator it = record->field_begin();
    184        it != record->field_end();
    185        ++it) {
    186     CountType(it->getType().getTypePtr(),
    187               &trivial_member,
    188               &non_trivial_member,
    189               &templated_non_trivial_member);
    190   }
    191 
    192   // Check to see if we need to ban inlined/synthesized constructors. Note
    193   // that the cutoffs here are kind of arbitrary. Scores over 10 break.
    194   int dtor_score = 0;
    195   // Deriving from a templated base class shouldn't be enough to trigger
    196   // the ctor warning, but if you do *anything* else, it should.
    197   //
    198   // TODO(erg): This is motivated by templated base classes that don't have
    199   // any data members. Somehow detect when templated base classes have data
    200   // members and treat them differently.
    201   dtor_score += templated_base_classes * 9;
    202   // Instantiating a template is an insta-hit.
    203   dtor_score += templated_non_trivial_member * 10;
    204   // The fourth normal class member should trigger the warning.
    205   dtor_score += non_trivial_member * 3;
    206 
    207   int ctor_score = dtor_score;
    208   // You should be able to have 9 ints before we warn you.
    209   ctor_score += trivial_member;
    210 
    211   if (ctor_score >= 10) {
    212     if (!record->hasUserDeclaredConstructor()) {
    213       emitWarning(record_location,
    214                   "Complex class/struct needs an explicit out-of-line "
    215                   "constructor.");
    216     } else {
    217       // Iterate across all the constructors in this file and yell if we
    218       // find one that tries to be inline.
    219       for (CXXRecordDecl::ctor_iterator it = record->ctor_begin();
    220            it != record->ctor_end();
    221            ++it) {
    222         if (it->hasInlineBody()) {
    223           if (it->isCopyConstructor() &&
    224               !record->hasUserDeclaredCopyConstructor()) {
    225             emitWarning(record_location,
    226                         "Complex class/struct needs an explicit out-of-line "
    227                         "copy constructor.");
    228           } else {
    229             emitWarning(it->getInnerLocStart(),
    230                         "Complex constructor has an inlined body.");
    231           }
    232         }
    233       }
    234     }
    235   }
    236 
    237   // The destructor side is equivalent except that we don't check for
    238   // trivial members; 20 ints don't need a destructor.
    239   if (dtor_score >= 10 && !record->hasTrivialDestructor()) {
    240     if (!record->hasUserDeclaredDestructor()) {
    241       emitWarning(record_location,
    242                   "Complex class/struct needs an explicit out-of-line "
    243                   "destructor.");
    244     } else if (CXXDestructorDecl* dtor = record->getDestructor()) {
    245       if (dtor->hasInlineBody()) {
    246         emitWarning(dtor->getInnerLocStart(),
    247                     "Complex destructor has an inline body.");
    248       }
    249     }
    250   }
    251 }
    252 
    253 void FindBadConstructsConsumer::CheckVirtualMethod(const CXXMethodDecl* method,
    254                                                    bool warn_on_inline_bodies) {
    255   if (!method->isVirtual())
    256     return;
    257 
    258   if (!method->isVirtualAsWritten()) {
    259     SourceLocation loc = method->getTypeSpecStartLoc();
    260     if (isa<CXXDestructorDecl>(method))
    261       loc = method->getInnerLocStart();
    262     SourceManager& manager = instance().getSourceManager();
    263     FullSourceLoc full_loc(loc, manager);
    264     SourceLocation spelling_loc = manager.getSpellingLoc(loc);
    265     diagnostic().Report(full_loc, diag_method_requires_virtual_)
    266         << FixItHint::CreateInsertion(spelling_loc, "virtual ");
    267   }
    268 
    269   // Virtual methods should not have inline definitions beyond "{}". This
    270   // only matters for header files.
    271   if (warn_on_inline_bodies && method->hasBody() && method->hasInlineBody()) {
    272     if (CompoundStmt* cs = dyn_cast<CompoundStmt>(method->getBody())) {
    273       if (cs->size()) {
    274         emitWarning(cs->getLBracLoc(),
    275                     "virtual methods with non-empty bodies shouldn't be "
    276                     "declared inline.");
    277       }
    278     }
    279   }
    280 }
    281 
    282 bool FindBadConstructsConsumer::InTestingNamespace(const Decl* record) {
    283   return GetNamespace(record).find("testing") != std::string::npos;
    284 }
    285 
    286 bool FindBadConstructsConsumer::IsMethodInBannedOrTestingNamespace(
    287     const CXXMethodDecl* method) {
    288   if (InBannedNamespace(method))
    289     return true;
    290   for (CXXMethodDecl::method_iterator i = method->begin_overridden_methods();
    291        i != method->end_overridden_methods();
    292        ++i) {
    293     const CXXMethodDecl* overridden = *i;
    294     if (IsMethodInBannedOrTestingNamespace(overridden) ||
    295         InTestingNamespace(overridden)) {
    296       return true;
    297     }
    298   }
    299 
    300   return false;
    301 }
    302 
    303 void FindBadConstructsConsumer::CheckOverriddenMethod(
    304     const CXXMethodDecl* method) {
    305   if (!method->size_overridden_methods() || method->getAttr<OverrideAttr>())
    306     return;
    307 
    308   if (isa<CXXDestructorDecl>(method) || method->isPure())
    309     return;
    310 
    311   if (IsMethodInBannedOrTestingNamespace(method))
    312     return;
    313 
    314   SourceManager& manager = instance().getSourceManager();
    315   SourceRange type_info_range =
    316       method->getTypeSourceInfo()->getTypeLoc().getSourceRange();
    317   FullSourceLoc loc(type_info_range.getBegin(), manager);
    318 
    319   // Build the FixIt insertion point after the end of the method definition,
    320   // including any const-qualifiers and attributes, and before the opening
    321   // of the l-curly-brace (if inline) or the semi-color (if a declaration).
    322   SourceLocation spelling_end =
    323       manager.getSpellingLoc(type_info_range.getEnd());
    324   if (spelling_end.isValid()) {
    325     SourceLocation token_end =
    326         Lexer::getLocForEndOfToken(spelling_end, 0, manager, LangOptions());
    327     diagnostic().Report(token_end, diag_method_requires_override_)
    328         << FixItHint::CreateInsertion(token_end, " OVERRIDE");
    329   } else {
    330     diagnostic().Report(loc, diag_method_requires_override_);
    331   }
    332 }
    333 
    334 // Makes sure there is a "virtual" keyword on virtual methods.
    335 //
    336 // Gmock objects trigger these for each MOCK_BLAH() macro used. So we have a
    337 // trick to get around that. If a class has member variables whose types are
    338 // in the "testing" namespace (which is how gmock works behind the scenes),
    339 // there's a really high chance we won't care about these errors
    340 void FindBadConstructsConsumer::CheckVirtualMethods(
    341     SourceLocation record_location,
    342     CXXRecordDecl* record,
    343     bool warn_on_inline_bodies) {
    344   for (CXXRecordDecl::field_iterator it = record->field_begin();
    345        it != record->field_end();
    346        ++it) {
    347     CXXRecordDecl* record_type = it->getTypeSourceInfo()
    348                                      ->getTypeLoc()
    349                                      .getTypePtr()
    350                                      ->getAsCXXRecordDecl();
    351     if (record_type) {
    352       if (InTestingNamespace(record_type)) {
    353         return;
    354       }
    355     }
    356   }
    357 
    358   for (CXXRecordDecl::method_iterator it = record->method_begin();
    359        it != record->method_end();
    360        ++it) {
    361     if (it->isCopyAssignmentOperator() || isa<CXXConstructorDecl>(*it)) {
    362       // Ignore constructors and assignment operators.
    363     } else if (isa<CXXDestructorDecl>(*it) &&
    364                !record->hasUserDeclaredDestructor()) {
    365       // Ignore non-user-declared destructors.
    366     } else {
    367       CheckVirtualMethod(*it, warn_on_inline_bodies);
    368       CheckOverriddenMethod(*it);
    369     }
    370   }
    371 }
    372 
    373 void FindBadConstructsConsumer::CountType(const Type* type,
    374                                           int* trivial_member,
    375                                           int* non_trivial_member,
    376                                           int* templated_non_trivial_member) {
    377   switch (type->getTypeClass()) {
    378     case Type::Record: {
    379       // Simplifying; the whole class isn't trivial if the dtor is, but
    380       // we use this as a signal about complexity.
    381       if (TypeHasNonTrivialDtor(type))
    382         (*trivial_member)++;
    383       else
    384         (*non_trivial_member)++;
    385       break;
    386     }
    387     case Type::TemplateSpecialization: {
    388       TemplateName name =
    389           dyn_cast<TemplateSpecializationType>(type)->getTemplateName();
    390       bool whitelisted_template = false;
    391 
    392       // HACK: I'm at a loss about how to get the syntax checker to get
    393       // whether a template is exterened or not. For the first pass here,
    394       // just do retarded string comparisons.
    395       if (TemplateDecl* decl = name.getAsTemplateDecl()) {
    396         std::string base_name = decl->getNameAsString();
    397         if (base_name == "basic_string")
    398           whitelisted_template = true;
    399       }
    400 
    401       if (whitelisted_template)
    402         (*non_trivial_member)++;
    403       else
    404         (*templated_non_trivial_member)++;
    405       break;
    406     }
    407     case Type::Elaborated: {
    408       CountType(dyn_cast<ElaboratedType>(type)->getNamedType().getTypePtr(),
    409                 trivial_member,
    410                 non_trivial_member,
    411                 templated_non_trivial_member);
    412       break;
    413     }
    414     case Type::Typedef: {
    415       while (const TypedefType* TT = dyn_cast<TypedefType>(type)) {
    416         type = TT->getDecl()->getUnderlyingType().getTypePtr();
    417       }
    418       CountType(type,
    419                 trivial_member,
    420                 non_trivial_member,
    421                 templated_non_trivial_member);
    422       break;
    423     }
    424     default: {
    425       // Stupid assumption: anything we see that isn't the above is one of
    426       // the 20 integer types.
    427       (*trivial_member)++;
    428       break;
    429     }
    430   }
    431 }
    432 
    433 // Check |record| for issues that are problematic for ref-counted types.
    434 // Note that |record| may not be a ref-counted type, but a base class for
    435 // a type that is.
    436 // If there are issues, update |loc| with the SourceLocation of the issue
    437 // and returns appropriately, or returns None if there are no issues.
    438 FindBadConstructsConsumer::RefcountIssue
    439 FindBadConstructsConsumer::CheckRecordForRefcountIssue(
    440     const CXXRecordDecl* record,
    441     SourceLocation& loc) {
    442   if (!record->hasUserDeclaredDestructor()) {
    443     loc = record->getLocation();
    444     return ImplicitDestructor;
    445   }
    446 
    447   if (CXXDestructorDecl* dtor = record->getDestructor()) {
    448     if (dtor->getAccess() == AS_public) {
    449       loc = dtor->getInnerLocStart();
    450       return PublicDestructor;
    451     }
    452   }
    453 
    454   return None;
    455 }
    456 
    457 // Adds either a warning or error, based on the current handling of
    458 // -Werror.
    459 DiagnosticsEngine::Level FindBadConstructsConsumer::getErrorLevel() {
    460   return diagnostic().getWarningsAsErrors() ? DiagnosticsEngine::Error
    461                                             : DiagnosticsEngine::Warning;
    462 }
    463 
    464 // Returns true if |base| specifies one of the Chromium reference counted
    465 // classes (base::RefCounted / base::RefCountedThreadSafe).
    466 bool FindBadConstructsConsumer::IsRefCountedCallback(
    467     const CXXBaseSpecifier* base,
    468     CXXBasePath& path,
    469     void* user_data) {
    470   FindBadConstructsConsumer* self =
    471       static_cast<FindBadConstructsConsumer*>(user_data);
    472 
    473   const TemplateSpecializationType* base_type =
    474       dyn_cast<TemplateSpecializationType>(
    475           UnwrapType(base->getType().getTypePtr()));
    476   if (!base_type) {
    477     // Base-most definition is not a template, so this cannot derive from
    478     // base::RefCounted. However, it may still be possible to use with a
    479     // scoped_refptr<> and support ref-counting, so this is not a perfect
    480     // guarantee of safety.
    481     return false;
    482   }
    483 
    484   TemplateName name = base_type->getTemplateName();
    485   if (TemplateDecl* decl = name.getAsTemplateDecl()) {
    486     std::string base_name = decl->getNameAsString();
    487 
    488     // Check for both base::RefCounted and base::RefCountedThreadSafe.
    489     if (base_name.compare(0, 10, "RefCounted") == 0 &&
    490         self->GetNamespace(decl) == "base") {
    491       return true;
    492     }
    493   }
    494 
    495   return false;
    496 }
    497 
    498 // Returns true if |base| specifies a class that has a public destructor,
    499 // either explicitly or implicitly.
    500 bool FindBadConstructsConsumer::HasPublicDtorCallback(
    501     const CXXBaseSpecifier* base,
    502     CXXBasePath& path,
    503     void* user_data) {
    504   // Only examine paths that have public inheritance, as they are the
    505   // only ones which will result in the destructor potentially being
    506   // exposed. This check is largely redundant, as Chromium code should be
    507   // exclusively using public inheritance.
    508   if (path.Access != AS_public)
    509     return false;
    510 
    511   CXXRecordDecl* record =
    512       dyn_cast<CXXRecordDecl>(base->getType()->getAs<RecordType>()->getDecl());
    513   SourceLocation unused;
    514   return None != CheckRecordForRefcountIssue(record, unused);
    515 }
    516 
    517 // Outputs a C++ inheritance chain as a diagnostic aid.
    518 void FindBadConstructsConsumer::PrintInheritanceChain(const CXXBasePath& path) {
    519   for (CXXBasePath::const_iterator it = path.begin(); it != path.end(); ++it) {
    520     diagnostic().Report(it->Base->getLocStart(), diag_note_inheritance_)
    521         << it->Class << it->Base->getType();
    522   }
    523 }
    524 
    525 unsigned FindBadConstructsConsumer::DiagnosticForIssue(RefcountIssue issue) {
    526   switch (issue) {
    527     case ImplicitDestructor:
    528       return diag_no_explicit_dtor_;
    529     case PublicDestructor:
    530       return diag_public_dtor_;
    531     case None:
    532       assert(false && "Do not call DiagnosticForIssue with issue None");
    533       return 0;
    534   }
    535   assert(false);
    536   return 0;
    537 }
    538 
    539 // Check |record| to determine if it has any problematic refcounting
    540 // issues and, if so, print them as warnings/errors based on the current
    541 // value of getErrorLevel().
    542 //
    543 // If |record| is a C++ class, and if it inherits from one of the Chromium
    544 // ref-counting classes (base::RefCounted / base::RefCountedThreadSafe),
    545 // ensure that there are no public destructors in the class hierarchy. This
    546 // is to guard against accidentally stack-allocating a RefCounted class or
    547 // sticking it in a non-ref-counted container (like scoped_ptr<>).
    548 void FindBadConstructsConsumer::CheckRefCountedDtors(
    549     SourceLocation record_location,
    550     CXXRecordDecl* record) {
    551   // Skip anonymous structs.
    552   if (record->getIdentifier() == NULL)
    553     return;
    554 
    555   // Determine if the current type is even ref-counted.
    556   CXXBasePaths refcounted_path;
    557   if (!record->lookupInBases(&FindBadConstructsConsumer::IsRefCountedCallback,
    558                              this,
    559                              refcounted_path)) {
    560     return;  // Class does not derive from a ref-counted base class.
    561   }
    562 
    563   // Easy check: Check to see if the current type is problematic.
    564   SourceLocation loc;
    565   RefcountIssue issue = CheckRecordForRefcountIssue(record, loc);
    566   if (issue != None) {
    567     diagnostic().Report(loc, DiagnosticForIssue(issue));
    568     PrintInheritanceChain(refcounted_path.front());
    569     return;
    570   }
    571   if (CXXDestructorDecl* dtor =
    572           refcounted_path.begin()->back().Class->getDestructor()) {
    573     if (dtor->getAccess() == AS_protected && !dtor->isVirtual()) {
    574       loc = dtor->getInnerLocStart();
    575       diagnostic().Report(loc, diag_protected_non_virtual_dtor_);
    576       return;
    577     }
    578   }
    579 
    580   // Long check: Check all possible base classes for problematic
    581   // destructors. This checks for situations involving multiple
    582   // inheritance, where the ref-counted class may be implementing an
    583   // interface that has a public or implicit destructor.
    584   //
    585   // struct SomeInterface {
    586   //   virtual void DoFoo();
    587   // };
    588   //
    589   // struct RefCountedInterface
    590   //    : public base::RefCounted<RefCountedInterface>,
    591   //      public SomeInterface {
    592   //  private:
    593   //   friend class base::Refcounted<RefCountedInterface>;
    594   //   virtual ~RefCountedInterface() {}
    595   // };
    596   //
    597   // While RefCountedInterface is "safe", in that its destructor is
    598   // private, it's possible to do the following "unsafe" code:
    599   //   scoped_refptr<RefCountedInterface> some_class(
    600   //       new RefCountedInterface);
    601   //   // Calls SomeInterface::~SomeInterface(), which is unsafe.
    602   //   delete static_cast<SomeInterface*>(some_class.get());
    603   if (!options_.check_base_classes)
    604     return;
    605 
    606   // Find all public destructors. This will record the class hierarchy
    607   // that leads to the public destructor in |dtor_paths|.
    608   CXXBasePaths dtor_paths;
    609   if (!record->lookupInBases(&FindBadConstructsConsumer::HasPublicDtorCallback,
    610                              this,
    611                              dtor_paths)) {
    612     return;
    613   }
    614 
    615   for (CXXBasePaths::const_paths_iterator it = dtor_paths.begin();
    616        it != dtor_paths.end();
    617        ++it) {
    618     // The record with the problem will always be the last record
    619     // in the path, since it is the record that stopped the search.
    620     const CXXRecordDecl* problem_record = dyn_cast<CXXRecordDecl>(
    621         it->back().Base->getType()->getAs<RecordType>()->getDecl());
    622 
    623     issue = CheckRecordForRefcountIssue(problem_record, loc);
    624 
    625     if (issue == ImplicitDestructor) {
    626       diagnostic().Report(record_location, diag_no_explicit_dtor_);
    627       PrintInheritanceChain(refcounted_path.front());
    628       diagnostic().Report(loc, diag_note_implicit_dtor_) << problem_record;
    629       PrintInheritanceChain(*it);
    630     } else if (issue == PublicDestructor) {
    631       diagnostic().Report(record_location, diag_public_dtor_);
    632       PrintInheritanceChain(refcounted_path.front());
    633       diagnostic().Report(loc, diag_note_public_dtor_);
    634       PrintInheritanceChain(*it);
    635     }
    636   }
    637 }
    638 
    639 // Check for any problems with WeakPtrFactory class members. This currently
    640 // only checks that any WeakPtrFactory<T> member of T appears as the last
    641 // data member in T. We could consider checking for bad uses of
    642 // WeakPtrFactory to refer to other data members, but that would require
    643 // looking at the initializer list in constructors to see what the factory
    644 // points to.
    645 // Note, if we later add other unrelated checks of data members, we should
    646 // consider collapsing them in to one loop to avoid iterating over the data
    647 // members more than once.
    648 void FindBadConstructsConsumer::CheckWeakPtrFactoryMembers(
    649     SourceLocation record_location,
    650     CXXRecordDecl* record) {
    651   // Skip anonymous structs.
    652   if (record->getIdentifier() == NULL)
    653     return;
    654 
    655   // Iterate through members of the class.
    656   RecordDecl::field_iterator iter(record->field_begin()),
    657       the_end(record->field_end());
    658   SourceLocation weak_ptr_factory_location;  // Invalid initially.
    659   for (; iter != the_end; ++iter) {
    660     // If we enter the loop but have already seen a matching WeakPtrFactory,
    661     // it means there is at least one member after the factory.
    662     if (weak_ptr_factory_location.isValid()) {
    663       diagnostic().Report(weak_ptr_factory_location,
    664                           diag_weak_ptr_factory_order_);
    665     }
    666     const TemplateSpecializationType* template_spec_type =
    667         iter->getType().getTypePtr()->getAs<TemplateSpecializationType>();
    668     if (template_spec_type) {
    669       const TemplateDecl* template_decl =
    670           template_spec_type->getTemplateName().getAsTemplateDecl();
    671       if (template_decl && template_spec_type->getNumArgs() >= 1) {
    672         if (template_decl->getNameAsString().compare("WeakPtrFactory") == 0 &&
    673             GetNamespace(template_decl) == "base") {
    674           const TemplateArgument& arg = template_spec_type->getArg(0);
    675           if (arg.getAsType().getTypePtr()->getAsCXXRecordDecl() ==
    676               record->getTypeForDecl()->getAsCXXRecordDecl()) {
    677             weak_ptr_factory_location = iter->getLocation();
    678           }
    679         }
    680       }
    681     }
    682   }
    683 }
    684 
    685 }  // namespace chrome_checker
    686