| OLD | NEW |
| 1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2011 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 // This file defines a bunch of recurring problems in the Chromium C++ code. | 5 // This file defines a bunch of recurring problems in the Chromium C++ code. |
| 6 // | 6 // |
| 7 // Checks that are implemented: | 7 // Checks that are implemented: |
| 8 // - Constructors/Destructors should not be inlined if they are of a complex | 8 // - Constructors/Destructors should not be inlined if they are of a complex |
| 9 // class type. | 9 // class type. |
| 10 // - Missing "virtual" keywords on methods that should be virtual. | 10 // - Missing "virtual" keywords on methods that should be virtual. |
| 11 // - Non-annotated overriding virtual methods. | 11 // - Non-annotated overriding virtual methods. |
| 12 // - Virtual methods with nonempty implementations in their headers. | 12 // - Virtual methods with nonempty implementations in their headers. |
| 13 // | 13 // - Classes that derive from base::RefCounted / base::RefCountedThreadSafe |
| 14 // Things that are still TODO: | 14 // should have protected or private destructors. |
| 15 // - Deriving from base::RefCounted and friends should mandate non-public | |
| 16 // destructors. | |
| 17 | 15 |
| 18 #include "clang/Frontend/FrontendPluginRegistry.h" | 16 #include "clang/Frontend/FrontendPluginRegistry.h" |
| 19 #include "clang/AST/ASTConsumer.h" | 17 #include "clang/AST/ASTConsumer.h" |
| 20 #include "clang/AST/AST.h" | 18 #include "clang/AST/AST.h" |
| 19 #include "clang/AST/CXXInheritance.h" |
| 21 #include "clang/AST/TypeLoc.h" | 20 #include "clang/AST/TypeLoc.h" |
| 22 #include "clang/Basic/SourceManager.h" | 21 #include "clang/Basic/SourceManager.h" |
| 23 #include "clang/Frontend/CompilerInstance.h" | 22 #include "clang/Frontend/CompilerInstance.h" |
| 24 #include "llvm/Support/raw_ostream.h" | 23 #include "llvm/Support/raw_ostream.h" |
| 25 | 24 |
| 26 #include "ChromeClassTester.h" | 25 #include "ChromeClassTester.h" |
| 27 | 26 |
| 28 using namespace clang; | 27 using namespace clang; |
| 29 | 28 |
| 30 namespace { | 29 namespace { |
| 31 | 30 |
| 32 bool TypeHasNonTrivialDtor(const Type* type) { | 31 bool TypeHasNonTrivialDtor(const Type* type) { |
| 33 if (const CXXRecordDecl* cxx_r = type->getCXXRecordDeclForPointerType()) | 32 if (const CXXRecordDecl* cxx_r = type->getCXXRecordDeclForPointerType()) |
| 34 return cxx_r->hasTrivialDestructor(); | 33 return cxx_r->hasTrivialDestructor(); |
| 35 | 34 |
| 36 return false; | 35 return false; |
| 37 } | 36 } |
| 38 | 37 |
| 39 // Searches for constructs that we know we don't want in the Chromium code base. | 38 // Searches for constructs that we know we don't want in the Chromium code base. |
| 40 class FindBadConstructsConsumer : public ChromeClassTester { | 39 class FindBadConstructsConsumer : public ChromeClassTester { |
| 41 public: | 40 public: |
| 42 FindBadConstructsConsumer(CompilerInstance& instance) | 41 FindBadConstructsConsumer(CompilerInstance& instance) |
| 43 : ChromeClassTester(instance) {} | 42 : ChromeClassTester(instance) {} |
| 44 | 43 |
| 45 virtual void CheckChromeClass(const SourceLocation& record_location, | 44 virtual void CheckChromeClass(const SourceLocation& record_location, |
| 46 CXXRecordDecl* record) { | 45 CXXRecordDecl* record) { |
| 47 CheckCtorDtorWeight(record_location, record); | 46 bool implementation_file = InImplementationFile(record_location); |
| 48 CheckVirtualMethods(record_location, record); | 47 |
| 48 if (!implementation_file) { |
| 49 // Only check for "heavy" constructors/destructors in header files; |
| 50 // within implementation files, there is no performance cost. |
| 51 CheckCtorDtorWeight(record_location, record); |
| 52 |
| 53 // Check that all virtual methods are marked accordingly with both |
| 54 // virtual and OVERRIDE. |
| 55 CheckVirtualMethods(record_location, record); |
| 56 } |
| 57 |
| 58 CheckRefCountedDtors(record_location, record); |
| 59 } |
| 60 |
| 61 // Returns true if |base| specifies one of the Chromium reference counted |
| 62 // classes (base::RefCounted / base::RefCountedThreadSafe). |user_data| is |
| 63 // ignored. |
| 64 static bool IsRefCountedCallback(const CXXBaseSpecifier* base, |
| 65 CXXBasePath& path, |
| 66 void* user_data) { |
| 67 FindBadConstructsConsumer* self = |
| 68 static_cast<FindBadConstructsConsumer*>(user_data); |
| 69 if (base->getTypeSourceInfo()->getTypeLoc().getTypeLocClass() != |
| 70 TypeLoc::TemplateSpecialization) { |
| 71 return false; |
| 72 } |
| 73 |
| 74 const TemplateSpecializationType* base_type = |
| 75 dyn_cast<TemplateSpecializationType>(base->getType()); |
| 76 TemplateName name = base_type->getTemplateName(); |
| 77 |
| 78 if (TemplateDecl* decl = name.getAsTemplateDecl()) { |
| 79 std::string base_name = decl->getNameAsString(); |
| 80 if (base_name.compare(0, 10, "RefCounted") == 0 && |
| 81 self->GetNamespace(decl) == "base") { |
| 82 return true; |
| 83 } |
| 84 } |
| 85 return false; |
| 86 } |
| 87 |
| 88 // Prints errors if the destructor of a RefCounted class is public. |
| 89 void CheckRefCountedDtors(const SourceLocation& record_location, |
| 90 CXXRecordDecl* record) { |
| 91 // Skip anonymous structs. |
| 92 if (record->getIdentifier() == NULL) |
| 93 return; |
| 94 |
| 95 CXXBasePaths paths; |
| 96 if (!record->lookupInBases( |
| 97 &FindBadConstructsConsumer::IsRefCountedCallback, this, paths)) { |
| 98 return; // Class does not derive from a ref-counted base class. |
| 99 } |
| 100 |
| 101 if (!record->hasUserDeclaredDestructor()) { |
| 102 emitWarning( |
| 103 record_location, |
| 104 "Classes that are ref-counted should have explicit " |
| 105 "destructors that are protected or private."); |
| 106 } else if (CXXDestructorDecl* dtor = record->getDestructor()) { |
| 107 if (dtor->getAccess() == AS_public) { |
| 108 emitWarning( |
| 109 dtor->getInnerLocStart(), |
| 110 "Classes that are ref-counted should not have " |
| 111 "public destructors."); |
| 112 } |
| 113 } |
| 49 } | 114 } |
| 50 | 115 |
| 51 // Prints errors if the constructor/destructor weight is too heavy. | 116 // Prints errors if the constructor/destructor weight is too heavy. |
| 52 void CheckCtorDtorWeight(const SourceLocation& record_location, | 117 void CheckCtorDtorWeight(const SourceLocation& record_location, |
| 53 CXXRecordDecl* record) { | 118 CXXRecordDecl* record) { |
| 54 // We don't handle anonymous structs. If this record doesn't have a | 119 // We don't handle anonymous structs. If this record doesn't have a |
| 55 // name, it's of the form: | 120 // name, it's of the form: |
| 56 // | 121 // |
| 57 // struct { | 122 // struct { |
| 58 // ... | 123 // ... |
| (...skipping 95 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 154 if (cs->size()) { | 219 if (cs->size()) { |
| 155 emitWarning( | 220 emitWarning( |
| 156 cs->getLBracLoc(), | 221 cs->getLBracLoc(), |
| 157 "virtual methods with non-empty bodies shouldn't be " | 222 "virtual methods with non-empty bodies shouldn't be " |
| 158 "declared inline."); | 223 "declared inline."); |
| 159 } | 224 } |
| 160 } | 225 } |
| 161 } | 226 } |
| 162 } | 227 } |
| 163 | 228 |
| 229 bool InTestingNamespace(const Decl* record) { |
| 230 return GetNamespace(record).find("testing") != std::string::npos; |
| 231 } |
| 232 |
| 164 bool IsMethodInBannedNamespace(const CXXMethodDecl* method) { | 233 bool IsMethodInBannedNamespace(const CXXMethodDecl* method) { |
| 165 if (InBannedNamespace(method)) | 234 if (InBannedNamespace(method)) |
| 166 return true; | 235 return true; |
| 167 for (CXXMethodDecl::method_iterator i = method->begin_overridden_methods(); | 236 for (CXXMethodDecl::method_iterator i = method->begin_overridden_methods(); |
| 168 i != method->end_overridden_methods(); | 237 i != method->end_overridden_methods(); |
| 169 ++i) { | 238 ++i) { |
| 170 const CXXMethodDecl* overridden = *i; | 239 const CXXMethodDecl* overridden = *i; |
| 171 if (IsMethodInBannedNamespace(overridden)) | 240 if (IsMethodInBannedNamespace(overridden)) |
| 172 return true; | 241 return true; |
| 173 } | 242 } |
| (...skipping 118 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 292 const std::vector<std::string>& args) { | 361 const std::vector<std::string>& args) { |
| 293 // We don't take any additional arguments here. | 362 // We don't take any additional arguments here. |
| 294 return true; | 363 return true; |
| 295 } | 364 } |
| 296 }; | 365 }; |
| 297 | 366 |
| 298 } // namespace | 367 } // namespace |
| 299 | 368 |
| 300 static FrontendPluginRegistry::Add<FindBadConstructsAction> | 369 static FrontendPluginRegistry::Add<FindBadConstructsAction> |
| 301 X("find-bad-constructs", "Finds bad C++ constructs"); | 370 X("find-bad-constructs", "Finds bad C++ constructs"); |
| OLD | NEW |