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 #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 Loading... | |
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 Loading... | |
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 Loading... | |
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 Loading... | |
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 Loading... | |
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 Loading... | |
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 Loading... | |
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 |
OLD | NEW |