Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "FindBadConstructsConsumer.h" | 5 #include "FindBadConstructsConsumer.h" |
| 6 | 6 |
| 7 #include "clang/Frontend/CompilerInstance.h" | 7 #include "clang/Frontend/CompilerInstance.h" |
| 8 #include "clang/AST/Attr.h" | 8 #include "clang/AST/Attr.h" |
| 9 #include "clang/Lex/Lexer.h" | 9 #include "clang/Lex/Lexer.h" |
| 10 #include "llvm/ADT/SmallPtrSet.h" | |
| 10 #include "llvm/Support/raw_ostream.h" | 11 #include "llvm/Support/raw_ostream.h" |
| 11 | 12 |
| 12 using namespace clang; | 13 using namespace clang; |
| 13 | 14 |
| 14 namespace chrome_checker { | 15 namespace chrome_checker { |
| 15 | 16 |
| 16 namespace { | 17 namespace { |
| 17 | 18 |
| 18 const char kMethodRequiresOverride[] = | 19 const char kMethodRequiresOverride[] = |
| 19 "[chromium-style] Overriding method must be marked with 'override' or " | 20 "[chromium-style] Overriding method must be marked with 'override' or " |
| (...skipping 71 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 91 return FixItHint::CreateRemoval(range); | 92 return FixItHint::CreateRemoval(range); |
| 92 } | 93 } |
| 93 | 94 |
| 94 bool IsPodOrTemplateType(const CXXRecordDecl& record) { | 95 bool IsPodOrTemplateType(const CXXRecordDecl& record) { |
| 95 return record.isPOD() || | 96 return record.isPOD() || |
| 96 record.getDescribedClassTemplate() || | 97 record.getDescribedClassTemplate() || |
| 97 record.getTemplateSpecializationKind() || | 98 record.getTemplateSpecializationKind() || |
| 98 record.isDependentType(); | 99 record.isDependentType(); |
| 99 } | 100 } |
| 100 | 101 |
| 102 // True if this class is likely to be created by user code rather than derived | |
| 103 // from. | |
| 104 bool IsLeafClass(const CXXRecordDecl& record) { | |
|
dcheng
2016/03/24 17:37:25
How accurate is this heuristic for determining lea
| |
| 105 if (record.isAbstract()) | |
| 106 return false; | |
| 107 bool found_protected = false; | |
| 108 for (const CXXConstructorDecl* ctor : record.ctors()) { | |
| 109 if (ctor->isDeleted()) { | |
| 110 continue; | |
| 111 } | |
| 112 switch (ctor->getAccess()) { | |
| 113 case AS_public: | |
| 114 return true; | |
| 115 case AS_protected: | |
| 116 found_protected = true; | |
| 117 break; | |
| 118 case AS_private: | |
| 119 break; | |
| 120 case AS_none: | |
| 121 // Shouldn't happen. | |
| 122 break; | |
| 123 } | |
| 124 } | |
| 125 // If all constructors are private, assume there's a public Create() function. | |
| 126 return !found_protected; | |
| 127 } | |
| 128 | |
| 129 // Returns the distance from r.begin() to r.end(), but stops if it reaches | |
| 130 // |at_most|. | |
| 131 template<typename Range> | |
| 132 int distance_at_most(const Range& r, int at_most) { | |
| 133 int result = 0; | |
| 134 for (auto it = r.begin(), e = r.end(); it != e && result < at_most; ++it) { | |
| 135 ++result; | |
| 136 } | |
| 137 return result; | |
| 138 } | |
| 139 | |
| 101 } // namespace | 140 } // namespace |
| 102 | 141 |
| 103 FindBadConstructsConsumer::FindBadConstructsConsumer(CompilerInstance& instance, | 142 FindBadConstructsConsumer::FindBadConstructsConsumer(CompilerInstance& instance, |
| 104 const Options& options) | 143 const Options& options) |
| 105 : ChromeClassTester(instance, options) { | 144 : ChromeClassTester(instance, options) { |
| 106 // Messages for virtual method specifiers. | 145 // Messages for virtual method specifiers. |
| 107 diag_method_requires_override_ = | 146 diag_method_requires_override_ = |
| 108 diagnostic().getCustomDiagID(getErrorLevel(), kMethodRequiresOverride); | 147 diagnostic().getCustomDiagID(getErrorLevel(), kMethodRequiresOverride); |
| 109 diag_redundant_virtual_specifier_ = | 148 diag_redundant_virtual_specifier_ = |
| 110 diagnostic().getCustomDiagID(getErrorLevel(), kRedundantVirtualSpecifier); | 149 diagnostic().getCustomDiagID(getErrorLevel(), kRedundantVirtualSpecifier); |
| (...skipping 47 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 158 // Only check for "heavy" constructors/destructors in header files; | 197 // Only check for "heavy" constructors/destructors in header files; |
| 159 // within implementation files, there is no performance cost. | 198 // within implementation files, there is no performance cost. |
| 160 | 199 |
| 161 // If this is a POD or a class template or a type dependent on a | 200 // If this is a POD or a class template or a type dependent on a |
| 162 // templated class, assume there's no ctor/dtor/virtual method | 201 // templated class, assume there's no ctor/dtor/virtual method |
| 163 // optimization that we should do. | 202 // optimization that we should do. |
| 164 if (!IsPodOrTemplateType(*record)) | 203 if (!IsPodOrTemplateType(*record)) |
| 165 CheckCtorDtorWeight(record_location, record); | 204 CheckCtorDtorWeight(record_location, record); |
| 166 } | 205 } |
| 167 | 206 |
| 168 bool warn_on_inline_bodies = !implementation_file; | |
| 169 // Check that all virtual methods are annotated with override or final. | 207 // Check that all virtual methods are annotated with override or final. |
| 170 // Note this could also apply to templates, but for some reason Clang | 208 // Note this could also apply to templates, but for some reason Clang |
| 171 // does not always see the "override", so we get false positives. | 209 // does not always see the "override", so we get false positives. |
| 172 // See http://llvm.org/bugs/show_bug.cgi?id=18440 and | 210 // See http://llvm.org/bugs/show_bug.cgi?id=18440 and |
| 173 // http://llvm.org/bugs/show_bug.cgi?id=21942 | 211 // http://llvm.org/bugs/show_bug.cgi?id=21942 |
| 174 if (!IsPodOrTemplateType(*record)) | 212 if (!IsPodOrTemplateType(*record)) |
| 175 CheckVirtualMethods(record_location, record, warn_on_inline_bodies); | 213 CheckVirtualMethods(record_location, record); |
| 176 | 214 |
| 177 CheckRefCountedDtors(record_location, record); | 215 CheckRefCountedDtors(record_location, record); |
| 178 | 216 |
| 179 CheckWeakPtrFactoryMembers(record_location, record); | 217 CheckWeakPtrFactoryMembers(record_location, record); |
| 180 } | 218 } |
| 181 | 219 |
| 182 void FindBadConstructsConsumer::CheckChromeEnum(SourceLocation enum_location, | 220 void FindBadConstructsConsumer::CheckChromeEnum(SourceLocation enum_location, |
| 183 EnumDecl* enum_decl) { | 221 EnumDecl* enum_decl) { |
| 184 if (!options_.check_enum_last_value) | 222 if (!options_.check_enum_last_value) |
| 185 return; | 223 return; |
| (...skipping 81 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 267 dtor_score += templated_base_classes * 9; | 305 dtor_score += templated_base_classes * 9; |
| 268 // Instantiating a template is an insta-hit. | 306 // Instantiating a template is an insta-hit. |
| 269 dtor_score += templated_non_trivial_member * 10; | 307 dtor_score += templated_non_trivial_member * 10; |
| 270 // The fourth normal class member should trigger the warning. | 308 // The fourth normal class member should trigger the warning. |
| 271 dtor_score += non_trivial_member * 3; | 309 dtor_score += non_trivial_member * 3; |
| 272 | 310 |
| 273 int ctor_score = dtor_score; | 311 int ctor_score = dtor_score; |
| 274 // You should be able to have 9 ints before we warn you. | 312 // You should be able to have 9 ints before we warn you. |
| 275 ctor_score += trivial_member; | 313 ctor_score += trivial_member; |
| 276 | 314 |
| 315 // For leaf classes, count the cost of emitting the vtable on Windows. | |
| 316 // (Windows because MSVC doesn't use the key-function optimization, so vtables | |
| 317 // are emitted more often.) The data behind this is in | |
| 318 // https://groups.google.com/a/chromium.org/d/topic/chromium-dev/rdF1mT3V_PM/d iscussion | |
| 319 if (options_.treat_virtuals_as_complexity && record->isPolymorphic() && | |
| 320 IsLeafClass(*record)) { | |
| 321 int virtual_score = 3; | |
| 322 | |
| 323 // For each non-overridden virtual function, add the length of its inline | |
| 324 // body, if any. | |
| 325 CXXFinalOverriderMap final_overriders; | |
| 326 llvm::SmallPtrSet<const Stmt*, 10> overriding_bodies; | |
| 327 record->getFinalOverriders(final_overriders); | |
| 328 for (const std::pair<const CXXMethodDecl*, OverridingMethods>& method : | |
|
dcheng
2016/03/24 17:37:25
Another way to write this might be to iterate over
| |
| 329 final_overriders) { | |
| 330 for (const auto& overrides : method.second) { | |
| 331 for (const UniqueVirtualMethod& override : overrides.second) { | |
| 332 if (override.Method->hasInlineBody()) { | |
| 333 if (const Stmt* body = override.Method->getBody()) { | |
| 334 // The same inline function can override multiple virtual | |
| 335 // functions in base classes, and we only want to count it once. | |
| 336 overriding_bodies.insert(body); | |
| 337 } | |
| 338 } | |
| 339 } | |
| 340 } | |
| 341 } | |
| 342 for (const Stmt* body : overriding_bodies) { | |
| 343 virtual_score += 1 + distance_at_most(body->children(), 10); | |
| 344 } | |
| 345 | |
| 346 ctor_score += virtual_score; | |
| 347 if (!record->getDestructor()->isVirtual()) { | |
| 348 dtor_score += virtual_score; | |
| 349 } | |
| 350 } | |
| 351 | |
| 277 if (ctor_score >= 10) { | 352 if (ctor_score >= 10) { |
| 278 if (!record->hasUserDeclaredConstructor()) { | 353 if (!record->hasUserDeclaredConstructor()) { |
| 279 emitWarning(record_location, | 354 emitWarning(record_location, |
| 280 "Complex class/struct needs an explicit out-of-line " | 355 "Complex class/struct needs an explicit out-of-line " |
| 281 "constructor."); | 356 "constructor."); |
| 282 } else { | 357 } else { |
| 283 // Iterate across all the constructors in this file and yell if we | 358 // Iterate across all the constructors in this file and yell if we |
| 284 // find one that tries to be inline. | 359 // find one that tries to be inline. |
| 285 for (CXXRecordDecl::ctor_iterator it = record->ctor_begin(); | 360 for (CXXRecordDecl::ctor_iterator it = record->ctor_begin(); |
| 286 it != record->ctor_end(); | 361 it != record->ctor_end(); |
| (...skipping 88 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 375 | 450 |
| 376 SuppressibleDiagnosticBuilder | 451 SuppressibleDiagnosticBuilder |
| 377 FindBadConstructsConsumer::ReportIfSpellingLocNotIgnored( | 452 FindBadConstructsConsumer::ReportIfSpellingLocNotIgnored( |
| 378 SourceLocation loc, | 453 SourceLocation loc, |
| 379 unsigned diagnostic_id) { | 454 unsigned diagnostic_id) { |
| 380 return SuppressibleDiagnosticBuilder( | 455 return SuppressibleDiagnosticBuilder( |
| 381 &diagnostic(), loc, diagnostic_id, | 456 &diagnostic(), loc, diagnostic_id, |
| 382 InBannedDirectory(instance().getSourceManager().getSpellingLoc(loc))); | 457 InBannedDirectory(instance().getSourceManager().getSpellingLoc(loc))); |
| 383 } | 458 } |
| 384 | 459 |
| 385 // Checks that virtual methods are correctly annotated, and have no body in a | 460 // Checks that virtual methods are correctly annotated. |
| 386 // header file. | |
| 387 void FindBadConstructsConsumer::CheckVirtualMethods( | 461 void FindBadConstructsConsumer::CheckVirtualMethods( |
| 388 SourceLocation record_location, | 462 SourceLocation record_location, |
| 389 CXXRecordDecl* record, | 463 CXXRecordDecl* record) { |
| 390 bool warn_on_inline_bodies) { | |
| 391 // Gmock objects trigger these for each MOCK_BLAH() macro used. So we have a | 464 // Gmock objects trigger these for each MOCK_BLAH() macro used. So we have a |
| 392 // trick to get around that. If a class has member variables whose types are | 465 // trick to get around that. If a class has member variables whose types are |
| 393 // in the "testing" namespace (which is how gmock works behind the scenes), | 466 // in the "testing" namespace (which is how gmock works behind the scenes), |
| 394 // there's a really high chance we won't care about these errors | 467 // there's a really high chance we won't care about these errors |
| 395 for (CXXRecordDecl::field_iterator it = record->field_begin(); | 468 for (CXXRecordDecl::field_iterator it = record->field_begin(); |
| 396 it != record->field_end(); | 469 it != record->field_end(); |
| 397 ++it) { | 470 ++it) { |
| 398 CXXRecordDecl* record_type = it->getTypeSourceInfo() | 471 CXXRecordDecl* record_type = it->getTypeSourceInfo() |
| 399 ->getTypeLoc() | 472 ->getTypeLoc() |
| 400 .getTypePtr() | 473 .getTypePtr() |
| (...skipping 10 matching lines...) Expand all Loading... | |
| 411 ++it) { | 484 ++it) { |
| 412 if (it->isCopyAssignmentOperator() || isa<CXXConstructorDecl>(*it)) { | 485 if (it->isCopyAssignmentOperator() || isa<CXXConstructorDecl>(*it)) { |
| 413 // Ignore constructors and assignment operators. | 486 // Ignore constructors and assignment operators. |
| 414 } else if (isa<CXXDestructorDecl>(*it) && | 487 } else if (isa<CXXDestructorDecl>(*it) && |
| 415 !record->hasUserDeclaredDestructor()) { | 488 !record->hasUserDeclaredDestructor()) { |
| 416 // Ignore non-user-declared destructors. | 489 // Ignore non-user-declared destructors. |
| 417 } else if (!it->isVirtual()) { | 490 } else if (!it->isVirtual()) { |
| 418 continue; | 491 continue; |
| 419 } else { | 492 } else { |
| 420 CheckVirtualSpecifiers(*it); | 493 CheckVirtualSpecifiers(*it); |
| 421 if (warn_on_inline_bodies) | |
| 422 CheckVirtualBodies(*it); | |
| 423 } | 494 } |
| 424 } | 495 } |
| 425 } | 496 } |
| 426 | 497 |
| 427 // Makes sure that virtual methods use the most appropriate specifier. If a | 498 // Makes sure that virtual methods use the most appropriate specifier. If a |
| 428 // virtual method overrides a method from a base class, only the override | 499 // virtual method overrides a method from a base class, only the override |
| 429 // specifier should be used. If the method should not be overridden by derived | 500 // specifier should be used. If the method should not be overridden by derived |
| 430 // classes, only the final specifier should be used. | 501 // classes, only the final specifier should be used. |
| 431 void FindBadConstructsConsumer::CheckVirtualSpecifiers( | 502 void FindBadConstructsConsumer::CheckVirtualSpecifiers( |
| 432 const CXXMethodDecl* method) { | 503 const CXXMethodDecl* method) { |
| (...skipping 81 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 514 } | 585 } |
| 515 | 586 |
| 516 if (final_attr && !is_override) { | 587 if (final_attr && !is_override) { |
| 517 ReportIfSpellingLocNotIgnored(method->getLocStart(), | 588 ReportIfSpellingLocNotIgnored(method->getLocStart(), |
| 518 diag_base_method_virtual_and_final_) | 589 diag_base_method_virtual_and_final_) |
| 519 << FixItRemovalForVirtual(manager, lang_opts, method) | 590 << FixItRemovalForVirtual(manager, lang_opts, method) |
| 520 << FixItHint::CreateRemoval(final_attr->getRange()); | 591 << FixItHint::CreateRemoval(final_attr->getRange()); |
| 521 } | 592 } |
| 522 } | 593 } |
| 523 | 594 |
| 524 void FindBadConstructsConsumer::CheckVirtualBodies( | |
| 525 const CXXMethodDecl* method) { | |
| 526 // Virtual methods should not have inline definitions beyond "{}". This | |
| 527 // only matters for header files. | |
| 528 if (method->hasBody() && method->hasInlineBody()) { | |
| 529 if (CompoundStmt* cs = dyn_cast<CompoundStmt>(method->getBody())) { | |
| 530 if (cs->size()) { | |
| 531 SourceLocation loc = cs->getLBracLoc(); | |
| 532 // CR_BEGIN_MSG_MAP_EX and BEGIN_SAFE_MSG_MAP_EX try to be compatible | |
| 533 // to BEGIN_MSG_MAP(_EX). So even though they are in chrome code, | |
| 534 // we can't easily fix them, so explicitly whitelist them here. | |
| 535 bool emit = true; | |
| 536 if (loc.isMacroID()) { | |
| 537 SourceManager& manager = instance().getSourceManager(); | |
| 538 if (InBannedDirectory(manager.getSpellingLoc(loc))) | |
| 539 emit = false; | |
| 540 else { | |
| 541 StringRef name = Lexer::getImmediateMacroName( | |
| 542 loc, manager, instance().getLangOpts()); | |
| 543 if (name == "CR_BEGIN_MSG_MAP_EX" || | |
| 544 name == "BEGIN_SAFE_MSG_MAP_EX") | |
| 545 emit = false; | |
| 546 } | |
| 547 } | |
| 548 if (emit) | |
| 549 emitWarning(loc, | |
| 550 "virtual methods with non-empty bodies shouldn't be " | |
| 551 "declared inline."); | |
| 552 } | |
| 553 } | |
| 554 } | |
| 555 } | |
| 556 | |
| 557 void FindBadConstructsConsumer::CountType(const Type* type, | 595 void FindBadConstructsConsumer::CountType(const Type* type, |
| 558 int* trivial_member, | 596 int* trivial_member, |
| 559 int* non_trivial_member, | 597 int* non_trivial_member, |
| 560 int* templated_non_trivial_member) { | 598 int* templated_non_trivial_member) { |
| 561 switch (type->getTypeClass()) { | 599 switch (type->getTypeClass()) { |
| 562 case Type::Record: { | 600 case Type::Record: { |
| 563 // Simplifying; the whole class isn't trivial if the dtor is, but | 601 // Simplifying; the whole class isn't trivial if the dtor is, but |
| 564 // we use this as a signal about complexity. | 602 // we use this as a signal about complexity. |
| 565 if (TypeHasNonTrivialDtor(type)) | 603 if (TypeHasNonTrivialDtor(type)) |
| 566 (*non_trivial_member)++; | 604 (*non_trivial_member)++; |
| (...skipping 299 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 866 // one of those, it means there is at least one member after a factory. | 904 // one of those, it means there is at least one member after a factory. |
| 867 if (weak_ptr_factory_location.isValid() && | 905 if (weak_ptr_factory_location.isValid() && |
| 868 !param_is_weak_ptr_factory_to_self) { | 906 !param_is_weak_ptr_factory_to_self) { |
| 869 diagnostic().Report(weak_ptr_factory_location, | 907 diagnostic().Report(weak_ptr_factory_location, |
| 870 diag_weak_ptr_factory_order_); | 908 diag_weak_ptr_factory_order_); |
| 871 } | 909 } |
| 872 } | 910 } |
| 873 } | 911 } |
| 874 | 912 |
| 875 } // namespace chrome_checker | 913 } // namespace chrome_checker |
| OLD | NEW |