Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(42)

Side by Side Diff: tools/clang/plugins/FindBadConstructs.cpp

Issue 10005022: Check for public dtors on base::RefCounted types (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Now with moar flags Created 8 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
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
38 // Returns the underlying Type for |type| by expanding typedefs and removing
39 // any namespace qualifiers.
40 const Type* UnwrapType(const Type* type) {
Nico 2012/04/13 22:40:55 Does Type::getDesugaredType() do some of this? If
Ryan Sleevi 2012/04/14 00:16:29 As far as I can tell, it does too much or not enou
41 switch (type->getTypeClass()) {
42 case Type::Elaborated: {
43 type = UnwrapType(
44 dyn_cast<ElaboratedType>(type)->getNamedType().getTypePtr());
45 break;
46 }
47 case Type::Typedef: {
48 type = UnwrapType(dyn_cast<TypedefType>(type)->desugar().getTypePtr());
49 break;
50 }
51 default: {
52 break;
53 }
54 }
55 return type;
56 }
57
39 // Searches for constructs that we know we don't want in the Chromium code base. 58 // Searches for constructs that we know we don't want in the Chromium code base.
40 class FindBadConstructsConsumer : public ChromeClassTester { 59 class FindBadConstructsConsumer : public ChromeClassTester {
41 public: 60 public:
42 FindBadConstructsConsumer(CompilerInstance& instance) 61 FindBadConstructsConsumer(CompilerInstance& instance,
43 : ChromeClassTester(instance) {} 62 bool skip_refcounted_dtors)
63 : ChromeClassTester(instance),
64 skip_refcounted_dtors_(skip_refcounted_dtors) {
65 }
44 66
45 virtual void CheckChromeClass(const SourceLocation& record_location, 67 virtual void CheckChromeClass(SourceLocation record_location,
46 CXXRecordDecl* record) { 68 CXXRecordDecl* record) {
47 CheckCtorDtorWeight(record_location, record); 69 bool implementation_file = InImplementationFile(record_location);
48 CheckVirtualMethods(record_location, record); 70
71 if (!implementation_file) {
72 // Only check for "heavy" constructors/destructors in header files;
73 // within implementation files, there is no performance cost.
74 CheckCtorDtorWeight(record_location, record);
75
76 // Check that all virtual methods are marked accordingly with both
77 // virtual and OVERRIDE.
78 CheckVirtualMethods(record_location, record);
79 }
80
81 if (!skip_refcounted_dtors_)
Nico 2012/04/13 22:40:55 double negation like this is confusing. Call the v
82 CheckRefCountedDtors(record_location, record);
83 }
84
85 private:
86 bool skip_refcounted_dtors_;
87
88 // Returns true if |base| specifies one of the Chromium reference counted
89 // classes (base::RefCounted / base::RefCountedThreadSafe). |user_data| is
90 // ignored.
91 static bool IsRefCountedCallback(const CXXBaseSpecifier* base,
92 CXXBasePath& path,
93 void* user_data) {
94 FindBadConstructsConsumer* self =
95 static_cast<FindBadConstructsConsumer*>(user_data);
96
97 const TemplateSpecializationType* base_type =
98 dyn_cast<TemplateSpecializationType>(
99 UnwrapType(base->getType().getTypePtr()));
100 if (!base_type) {
101 // Base-most definition is not a template, so this cannot derive from
102 // base::RefCounted. However, it may still be possible to use with a
103 // scoped_refptr<> and support ref-counting, so this is not a perfect
104 // guarantee of safety.
105 return false;
106 }
107
108 TemplateName name = base_type->getTemplateName();
109 if (TemplateDecl* decl = name.getAsTemplateDecl()) {
110 std::string base_name = decl->getNameAsString();
111
112 // Check for both base::RefCounted and base::RefCountedThreadSafe.
113 if (base_name.compare(0, 10, "RefCounted") == 0 &&
114 self->GetNamespace(decl) == "base") {
115 return true;
116 }
117 }
118 return false;
119 }
120
121 // Prints errors if the destructor of a RefCounted class is public.
122 void CheckRefCountedDtors(SourceLocation record_location,
123 CXXRecordDecl* record) {
124 // Skip anonymous structs.
125 if (record->getIdentifier() == NULL)
126 return;
127
128 CXXBasePaths paths;
129 if (!record->lookupInBases(
130 &FindBadConstructsConsumer::IsRefCountedCallback, this, paths)) {
131 return; // Class does not derive from a ref-counted base class.
132 }
133
134 if (!record->hasUserDeclaredDestructor()) {
135 emitWarning(
136 record_location,
137 "Classes that are ref-counted should have explicit "
138 "destructors that are protected or private.");
139 } else if (CXXDestructorDecl* dtor = record->getDestructor()) {
140 if (dtor->getAccess() == AS_public) {
141 emitWarning(
142 dtor->getInnerLocStart(),
143 "Classes that are ref-counted should not have "
144 "public destructors.");
145 }
146 }
49 } 147 }
50 148
51 // Prints errors if the constructor/destructor weight is too heavy. 149 // Prints errors if the constructor/destructor weight is too heavy.
52 void CheckCtorDtorWeight(const SourceLocation& record_location, 150 void CheckCtorDtorWeight(SourceLocation record_location,
53 CXXRecordDecl* record) { 151 CXXRecordDecl* record) {
54 // We don't handle anonymous structs. If this record doesn't have a 152 // We don't handle anonymous structs. If this record doesn't have a
55 // name, it's of the form: 153 // name, it's of the form:
56 // 154 //
57 // struct { 155 // struct {
58 // ... 156 // ...
59 // } name_; 157 // } name_;
60 if (record->getIdentifier() == NULL) 158 if (record->getIdentifier() == NULL)
61 return; 159 return;
62 160
(...skipping 91 matching lines...) Expand 10 before | Expand all | Expand 10 after
154 if (cs->size()) { 252 if (cs->size()) {
155 emitWarning( 253 emitWarning(
156 cs->getLBracLoc(), 254 cs->getLBracLoc(),
157 "virtual methods with non-empty bodies shouldn't be " 255 "virtual methods with non-empty bodies shouldn't be "
158 "declared inline."); 256 "declared inline.");
159 } 257 }
160 } 258 }
161 } 259 }
162 } 260 }
163 261
262 bool InTestingNamespace(const Decl* record) {
263 return GetNamespace(record).find("testing") != std::string::npos;
264 }
265
164 bool IsMethodInBannedNamespace(const CXXMethodDecl* method) { 266 bool IsMethodInBannedNamespace(const CXXMethodDecl* method) {
165 if (InBannedNamespace(method)) 267 if (InBannedNamespace(method))
166 return true; 268 return true;
167 for (CXXMethodDecl::method_iterator i = method->begin_overridden_methods(); 269 for (CXXMethodDecl::method_iterator i = method->begin_overridden_methods();
168 i != method->end_overridden_methods(); 270 i != method->end_overridden_methods();
169 ++i) { 271 ++i) {
170 const CXXMethodDecl* overridden = *i; 272 const CXXMethodDecl* overridden = *i;
171 if (IsMethodInBannedNamespace(overridden)) 273 if (IsMethodInBannedNamespace(overridden))
172 return true; 274 return true;
173 } 275 }
(...skipping 15 matching lines...) Expand all
189 emitWarning(loc, "Overriding method must be marked with OVERRIDE."); 291 emitWarning(loc, "Overriding method must be marked with OVERRIDE.");
190 } 292 }
191 293
192 // Makes sure there is a "virtual" keyword on virtual methods and that there 294 // Makes sure there is a "virtual" keyword on virtual methods and that there
193 // are no inline function bodies on them (but "{}" is allowed). 295 // are no inline function bodies on them (but "{}" is allowed).
194 // 296 //
195 // Gmock objects trigger these for each MOCK_BLAH() macro used. So we have a 297 // 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 298 // 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), 299 // 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 300 // there's a really high chance we won't care about these errors
199 void CheckVirtualMethods(const SourceLocation& record_location, 301 void CheckVirtualMethods(SourceLocation record_location,
200 CXXRecordDecl* record) { 302 CXXRecordDecl* record) {
201 for (CXXRecordDecl::field_iterator it = record->field_begin(); 303 for (CXXRecordDecl::field_iterator it = record->field_begin();
202 it != record->field_end(); ++it) { 304 it != record->field_end(); ++it) {
203 CXXRecordDecl* record_type = 305 CXXRecordDecl* record_type =
204 it->getTypeSourceInfo()->getTypeLoc().getTypePtr()-> 306 it->getTypeSourceInfo()->getTypeLoc().getTypePtr()->
205 getAsCXXRecordDecl(); 307 getAsCXXRecordDecl();
206 if (record_type) { 308 if (record_type) {
207 if (InTestingNamespace(record_type)) { 309 if (InTestingNamespace(record_type)) {
208 return; 310 return;
209 } 311 }
(...skipping 66 matching lines...) Expand 10 before | Expand all | Expand 10 after
276 // Stupid assumption: anything we see that isn't the above is one of 378 // Stupid assumption: anything we see that isn't the above is one of
277 // the 20 integer types. 379 // the 20 integer types.
278 (*trivial_member)++; 380 (*trivial_member)++;
279 break; 381 break;
280 } 382 }
281 } 383 }
282 } 384 }
283 }; 385 };
284 386
285 class FindBadConstructsAction : public PluginASTAction { 387 class FindBadConstructsAction : public PluginASTAction {
388 public:
389 FindBadConstructsAction() : skip_refcounted_dtors_(false) {}
390
286 protected: 391 protected:
287 ASTConsumer* CreateASTConsumer(CompilerInstance &CI, llvm::StringRef ref) { 392 ASTConsumer* CreateASTConsumer(CompilerInstance &CI, llvm::StringRef ref) {
288 return new FindBadConstructsConsumer(CI); 393 return new FindBadConstructsConsumer(CI, skip_refcounted_dtors_);
289 } 394 }
290 395
291 bool ParseArgs(const CompilerInstance &CI, 396 bool ParseArgs(const CompilerInstance &CI,
292 const std::vector<std::string>& args) { 397 const std::vector<std::string>& args) {
293 // We don't take any additional arguments here. 398 bool parsed = true;
294 return true; 399
400 for (size_t i = 0; i < args.size() && parsed; ++i) {
401 if (args[i] == "skip-refcounted-dtors") {
402 skip_refcounted_dtors_ = true;
403 } else {
404 parsed = false;
405 llvm::errs() << "Unknown argument: " << args[i] << "\n";
406 }
407 }
408
409 return parsed;
295 } 410 }
411
412 private:
413 bool skip_refcounted_dtors_;
296 }; 414 };
297 415
298 } // namespace 416 } // namespace
299 417
300 static FrontendPluginRegistry::Add<FindBadConstructsAction> 418 static FrontendPluginRegistry::Add<FindBadConstructsAction>
301 X("find-bad-constructs", "Finds bad C++ constructs"); 419 X("find-bad-constructs", "Finds bad C++ constructs");
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698