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. |
| (...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 42 return UnwrapType(elaborated->getNamedType().getTypePtr()); | 42 return UnwrapType(elaborated->getNamedType().getTypePtr()); |
| 43 if (const TypedefType* typedefed = dyn_cast<TypedefType>(type)) | 43 if (const TypedefType* typedefed = dyn_cast<TypedefType>(type)) |
| 44 return UnwrapType(typedefed->desugar().getTypePtr()); | 44 return UnwrapType(typedefed->desugar().getTypePtr()); |
| 45 return type; | 45 return type; |
| 46 } | 46 } |
| 47 | 47 |
| 48 // Searches for constructs that we know we don't want in the Chromium code base. | 48 // Searches for constructs that we know we don't want in the Chromium code base. |
| 49 class FindBadConstructsConsumer : public ChromeClassTester { | 49 class FindBadConstructsConsumer : public ChromeClassTester { |
| 50 public: | 50 public: |
| 51 FindBadConstructsConsumer(CompilerInstance& instance, | 51 FindBadConstructsConsumer(CompilerInstance& instance, |
| 52 bool check_refcounted_dtors) | 52 bool check_refcounted_dtors, |
| 53 bool check_virtuals_in_implementations) | |
| 53 : ChromeClassTester(instance), | 54 : ChromeClassTester(instance), |
| 54 check_refcounted_dtors_(check_refcounted_dtors) { | 55 check_refcounted_dtors_(check_refcounted_dtors), |
| 56 check_virtuals_in_implementations_(check_virtuals_in_implementations) { | |
| 55 } | 57 } |
| 56 | 58 |
| 57 virtual void CheckChromeClass(SourceLocation record_location, | 59 virtual void CheckChromeClass(SourceLocation record_location, |
| 58 CXXRecordDecl* record) { | 60 CXXRecordDecl* record) { |
| 59 bool implementation_file = InImplementationFile(record_location); | 61 bool implementation_file = InImplementationFile(record_location); |
| 60 | 62 |
| 61 if (!implementation_file) { | 63 if (!implementation_file) { |
| 62 // Only check for "heavy" constructors/destructors in header files; | 64 // Only check for "heavy" constructors/destructors in header files; |
| 63 // within implementation files, there is no performance cost. | 65 // within implementation files, there is no performance cost. |
| 64 CheckCtorDtorWeight(record_location, record); | 66 CheckCtorDtorWeight(record_location, record); |
| 65 | |
| 66 // Check that all virtual methods are marked accordingly with both | |
| 67 // virtual and OVERRIDE. | |
| 68 CheckVirtualMethods(record_location, record); | |
| 69 } | 67 } |
| 70 | 68 |
| 71 if (check_refcounted_dtors_) | 69 if (!implementation_file || check_virtuals_in_implementations_) { |
| 70 // Check that all virtual methods are marked accordingly with both | |
| 71 // virtual and OVERRIDE. However, only warn on inline bodies for | |
| 72 // declarations in headers, not implementation files. | |
| 73 CheckVirtualMethods(record_location, record, !implementation_file); | |
|
Nico
2012/04/17 00:46:01
Do:
bool warn_on_inline_bodies = !implementation_
| |
| 74 } | |
| 75 | |
| 76 if (check_refcounted_dtors_) { | |
| 77 // Check to make sure that a class that derives from base::RefCounted | |
| 78 // or base::RefCountedThreadSafe does not have a public destructor. | |
|
Nico
2012/04/17 00:46:01
useless comment, remove (the method comment alread
| |
| 72 CheckRefCountedDtors(record_location, record); | 79 CheckRefCountedDtors(record_location, record); |
| 80 } | |
| 73 } | 81 } |
| 74 | 82 |
| 75 private: | 83 private: |
| 76 bool check_refcounted_dtors_; | 84 bool check_refcounted_dtors_; |
| 85 bool check_virtuals_in_implementations_; | |
| 77 | 86 |
| 78 // Returns true if |base| specifies one of the Chromium reference counted | 87 // Returns true if |base| specifies one of the Chromium reference counted |
| 79 // classes (base::RefCounted / base::RefCountedThreadSafe). |user_data| is | 88 // classes (base::RefCounted / base::RefCountedThreadSafe). |user_data| is |
| 80 // ignored. | 89 // ignored. |
| 81 static bool IsRefCountedCallback(const CXXBaseSpecifier* base, | 90 static bool IsRefCountedCallback(const CXXBaseSpecifier* base, |
| 82 CXXBasePath& path, | 91 CXXBasePath& path, |
| 83 void* user_data) { | 92 void* user_data) { |
| 84 FindBadConstructsConsumer* self = | 93 FindBadConstructsConsumer* self = |
| 85 static_cast<FindBadConstructsConsumer*>(user_data); | 94 static_cast<FindBadConstructsConsumer*>(user_data); |
| 86 | 95 |
| (...skipping 131 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 218 "destructor."); | 227 "destructor."); |
| 219 } else if (CXXDestructorDecl* dtor = record->getDestructor()) { | 228 } else if (CXXDestructorDecl* dtor = record->getDestructor()) { |
| 220 if (dtor->hasInlineBody()) { | 229 if (dtor->hasInlineBody()) { |
| 221 emitWarning(dtor->getInnerLocStart(), | 230 emitWarning(dtor->getInnerLocStart(), |
| 222 "Complex destructor has an inline body."); | 231 "Complex destructor has an inline body."); |
| 223 } | 232 } |
| 224 } | 233 } |
| 225 } | 234 } |
| 226 } | 235 } |
| 227 | 236 |
| 228 void CheckVirtualMethod(const CXXMethodDecl* method) { | 237 void CheckVirtualMethod(const CXXMethodDecl* method, |
| 238 bool warn_on_inline_bodies) { | |
| 229 if (!method->isVirtual()) | 239 if (!method->isVirtual()) |
| 230 return; | 240 return; |
| 231 | 241 |
| 232 if (!method->isVirtualAsWritten()) { | 242 if (!method->isVirtualAsWritten()) { |
| 233 SourceLocation loc = method->getTypeSpecStartLoc(); | 243 SourceLocation loc = method->getTypeSpecStartLoc(); |
| 234 if (isa<CXXDestructorDecl>(method)) | 244 if (isa<CXXDestructorDecl>(method)) |
| 235 loc = method->getInnerLocStart(); | 245 loc = method->getInnerLocStart(); |
| 236 emitWarning(loc, "Overriding method must have \"virtual\" keyword."); | 246 emitWarning(loc, "Overriding method must have \"virtual\" keyword."); |
| 237 } | 247 } |
| 238 | 248 |
| 239 // Virtual methods should not have inline definitions beyond "{}". | 249 // Virtual methods should not have inline definitions beyond "{}". This |
| 240 if (method->hasBody() && method->hasInlineBody()) { | 250 // only matters for header files. |
| 251 if (warn_on_inline_bodies && method->hasBody() && | |
| 252 method->hasInlineBody()) { | |
| 241 if (CompoundStmt* cs = dyn_cast<CompoundStmt>(method->getBody())) { | 253 if (CompoundStmt* cs = dyn_cast<CompoundStmt>(method->getBody())) { |
| 242 if (cs->size()) { | 254 if (cs->size()) { |
| 243 emitWarning( | 255 emitWarning( |
| 244 cs->getLBracLoc(), | 256 cs->getLBracLoc(), |
| 245 "virtual methods with non-empty bodies shouldn't be " | 257 "virtual methods with non-empty bodies shouldn't be " |
| 246 "declared inline."); | 258 "declared inline."); |
| 247 } | 259 } |
| 248 } | 260 } |
| 249 } | 261 } |
| 250 } | 262 } |
| (...skipping 23 matching lines...) Expand all Loading... | |
| 274 if (isa<CXXDestructorDecl>(method) || method->isPure()) | 286 if (isa<CXXDestructorDecl>(method) || method->isPure()) |
| 275 return; | 287 return; |
| 276 | 288 |
| 277 if (IsMethodInBannedNamespace(method)) | 289 if (IsMethodInBannedNamespace(method)) |
| 278 return; | 290 return; |
| 279 | 291 |
| 280 SourceLocation loc = method->getTypeSpecStartLoc(); | 292 SourceLocation loc = method->getTypeSpecStartLoc(); |
| 281 emitWarning(loc, "Overriding method must be marked with OVERRIDE."); | 293 emitWarning(loc, "Overriding method must be marked with OVERRIDE."); |
| 282 } | 294 } |
| 283 | 295 |
| 284 // Makes sure there is a "virtual" keyword on virtual methods and that there | 296 // Makes sure there is a "virtual" keyword on virtual methods. |
| 285 // are no inline function bodies on them (but "{}" is allowed). | |
| 286 // | 297 // |
| 287 // Gmock objects trigger these for each MOCK_BLAH() macro used. So we have a | 298 // Gmock objects trigger these for each MOCK_BLAH() macro used. So we have a |
| 288 // trick to get around that. If a class has member variables whose types are | 299 // trick to get around that. If a class has member variables whose types are |
| 289 // in the "testing" namespace (which is how gmock works behind the scenes), | 300 // in the "testing" namespace (which is how gmock works behind the scenes), |
| 290 // there's a really high chance we won't care about these errors | 301 // there's a really high chance we won't care about these errors |
| 291 void CheckVirtualMethods(SourceLocation record_location, | 302 void CheckVirtualMethods(SourceLocation record_location, |
| 292 CXXRecordDecl* record) { | 303 CXXRecordDecl* record, |
| 304 bool warn_on_inline_bodies) { | |
| 293 for (CXXRecordDecl::field_iterator it = record->field_begin(); | 305 for (CXXRecordDecl::field_iterator it = record->field_begin(); |
| 294 it != record->field_end(); ++it) { | 306 it != record->field_end(); ++it) { |
| 295 CXXRecordDecl* record_type = | 307 CXXRecordDecl* record_type = |
| 296 it->getTypeSourceInfo()->getTypeLoc().getTypePtr()-> | 308 it->getTypeSourceInfo()->getTypeLoc().getTypePtr()-> |
| 297 getAsCXXRecordDecl(); | 309 getAsCXXRecordDecl(); |
| 298 if (record_type) { | 310 if (record_type) { |
| 299 if (InTestingNamespace(record_type)) { | 311 if (InTestingNamespace(record_type)) { |
| 300 return; | 312 return; |
| 301 } | 313 } |
| 302 } | 314 } |
| 303 } | 315 } |
| 304 | 316 |
| 305 for (CXXRecordDecl::method_iterator it = record->method_begin(); | 317 for (CXXRecordDecl::method_iterator it = record->method_begin(); |
| 306 it != record->method_end(); ++it) { | 318 it != record->method_end(); ++it) { |
| 307 if (it->isCopyAssignmentOperator() || isa<CXXConstructorDecl>(*it)) { | 319 if (it->isCopyAssignmentOperator() || isa<CXXConstructorDecl>(*it)) { |
| 308 // Ignore constructors and assignment operators. | 320 // Ignore constructors and assignment operators. |
| 309 } else if (isa<CXXDestructorDecl>(*it) && | 321 } else if (isa<CXXDestructorDecl>(*it) && |
| 310 !record->hasUserDeclaredDestructor()) { | 322 !record->hasUserDeclaredDestructor()) { |
| 311 // Ignore non-user-declared destructors. | 323 // Ignore non-user-declared destructors. |
| 312 } else { | 324 } else { |
| 313 CheckVirtualMethod(*it); | 325 CheckVirtualMethod(*it, warn_on_inline_bodies); |
| 314 CheckOverriddenMethod(*it); | 326 CheckOverriddenMethod(*it); |
| 315 } | 327 } |
| 316 } | 328 } |
| 317 } | 329 } |
| 318 | 330 |
| 319 void CountType(const Type* type, | 331 void CountType(const Type* type, |
| 320 int* trivial_member, | 332 int* trivial_member, |
| 321 int* non_trivial_member, | 333 int* non_trivial_member, |
| 322 int* templated_non_trivial_member) { | 334 int* templated_non_trivial_member) { |
| 323 switch (type->getTypeClass()) { | 335 switch (type->getTypeClass()) { |
| (...skipping 45 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 369 // the 20 integer types. | 381 // the 20 integer types. |
| 370 (*trivial_member)++; | 382 (*trivial_member)++; |
| 371 break; | 383 break; |
| 372 } | 384 } |
| 373 } | 385 } |
| 374 } | 386 } |
| 375 }; | 387 }; |
| 376 | 388 |
| 377 class FindBadConstructsAction : public PluginASTAction { | 389 class FindBadConstructsAction : public PluginASTAction { |
| 378 public: | 390 public: |
| 379 FindBadConstructsAction() : check_refcounted_dtors_(true) {} | 391 FindBadConstructsAction() |
| 392 : check_refcounted_dtors_(true), | |
| 393 check_virtuals_in_implementations_(true) { | |
| 394 } | |
| 380 | 395 |
| 381 protected: | 396 protected: |
| 382 ASTConsumer* CreateASTConsumer(CompilerInstance &CI, llvm::StringRef ref) { | 397 ASTConsumer* CreateASTConsumer(CompilerInstance &CI, llvm::StringRef ref) { |
| 383 return new FindBadConstructsConsumer(CI, check_refcounted_dtors_); | 398 return new FindBadConstructsConsumer( |
| 399 CI, check_refcounted_dtors_, check_virtuals_in_implementations_); | |
| 384 } | 400 } |
| 385 | 401 |
| 386 bool ParseArgs(const CompilerInstance &CI, | 402 bool ParseArgs(const CompilerInstance &CI, |
| 387 const std::vector<std::string>& args) { | 403 const std::vector<std::string>& args) { |
| 388 bool parsed = true; | 404 bool parsed = true; |
| 389 | 405 |
| 390 for (size_t i = 0; i < args.size() && parsed; ++i) { | 406 for (size_t i = 0; i < args.size() && parsed; ++i) { |
| 391 if (args[i] == "skip-refcounted-dtors") { | 407 if (args[i] == "skip-refcounted-dtors") { |
| 392 check_refcounted_dtors_ = false; | 408 check_refcounted_dtors_ = false; |
| 409 } else if (args[i] == "skip-virtuals-in-implementations") { | |
| 410 check_virtuals_in_implementations_ = false; | |
| 393 } else { | 411 } else { |
| 394 parsed = false; | 412 parsed = false; |
| 395 llvm::errs() << "Unknown argument: " << args[i] << "\n"; | 413 llvm::errs() << "Unknown argument: " << args[i] << "\n"; |
| 396 } | 414 } |
| 397 } | 415 } |
| 398 | 416 |
| 399 return parsed; | 417 return parsed; |
| 400 } | 418 } |
| 401 | 419 |
| 402 private: | 420 private: |
| 403 bool check_refcounted_dtors_; | 421 bool check_refcounted_dtors_; |
| 422 bool check_virtuals_in_implementations_; | |
| 404 }; | 423 }; |
| 405 | 424 |
| 406 } // namespace | 425 } // namespace |
| 407 | 426 |
| 408 static FrontendPluginRegistry::Add<FindBadConstructsAction> | 427 static FrontendPluginRegistry::Add<FindBadConstructsAction> |
| 409 X("find-bad-constructs", "Finds bad C++ constructs"); | 428 X("find-bad-constructs", "Finds bad C++ constructs"); |
| OLD | NEW |