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