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