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