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

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

Issue 1493813003: Convert the no-inline-virtuals rule into a constructors rule. Base URL: https://chromium.googlesource.com/chromium/src.git@lkcr
Patch Set: Rebase onto https://codereview.chromium.org/1504033010 Created 5 years 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
OLDNEW
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 #include "FindBadConstructsConsumer.h" 5 #include "FindBadConstructsConsumer.h"
6 6
7 #include "clang/Frontend/CompilerInstance.h" 7 #include "clang/Frontend/CompilerInstance.h"
8 #include "clang/AST/Attr.h" 8 #include "clang/AST/Attr.h"
9 #include "clang/Lex/Lexer.h" 9 #include "clang/Lex/Lexer.h"
10 #include "llvm/ADT/SmallPtrSet.h"
10 #include "llvm/Support/raw_ostream.h" 11 #include "llvm/Support/raw_ostream.h"
11 12
12 using namespace clang; 13 using namespace clang;
13 14
14 namespace chrome_checker { 15 namespace chrome_checker {
15 16
16 namespace { 17 namespace {
17 18
18 const char kMethodRequiresOverride[] = 19 const char kMethodRequiresOverride[] =
19 "[chromium-style] Overriding method must be marked with 'override' or " 20 "[chromium-style] Overriding method must be marked with 'override' or "
(...skipping 71 matching lines...) Expand 10 before | Expand all | Expand 10 after
91 return FixItHint::CreateRemoval(range); 92 return FixItHint::CreateRemoval(range);
92 } 93 }
93 94
94 bool IsPodOrTemplateType(const CXXRecordDecl& record) { 95 bool IsPodOrTemplateType(const CXXRecordDecl& record) {
95 return record.isPOD() || 96 return record.isPOD() ||
96 record.getDescribedClassTemplate() || 97 record.getDescribedClassTemplate() ||
97 record.getTemplateSpecializationKind() || 98 record.getTemplateSpecializationKind() ||
98 record.isDependentType(); 99 record.isDependentType();
99 } 100 }
100 101
102 // True if this class is likely to be created by user code rather than derived
103 // from.
104 bool IsLeafClass(const CXXRecordDecl& record) {
dcheng 2016/03/24 17:37:25 How accurate is this heuristic for determining lea
105 if (record.isAbstract())
106 return false;
107 bool found_protected = false;
108 for (const CXXConstructorDecl* ctor : record.ctors()) {
109 if (ctor->isDeleted()) {
110 continue;
111 }
112 switch (ctor->getAccess()) {
113 case AS_public:
114 return true;
115 case AS_protected:
116 found_protected = true;
117 break;
118 case AS_private:
119 break;
120 case AS_none:
121 // Shouldn't happen.
122 break;
123 }
124 }
125 // If all constructors are private, assume there's a public Create() function.
126 return !found_protected;
127 }
128
129 // Returns the distance from r.begin() to r.end(), but stops if it reaches
130 // |at_most|.
131 template<typename Range>
132 int distance_at_most(const Range& r, int at_most) {
133 int result = 0;
134 for (auto it = r.begin(), e = r.end(); it != e && result < at_most; ++it) {
135 ++result;
136 }
137 return result;
138 }
139
101 } // namespace 140 } // namespace
102 141
103 FindBadConstructsConsumer::FindBadConstructsConsumer(CompilerInstance& instance, 142 FindBadConstructsConsumer::FindBadConstructsConsumer(CompilerInstance& instance,
104 const Options& options) 143 const Options& options)
105 : ChromeClassTester(instance, options) { 144 : ChromeClassTester(instance, options) {
106 // Messages for virtual method specifiers. 145 // Messages for virtual method specifiers.
107 diag_method_requires_override_ = 146 diag_method_requires_override_ =
108 diagnostic().getCustomDiagID(getErrorLevel(), kMethodRequiresOverride); 147 diagnostic().getCustomDiagID(getErrorLevel(), kMethodRequiresOverride);
109 diag_redundant_virtual_specifier_ = 148 diag_redundant_virtual_specifier_ =
110 diagnostic().getCustomDiagID(getErrorLevel(), kRedundantVirtualSpecifier); 149 diagnostic().getCustomDiagID(getErrorLevel(), kRedundantVirtualSpecifier);
(...skipping 47 matching lines...) Expand 10 before | Expand all | Expand 10 after
158 // Only check for "heavy" constructors/destructors in header files; 197 // Only check for "heavy" constructors/destructors in header files;
159 // within implementation files, there is no performance cost. 198 // within implementation files, there is no performance cost.
160 199
161 // If this is a POD or a class template or a type dependent on a 200 // If this is a POD or a class template or a type dependent on a
162 // templated class, assume there's no ctor/dtor/virtual method 201 // templated class, assume there's no ctor/dtor/virtual method
163 // optimization that we should do. 202 // optimization that we should do.
164 if (!IsPodOrTemplateType(*record)) 203 if (!IsPodOrTemplateType(*record))
165 CheckCtorDtorWeight(record_location, record); 204 CheckCtorDtorWeight(record_location, record);
166 } 205 }
167 206
168 bool warn_on_inline_bodies = !implementation_file;
169 // Check that all virtual methods are annotated with override or final. 207 // Check that all virtual methods are annotated with override or final.
170 // Note this could also apply to templates, but for some reason Clang 208 // Note this could also apply to templates, but for some reason Clang
171 // does not always see the "override", so we get false positives. 209 // does not always see the "override", so we get false positives.
172 // See http://llvm.org/bugs/show_bug.cgi?id=18440 and 210 // See http://llvm.org/bugs/show_bug.cgi?id=18440 and
173 // http://llvm.org/bugs/show_bug.cgi?id=21942 211 // http://llvm.org/bugs/show_bug.cgi?id=21942
174 if (!IsPodOrTemplateType(*record)) 212 if (!IsPodOrTemplateType(*record))
175 CheckVirtualMethods(record_location, record, warn_on_inline_bodies); 213 CheckVirtualMethods(record_location, record);
176 214
177 CheckRefCountedDtors(record_location, record); 215 CheckRefCountedDtors(record_location, record);
178 216
179 CheckWeakPtrFactoryMembers(record_location, record); 217 CheckWeakPtrFactoryMembers(record_location, record);
180 } 218 }
181 219
182 void FindBadConstructsConsumer::CheckChromeEnum(SourceLocation enum_location, 220 void FindBadConstructsConsumer::CheckChromeEnum(SourceLocation enum_location,
183 EnumDecl* enum_decl) { 221 EnumDecl* enum_decl) {
184 if (!options_.check_enum_last_value) 222 if (!options_.check_enum_last_value)
185 return; 223 return;
(...skipping 81 matching lines...) Expand 10 before | Expand all | Expand 10 after
267 dtor_score += templated_base_classes * 9; 305 dtor_score += templated_base_classes * 9;
268 // Instantiating a template is an insta-hit. 306 // Instantiating a template is an insta-hit.
269 dtor_score += templated_non_trivial_member * 10; 307 dtor_score += templated_non_trivial_member * 10;
270 // The fourth normal class member should trigger the warning. 308 // The fourth normal class member should trigger the warning.
271 dtor_score += non_trivial_member * 3; 309 dtor_score += non_trivial_member * 3;
272 310
273 int ctor_score = dtor_score; 311 int ctor_score = dtor_score;
274 // You should be able to have 9 ints before we warn you. 312 // You should be able to have 9 ints before we warn you.
275 ctor_score += trivial_member; 313 ctor_score += trivial_member;
276 314
315 // For leaf classes, count the cost of emitting the vtable on Windows.
316 // (Windows because MSVC doesn't use the key-function optimization, so vtables
317 // are emitted more often.) The data behind this is in
318 // https://groups.google.com/a/chromium.org/d/topic/chromium-dev/rdF1mT3V_PM/d iscussion
319 if (options_.treat_virtuals_as_complexity && record->isPolymorphic() &&
320 IsLeafClass(*record)) {
321 int virtual_score = 3;
322
323 // For each non-overridden virtual function, add the length of its inline
324 // body, if any.
325 CXXFinalOverriderMap final_overriders;
326 llvm::SmallPtrSet<const Stmt*, 10> overriding_bodies;
327 record->getFinalOverriders(final_overriders);
328 for (const std::pair<const CXXMethodDecl*, OverridingMethods>& method :
dcheng 2016/03/24 17:37:25 Another way to write this might be to iterate over
329 final_overriders) {
330 for (const auto& overrides : method.second) {
331 for (const UniqueVirtualMethod& override : overrides.second) {
332 if (override.Method->hasInlineBody()) {
333 if (const Stmt* body = override.Method->getBody()) {
334 // The same inline function can override multiple virtual
335 // functions in base classes, and we only want to count it once.
336 overriding_bodies.insert(body);
337 }
338 }
339 }
340 }
341 }
342 for (const Stmt* body : overriding_bodies) {
343 virtual_score += 1 + distance_at_most(body->children(), 10);
344 }
345
346 ctor_score += virtual_score;
347 if (!record->getDestructor()->isVirtual()) {
348 dtor_score += virtual_score;
349 }
350 }
351
277 if (ctor_score >= 10) { 352 if (ctor_score >= 10) {
278 if (!record->hasUserDeclaredConstructor()) { 353 if (!record->hasUserDeclaredConstructor()) {
279 emitWarning(record_location, 354 emitWarning(record_location,
280 "Complex class/struct needs an explicit out-of-line " 355 "Complex class/struct needs an explicit out-of-line "
281 "constructor."); 356 "constructor.");
282 } else { 357 } else {
283 // Iterate across all the constructors in this file and yell if we 358 // Iterate across all the constructors in this file and yell if we
284 // find one that tries to be inline. 359 // find one that tries to be inline.
285 for (CXXRecordDecl::ctor_iterator it = record->ctor_begin(); 360 for (CXXRecordDecl::ctor_iterator it = record->ctor_begin();
286 it != record->ctor_end(); 361 it != record->ctor_end();
(...skipping 88 matching lines...) Expand 10 before | Expand all | Expand 10 after
375 450
376 SuppressibleDiagnosticBuilder 451 SuppressibleDiagnosticBuilder
377 FindBadConstructsConsumer::ReportIfSpellingLocNotIgnored( 452 FindBadConstructsConsumer::ReportIfSpellingLocNotIgnored(
378 SourceLocation loc, 453 SourceLocation loc,
379 unsigned diagnostic_id) { 454 unsigned diagnostic_id) {
380 return SuppressibleDiagnosticBuilder( 455 return SuppressibleDiagnosticBuilder(
381 &diagnostic(), loc, diagnostic_id, 456 &diagnostic(), loc, diagnostic_id,
382 InBannedDirectory(instance().getSourceManager().getSpellingLoc(loc))); 457 InBannedDirectory(instance().getSourceManager().getSpellingLoc(loc)));
383 } 458 }
384 459
385 // Checks that virtual methods are correctly annotated, and have no body in a 460 // Checks that virtual methods are correctly annotated.
386 // header file.
387 void FindBadConstructsConsumer::CheckVirtualMethods( 461 void FindBadConstructsConsumer::CheckVirtualMethods(
388 SourceLocation record_location, 462 SourceLocation record_location,
389 CXXRecordDecl* record, 463 CXXRecordDecl* record) {
390 bool warn_on_inline_bodies) {
391 // Gmock objects trigger these for each MOCK_BLAH() macro used. So we have a 464 // Gmock objects trigger these for each MOCK_BLAH() macro used. So we have a
392 // trick to get around that. If a class has member variables whose types are 465 // trick to get around that. If a class has member variables whose types are
393 // in the "testing" namespace (which is how gmock works behind the scenes), 466 // in the "testing" namespace (which is how gmock works behind the scenes),
394 // there's a really high chance we won't care about these errors 467 // there's a really high chance we won't care about these errors
395 for (CXXRecordDecl::field_iterator it = record->field_begin(); 468 for (CXXRecordDecl::field_iterator it = record->field_begin();
396 it != record->field_end(); 469 it != record->field_end();
397 ++it) { 470 ++it) {
398 CXXRecordDecl* record_type = it->getTypeSourceInfo() 471 CXXRecordDecl* record_type = it->getTypeSourceInfo()
399 ->getTypeLoc() 472 ->getTypeLoc()
400 .getTypePtr() 473 .getTypePtr()
(...skipping 10 matching lines...) Expand all
411 ++it) { 484 ++it) {
412 if (it->isCopyAssignmentOperator() || isa<CXXConstructorDecl>(*it)) { 485 if (it->isCopyAssignmentOperator() || isa<CXXConstructorDecl>(*it)) {
413 // Ignore constructors and assignment operators. 486 // Ignore constructors and assignment operators.
414 } else if (isa<CXXDestructorDecl>(*it) && 487 } else if (isa<CXXDestructorDecl>(*it) &&
415 !record->hasUserDeclaredDestructor()) { 488 !record->hasUserDeclaredDestructor()) {
416 // Ignore non-user-declared destructors. 489 // Ignore non-user-declared destructors.
417 } else if (!it->isVirtual()) { 490 } else if (!it->isVirtual()) {
418 continue; 491 continue;
419 } else { 492 } else {
420 CheckVirtualSpecifiers(*it); 493 CheckVirtualSpecifiers(*it);
421 if (warn_on_inline_bodies)
422 CheckVirtualBodies(*it);
423 } 494 }
424 } 495 }
425 } 496 }
426 497
427 // Makes sure that virtual methods use the most appropriate specifier. If a 498 // Makes sure that virtual methods use the most appropriate specifier. If a
428 // virtual method overrides a method from a base class, only the override 499 // virtual method overrides a method from a base class, only the override
429 // specifier should be used. If the method should not be overridden by derived 500 // specifier should be used. If the method should not be overridden by derived
430 // classes, only the final specifier should be used. 501 // classes, only the final specifier should be used.
431 void FindBadConstructsConsumer::CheckVirtualSpecifiers( 502 void FindBadConstructsConsumer::CheckVirtualSpecifiers(
432 const CXXMethodDecl* method) { 503 const CXXMethodDecl* method) {
(...skipping 81 matching lines...) Expand 10 before | Expand all | Expand 10 after
514 } 585 }
515 586
516 if (final_attr && !is_override) { 587 if (final_attr && !is_override) {
517 ReportIfSpellingLocNotIgnored(method->getLocStart(), 588 ReportIfSpellingLocNotIgnored(method->getLocStart(),
518 diag_base_method_virtual_and_final_) 589 diag_base_method_virtual_and_final_)
519 << FixItRemovalForVirtual(manager, lang_opts, method) 590 << FixItRemovalForVirtual(manager, lang_opts, method)
520 << FixItHint::CreateRemoval(final_attr->getRange()); 591 << FixItHint::CreateRemoval(final_attr->getRange());
521 } 592 }
522 } 593 }
523 594
524 void FindBadConstructsConsumer::CheckVirtualBodies(
525 const CXXMethodDecl* method) {
526 // Virtual methods should not have inline definitions beyond "{}". This
527 // only matters for header files.
528 if (method->hasBody() && method->hasInlineBody()) {
529 if (CompoundStmt* cs = dyn_cast<CompoundStmt>(method->getBody())) {
530 if (cs->size()) {
531 SourceLocation loc = cs->getLBracLoc();
532 // CR_BEGIN_MSG_MAP_EX and BEGIN_SAFE_MSG_MAP_EX try to be compatible
533 // to BEGIN_MSG_MAP(_EX). So even though they are in chrome code,
534 // we can't easily fix them, so explicitly whitelist them here.
535 bool emit = true;
536 if (loc.isMacroID()) {
537 SourceManager& manager = instance().getSourceManager();
538 if (InBannedDirectory(manager.getSpellingLoc(loc)))
539 emit = false;
540 else {
541 StringRef name = Lexer::getImmediateMacroName(
542 loc, manager, instance().getLangOpts());
543 if (name == "CR_BEGIN_MSG_MAP_EX" ||
544 name == "BEGIN_SAFE_MSG_MAP_EX")
545 emit = false;
546 }
547 }
548 if (emit)
549 emitWarning(loc,
550 "virtual methods with non-empty bodies shouldn't be "
551 "declared inline.");
552 }
553 }
554 }
555 }
556
557 void FindBadConstructsConsumer::CountType(const Type* type, 595 void FindBadConstructsConsumer::CountType(const Type* type,
558 int* trivial_member, 596 int* trivial_member,
559 int* non_trivial_member, 597 int* non_trivial_member,
560 int* templated_non_trivial_member) { 598 int* templated_non_trivial_member) {
561 switch (type->getTypeClass()) { 599 switch (type->getTypeClass()) {
562 case Type::Record: { 600 case Type::Record: {
563 // Simplifying; the whole class isn't trivial if the dtor is, but 601 // Simplifying; the whole class isn't trivial if the dtor is, but
564 // we use this as a signal about complexity. 602 // we use this as a signal about complexity.
565 if (TypeHasNonTrivialDtor(type)) 603 if (TypeHasNonTrivialDtor(type))
566 (*non_trivial_member)++; 604 (*non_trivial_member)++;
(...skipping 299 matching lines...) Expand 10 before | Expand all | Expand 10 after
866 // one of those, it means there is at least one member after a factory. 904 // one of those, it means there is at least one member after a factory.
867 if (weak_ptr_factory_location.isValid() && 905 if (weak_ptr_factory_location.isValid() &&
868 !param_is_weak_ptr_factory_to_self) { 906 !param_is_weak_ptr_factory_to_self) {
869 diagnostic().Report(weak_ptr_factory_location, 907 diagnostic().Report(weak_ptr_factory_location,
870 diag_weak_ptr_factory_order_); 908 diag_weak_ptr_factory_order_);
871 } 909 }
872 } 910 }
873 } 911 }
874 912
875 } // namespace chrome_checker 913 } // namespace chrome_checker
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698