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

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: 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
« no previous file with comments | « tools/clang/plugins/ChromeClassTester.cpp ('k') | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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 //
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
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
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
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");
OLDNEW
« no previous file with comments | « tools/clang/plugins/ChromeClassTester.cpp ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698