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