Chromium Code Reviews| 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 // |
| 14 // Things that are still TODO: | 14 // Things that are still TODO: |
| 15 // - Deriving from base::RefCounted and friends should mandate non-public | 15 // - Deriving from base::RefCounted and friends should mandate non-public |
| 16 // destructors. | 16 // destructors. |
| 17 | 17 |
| 18 #include "clang/Frontend/FrontendPluginRegistry.h" | 18 #include "clang/Frontend/FrontendPluginRegistry.h" |
| 19 #include "clang/AST/ASTConsumer.h" | 19 #include "clang/AST/ASTConsumer.h" |
| 20 #include "clang/AST/AST.h" | 20 #include "clang/AST/AST.h" |
| 21 #include "clang/AST/CXXInheritance.h" | |
| 21 #include "clang/AST/TypeLoc.h" | 22 #include "clang/AST/TypeLoc.h" |
| 22 #include "clang/Basic/SourceManager.h" | 23 #include "clang/Basic/SourceManager.h" |
| 23 #include "clang/Frontend/CompilerInstance.h" | 24 #include "clang/Frontend/CompilerInstance.h" |
| 24 #include "llvm/Support/raw_ostream.h" | 25 #include "llvm/Support/raw_ostream.h" |
| 25 | 26 |
| 26 #include "ChromeClassTester.h" | 27 #include "ChromeClassTester.h" |
| 27 | 28 |
| 28 using namespace clang; | 29 using namespace clang; |
| 29 | 30 |
| 30 namespace { | 31 namespace { |
| 31 | 32 |
| 32 bool TypeHasNonTrivialDtor(const Type* type) { | 33 bool TypeHasNonTrivialDtor(const Type* type) { |
| 33 if (const CXXRecordDecl* cxx_r = type->getCXXRecordDeclForPointerType()) | 34 if (const CXXRecordDecl* cxx_r = type->getCXXRecordDeclForPointerType()) |
| 34 return cxx_r->hasTrivialDestructor(); | 35 return cxx_r->hasTrivialDestructor(); |
| 35 | 36 |
| 36 return false; | 37 return false; |
| 37 } | 38 } |
| 38 | 39 |
| 40 // Returns true if |base| specifies one of the Chromium reference counted | |
| 41 // classes (base::RefCounted / base::RefCountedThreadSafe). |user_data| is | |
| 42 // ignored. | |
| 43 bool IsRefCounted(const CXXBaseSpecifier* base, | |
| 44 CXXBasePath& path, | |
| 45 void* user_data) { | |
| 46 if (base->getTypeSourceInfo()->getTypeLoc().getTypeLocClass() != | |
| 47 TypeLoc::TemplateSpecialization) { | |
| 48 return false; | |
| 49 } | |
| 50 | |
| 51 TemplateName name = | |
| 52 dyn_cast<TemplateSpecializationType>(base->getType())->getTemplateName(); | |
| 53 | |
| 54 if (TemplateDecl* decl = name.getAsTemplateDecl()) { | |
| 55 std::string base_name = decl->getNameAsString(); | |
|
Nico
2012/04/05 23:08:13
might want to check the namespace too
| |
| 56 if (base_name.compare(0, 10, "RefCounted") == 0) | |
| 57 return true; | |
| 58 } | |
| 59 return false; | |
| 60 } | |
| 61 | |
| 39 // Searches for constructs that we know we don't want in the Chromium code base. | 62 // Searches for constructs that we know we don't want in the Chromium code base. |
| 40 class FindBadConstructsConsumer : public ChromeClassTester { | 63 class FindBadConstructsConsumer : public ChromeClassTester { |
| 41 public: | 64 public: |
| 42 FindBadConstructsConsumer(CompilerInstance& instance) | 65 FindBadConstructsConsumer(CompilerInstance& instance) |
| 43 : ChromeClassTester(instance) {} | 66 : ChromeClassTester(instance) {} |
| 44 | 67 |
| 45 virtual void CheckChromeClass(const SourceLocation& record_location, | 68 virtual void CheckChromeClass(const SourceLocation& record_location, |
| 46 CXXRecordDecl* record) { | 69 CXXRecordDecl* record) { |
| 47 CheckCtorDtorWeight(record_location, record); | 70 bool implementation_file = InImplementationFile(record_location); |
| 48 CheckVirtualMethods(record_location, record); | 71 |
| 72 if (!implementation_file) { | |
| 73 // Only check for "heavy" constructors/destructors in header files; | |
| 74 // within implementation files, there is no performance cost. | |
| 75 CheckCtorDtorWeight(record_location, record); | |
| 76 } | |
| 77 | |
| 78 CheckRefCountedDtors(record_location, record); | |
| 79 | |
| 80 // Check that all virtual methods are marked accordingly with both | |
| 81 // virtual and OVERRIDE. However, only warn on inline bodies for | |
| 82 // declarations in headers, not implementation files. | |
| 83 CheckVirtualMethods(record_location, record, !implementation_file); | |
| 84 } | |
| 85 | |
| 86 // Prints errors if the destructor of a RefCounted class is public. | |
| 87 void CheckRefCountedDtors(const SourceLocation& record_location, | |
| 88 CXXRecordDecl* record) { | |
| 89 // Skip anonymous structs. | |
| 90 if (record->getIdentifier() == NULL) | |
| 91 return; | |
| 92 | |
| 93 CXXBasePaths paths; | |
| 94 if (!record->lookupInBases(&IsRefCounted, NULL, paths)) | |
| 95 return; // Class does not derive from a ref-counted base class. | |
| 96 | |
| 97 if (!record->hasUserDeclaredDestructor()) { | |
| 98 emitWarning( | |
| 99 record_location, | |
| 100 "Classes that are ref-counted should have explicit " | |
| 101 "destructors that are protected or private."); | |
| 102 } else if (CXXDestructorDecl* dtor = record->getDestructor()) { | |
| 103 if (dtor->getAccess() == AS_public) { | |
| 104 emitWarning( | |
| 105 dtor->getInnerLocStart(), | |
| 106 "Classes that are ref-counted should not have " | |
| 107 "public destructors."); | |
| 108 } | |
| 109 } | |
| 49 } | 110 } |
| 50 | 111 |
| 51 // Prints errors if the constructor/destructor weight is too heavy. | 112 // Prints errors if the constructor/destructor weight is too heavy. |
| 52 void CheckCtorDtorWeight(const SourceLocation& record_location, | 113 void CheckCtorDtorWeight(const SourceLocation& record_location, |
| 53 CXXRecordDecl* record) { | 114 CXXRecordDecl* record) { |
| 54 // We don't handle anonymous structs. If this record doesn't have a | 115 // We don't handle anonymous structs. If this record doesn't have a |
| 55 // name, it's of the form: | 116 // name, it's of the form: |
| 56 // | 117 // |
| 57 // struct { | 118 // struct { |
| 58 // ... | 119 // ... |
| (...skipping 71 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 130 "destructor."); | 191 "destructor."); |
| 131 } else if (CXXDestructorDecl* dtor = record->getDestructor()) { | 192 } else if (CXXDestructorDecl* dtor = record->getDestructor()) { |
| 132 if (dtor->hasInlineBody()) { | 193 if (dtor->hasInlineBody()) { |
| 133 emitWarning(dtor->getInnerLocStart(), | 194 emitWarning(dtor->getInnerLocStart(), |
| 134 "Complex destructor has an inline body."); | 195 "Complex destructor has an inline body."); |
| 135 } | 196 } |
| 136 } | 197 } |
| 137 } | 198 } |
| 138 } | 199 } |
| 139 | 200 |
| 140 void CheckVirtualMethod(const CXXMethodDecl* method) { | 201 void CheckVirtualMethod(const CXXMethodDecl* method, |
| 202 bool warn_on_inline_bodies) { | |
| 141 if (!method->isVirtual()) | 203 if (!method->isVirtual()) |
| 142 return; | 204 return; |
| 143 | 205 |
| 144 if (!method->isVirtualAsWritten()) { | 206 if (!method->isVirtualAsWritten()) { |
| 145 SourceLocation loc = method->getTypeSpecStartLoc(); | 207 SourceLocation loc = method->getTypeSpecStartLoc(); |
| 146 if (isa<CXXDestructorDecl>(method)) | 208 if (isa<CXXDestructorDecl>(method)) |
| 147 loc = method->getInnerLocStart(); | 209 loc = method->getInnerLocStart(); |
| 148 emitWarning(loc, "Overriding method must have \"virtual\" keyword."); | 210 emitWarning(loc, "Overriding method must have \"virtual\" keyword."); |
| 149 } | 211 } |
| 150 | 212 |
| 151 // Virtual methods should not have inline definitions beyond "{}". | 213 // Virtual methods should not have inline definitions beyond "{}". This |
| 152 if (method->hasBody() && method->hasInlineBody()) { | 214 // only matters for header files. |
| 215 if (warn_on_inline_bodies && method->hasBody() && | |
| 216 method->hasInlineBody()) { | |
| 153 if (CompoundStmt* cs = dyn_cast<CompoundStmt>(method->getBody())) { | 217 if (CompoundStmt* cs = dyn_cast<CompoundStmt>(method->getBody())) { |
| 154 if (cs->size()) { | 218 if (cs->size()) { |
| 155 emitWarning( | 219 emitWarning( |
| 156 cs->getLBracLoc(), | 220 cs->getLBracLoc(), |
| 157 "virtual methods with non-empty bodies shouldn't be " | 221 "virtual methods with non-empty bodies shouldn't be " |
| 158 "declared inline."); | 222 "declared inline."); |
| 159 } | 223 } |
| 160 } | 224 } |
| 161 } | 225 } |
| 162 } | 226 } |
| (...skipping 19 matching lines...) Expand all Loading... | |
| 182 if (isa<CXXDestructorDecl>(method) || method->isPure()) | 246 if (isa<CXXDestructorDecl>(method) || method->isPure()) |
| 183 return; | 247 return; |
| 184 | 248 |
| 185 if (IsMethodInBannedNamespace(method)) | 249 if (IsMethodInBannedNamespace(method)) |
| 186 return; | 250 return; |
| 187 | 251 |
| 188 SourceLocation loc = method->getTypeSpecStartLoc(); | 252 SourceLocation loc = method->getTypeSpecStartLoc(); |
| 189 emitWarning(loc, "Overriding method must be marked with OVERRIDE."); | 253 emitWarning(loc, "Overriding method must be marked with OVERRIDE."); |
| 190 } | 254 } |
| 191 | 255 |
| 192 // Makes sure there is a "virtual" keyword on virtual methods and that there | 256 // Makes sure there is a "virtual" keyword on virtual methods. |
| 193 // are no inline function bodies on them (but "{}" is allowed). | |
| 194 // | 257 // |
| 195 // Gmock objects trigger these for each MOCK_BLAH() macro used. So we have a | 258 // Gmock objects trigger these for each MOCK_BLAH() macro used. So we have a |
| 196 // trick to get around that. If a class has member variables whose types are | 259 // trick to get around that. If a class has member variables whose types are |
| 197 // in the "testing" namespace (which is how gmock works behind the scenes), | 260 // in the "testing" namespace (which is how gmock works behind the scenes), |
| 198 // there's a really high chance we won't care about these errors | 261 // there's a really high chance we won't care about these errors |
| 199 void CheckVirtualMethods(const SourceLocation& record_location, | 262 void CheckVirtualMethods(const SourceLocation& record_location, |
| 200 CXXRecordDecl* record) { | 263 CXXRecordDecl* record, |
| 264 bool warn_on_inline_bodies) { | |
| 201 for (CXXRecordDecl::field_iterator it = record->field_begin(); | 265 for (CXXRecordDecl::field_iterator it = record->field_begin(); |
| 202 it != record->field_end(); ++it) { | 266 it != record->field_end(); ++it) { |
| 203 CXXRecordDecl* record_type = | 267 CXXRecordDecl* record_type = |
| 204 it->getTypeSourceInfo()->getTypeLoc().getTypePtr()-> | 268 it->getTypeSourceInfo()->getTypeLoc().getTypePtr()-> |
| 205 getAsCXXRecordDecl(); | 269 getAsCXXRecordDecl(); |
| 206 if (record_type) { | 270 if (record_type) { |
| 207 if (InTestingNamespace(record_type)) { | 271 if (InTestingNamespace(record_type)) { |
| 208 return; | 272 return; |
| 209 } | 273 } |
| 210 } | 274 } |
| 211 } | 275 } |
| 212 | 276 |
| 213 for (CXXRecordDecl::method_iterator it = record->method_begin(); | 277 for (CXXRecordDecl::method_iterator it = record->method_begin(); |
| 214 it != record->method_end(); ++it) { | 278 it != record->method_end(); ++it) { |
| 215 if (it->isCopyAssignmentOperator() || isa<CXXConstructorDecl>(*it)) { | 279 if (it->isCopyAssignmentOperator() || isa<CXXConstructorDecl>(*it)) { |
| 216 // Ignore constructors and assignment operators. | 280 // Ignore constructors and assignment operators. |
| 217 } else if (isa<CXXDestructorDecl>(*it) && | 281 } else if (isa<CXXDestructorDecl>(*it) && |
| 218 !record->hasUserDeclaredDestructor()) { | 282 !record->hasUserDeclaredDestructor()) { |
| 219 // Ignore non-user-declared destructors. | 283 // Ignore non-user-declared destructors. |
| 220 } else { | 284 } else { |
| 221 CheckVirtualMethod(*it); | 285 CheckVirtualMethod(*it, warn_on_inline_bodies); |
| 222 CheckOverriddenMethod(*it); | 286 CheckOverriddenMethod(*it); |
| 223 } | 287 } |
| 224 } | 288 } |
| 225 } | 289 } |
| 226 | 290 |
| 227 void CountType(const Type* type, | 291 void CountType(const Type* type, |
| 228 int* trivial_member, | 292 int* trivial_member, |
| 229 int* non_trivial_member, | 293 int* non_trivial_member, |
| 230 int* templated_non_trivial_member) { | 294 int* templated_non_trivial_member) { |
| 231 switch (type->getTypeClass()) { | 295 switch (type->getTypeClass()) { |
| (...skipping 60 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 292 const std::vector<std::string>& args) { | 356 const std::vector<std::string>& args) { |
| 293 // We don't take any additional arguments here. | 357 // We don't take any additional arguments here. |
| 294 return true; | 358 return true; |
| 295 } | 359 } |
| 296 }; | 360 }; |
| 297 | 361 |
| 298 } // namespace | 362 } // namespace |
| 299 | 363 |
| 300 static FrontendPluginRegistry::Add<FindBadConstructsAction> | 364 static FrontendPluginRegistry::Add<FindBadConstructsAction> |
| 301 X("find-bad-constructs", "Finds bad C++ constructs"); | 365 X("find-bad-constructs", "Finds bad C++ constructs"); |
| OLD | NEW |