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. |
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 // - Classes that derive from base::RefCounted / base::RefCountedThreadSafe | 13 // - Classes that derive from base::RefCounted / base::RefCountedThreadSafe |
14 // should have protected or private destructors. | 14 // should have protected or private destructors. |
| 15 // - WeakPtrFactory members that refer to their outer class should be the last |
| 16 // member. |
15 | 17 |
16 #include "clang/AST/ASTConsumer.h" | 18 #include "clang/AST/ASTConsumer.h" |
17 #include "clang/AST/AST.h" | 19 #include "clang/AST/AST.h" |
18 #include "clang/AST/Attr.h" | 20 #include "clang/AST/Attr.h" |
19 #include "clang/AST/CXXInheritance.h" | 21 #include "clang/AST/CXXInheritance.h" |
20 #include "clang/AST/TypeLoc.h" | 22 #include "clang/AST/TypeLoc.h" |
21 #include "clang/Basic/SourceManager.h" | 23 #include "clang/Basic/SourceManager.h" |
22 #include "clang/Frontend/CompilerInstance.h" | 24 #include "clang/Frontend/CompilerInstance.h" |
23 #include "clang/Frontend/FrontendPluginRegistry.h" | 25 #include "clang/Frontend/FrontendPluginRegistry.h" |
24 #include "clang/Lex/Lexer.h" | 26 #include "clang/Lex/Lexer.h" |
(...skipping 11 matching lines...) Expand all Loading... |
36 "[chromium-style] Overriding method must have \"virtual\" keyword."; | 38 "[chromium-style] Overriding method must have \"virtual\" keyword."; |
37 const char kNoExplicitDtor[] = | 39 const char kNoExplicitDtor[] = |
38 "[chromium-style] Classes that are ref-counted should have explicit " | 40 "[chromium-style] Classes that are ref-counted should have explicit " |
39 "destructors that are declared protected or private."; | 41 "destructors that are declared protected or private."; |
40 const char kPublicDtor[] = | 42 const char kPublicDtor[] = |
41 "[chromium-style] Classes that are ref-counted should have " | 43 "[chromium-style] Classes that are ref-counted should have " |
42 "destructors that are declared protected or private."; | 44 "destructors that are declared protected or private."; |
43 const char kProtectedNonVirtualDtor[] = | 45 const char kProtectedNonVirtualDtor[] = |
44 "[chromium-style] Classes that are ref-counted and have non-private " | 46 "[chromium-style] Classes that are ref-counted and have non-private " |
45 "destructors should declare their destructor virtual."; | 47 "destructors should declare their destructor virtual."; |
| 48 const char kWeakPtrFactoryOrder[] = |
| 49 "[chromium-style] WeakPtrFactory members which refer to their outer class " |
| 50 "must be the last member in the outer class definition."; |
46 const char kNoteInheritance[] = | 51 const char kNoteInheritance[] = |
47 "[chromium-style] %0 inherits from %1 here"; | 52 "[chromium-style] %0 inherits from %1 here"; |
48 const char kNoteImplicitDtor[] = | 53 const char kNoteImplicitDtor[] = |
49 "[chromium-style] No explicit destructor for %0 defined"; | 54 "[chromium-style] No explicit destructor for %0 defined"; |
50 const char kNotePublicDtor[] = | 55 const char kNotePublicDtor[] = |
51 "[chromium-style] Public destructor declared here"; | 56 "[chromium-style] Public destructor declared here"; |
52 const char kNoteProtectedNonVirtualDtor[] = | 57 const char kNoteProtectedNonVirtualDtor[] = |
53 "[chromium-style] Protected non-virtual destructor declared here"; | 58 "[chromium-style] Protected non-virtual destructor declared here"; |
54 | 59 |
55 bool TypeHasNonTrivialDtor(const Type* type) { | 60 bool TypeHasNonTrivialDtor(const Type* type) { |
56 if (const CXXRecordDecl* cxx_r = type->getPointeeCXXRecordDecl()) | 61 if (const CXXRecordDecl* cxx_r = type->getPointeeCXXRecordDecl()) |
57 return cxx_r->hasTrivialDestructor(); | 62 return cxx_r->hasTrivialDestructor(); |
58 | 63 |
59 return false; | 64 return false; |
60 } | 65 } |
61 | 66 |
62 // Returns the underlying Type for |type| by expanding typedefs and removing | 67 // Returns the underlying Type for |type| by expanding typedefs and removing |
63 // any namespace qualifiers. This is similar to desugaring, except that for | 68 // any namespace qualifiers. This is similar to desugaring, except that for |
64 // ElaboratedTypes, desugar will unwrap too much. | 69 // ElaboratedTypes, desugar will unwrap too much. |
65 const Type* UnwrapType(const Type* type) { | 70 const Type* UnwrapType(const Type* type) { |
66 if (const ElaboratedType* elaborated = dyn_cast<ElaboratedType>(type)) | 71 if (const ElaboratedType* elaborated = dyn_cast<ElaboratedType>(type)) |
67 return UnwrapType(elaborated->getNamedType().getTypePtr()); | 72 return UnwrapType(elaborated->getNamedType().getTypePtr()); |
68 if (const TypedefType* typedefed = dyn_cast<TypedefType>(type)) | 73 if (const TypedefType* typedefed = dyn_cast<TypedefType>(type)) |
69 return UnwrapType(typedefed->desugar().getTypePtr()); | 74 return UnwrapType(typedefed->desugar().getTypePtr()); |
70 return type; | 75 return type; |
71 } | 76 } |
72 | 77 |
| 78 struct FindBadConstructsOptions { |
| 79 FindBadConstructsOptions() : check_base_classes(false), |
| 80 check_virtuals_in_implementations(true), |
| 81 check_url_directory(false), |
| 82 check_weak_ptr_factory_order(false) { |
| 83 } |
| 84 bool check_base_classes; |
| 85 bool check_virtuals_in_implementations; |
| 86 bool check_url_directory; |
| 87 bool check_weak_ptr_factory_order; |
| 88 }; |
| 89 |
73 // Searches for constructs that we know we don't want in the Chromium code base. | 90 // Searches for constructs that we know we don't want in the Chromium code base. |
74 class FindBadConstructsConsumer : public ChromeClassTester { | 91 class FindBadConstructsConsumer : public ChromeClassTester { |
75 public: | 92 public: |
76 FindBadConstructsConsumer(CompilerInstance& instance, | 93 FindBadConstructsConsumer(CompilerInstance& instance, |
77 bool check_base_classes, | 94 const FindBadConstructsOptions& options) |
78 bool check_virtuals_in_implementations, | 95 : ChromeClassTester(instance, options.check_url_directory), |
79 bool check_url_directory) | 96 options_(options) { |
80 : ChromeClassTester(instance, check_url_directory), | |
81 check_base_classes_(check_base_classes), | |
82 check_virtuals_in_implementations_(check_virtuals_in_implementations) { | |
83 // Register warning/error messages. | 97 // Register warning/error messages. |
84 diag_method_requires_override_ = diagnostic().getCustomDiagID( | 98 diag_method_requires_override_ = diagnostic().getCustomDiagID( |
85 getErrorLevel(), kMethodRequiresOverride); | 99 getErrorLevel(), kMethodRequiresOverride); |
86 diag_method_requires_virtual_ = diagnostic().getCustomDiagID( | 100 diag_method_requires_virtual_ = diagnostic().getCustomDiagID( |
87 getErrorLevel(), kMethodRequiresVirtual); | 101 getErrorLevel(), kMethodRequiresVirtual); |
88 diag_no_explicit_dtor_ = diagnostic().getCustomDiagID( | 102 diag_no_explicit_dtor_ = diagnostic().getCustomDiagID( |
89 getErrorLevel(), kNoExplicitDtor); | 103 getErrorLevel(), kNoExplicitDtor); |
90 diag_public_dtor_ = diagnostic().getCustomDiagID( | 104 diag_public_dtor_ = diagnostic().getCustomDiagID( |
91 getErrorLevel(), kPublicDtor); | 105 getErrorLevel(), kPublicDtor); |
92 diag_protected_non_virtual_dtor_ = diagnostic().getCustomDiagID( | 106 diag_protected_non_virtual_dtor_ = diagnostic().getCustomDiagID( |
93 getErrorLevel(), kProtectedNonVirtualDtor); | 107 getErrorLevel(), kProtectedNonVirtualDtor); |
| 108 diag_weak_ptr_factory_order_ = diagnostic().getCustomDiagID( |
| 109 getErrorLevel(), kWeakPtrFactoryOrder); |
94 | 110 |
95 // Registers notes to make it easier to interpret warnings. | 111 // Registers notes to make it easier to interpret warnings. |
96 diag_note_inheritance_ = diagnostic().getCustomDiagID( | 112 diag_note_inheritance_ = diagnostic().getCustomDiagID( |
97 DiagnosticsEngine::Note, kNoteInheritance); | 113 DiagnosticsEngine::Note, kNoteInheritance); |
98 diag_note_implicit_dtor_ = diagnostic().getCustomDiagID( | 114 diag_note_implicit_dtor_ = diagnostic().getCustomDiagID( |
99 DiagnosticsEngine::Note, kNoteImplicitDtor); | 115 DiagnosticsEngine::Note, kNoteImplicitDtor); |
100 diag_note_public_dtor_ = diagnostic().getCustomDiagID( | 116 diag_note_public_dtor_ = diagnostic().getCustomDiagID( |
101 DiagnosticsEngine::Note, kNotePublicDtor); | 117 DiagnosticsEngine::Note, kNotePublicDtor); |
102 diag_note_protected_non_virtual_dtor_ = diagnostic().getCustomDiagID( | 118 diag_note_protected_non_virtual_dtor_ = diagnostic().getCustomDiagID( |
103 DiagnosticsEngine::Note, kNoteProtectedNonVirtualDtor); | 119 DiagnosticsEngine::Note, kNoteProtectedNonVirtualDtor); |
104 } | 120 } |
105 | 121 |
106 virtual void CheckChromeClass(SourceLocation record_location, | 122 virtual void CheckChromeClass(SourceLocation record_location, |
107 CXXRecordDecl* record) { | 123 CXXRecordDecl* record) { |
108 bool implementation_file = InImplementationFile(record_location); | 124 bool implementation_file = InImplementationFile(record_location); |
109 | 125 |
110 if (!implementation_file) { | 126 if (!implementation_file) { |
111 // Only check for "heavy" constructors/destructors in header files; | 127 // Only check for "heavy" constructors/destructors in header files; |
112 // within implementation files, there is no performance cost. | 128 // within implementation files, there is no performance cost. |
113 CheckCtorDtorWeight(record_location, record); | 129 CheckCtorDtorWeight(record_location, record); |
114 } | 130 } |
115 | 131 |
116 if (!implementation_file || check_virtuals_in_implementations_) { | 132 if (!implementation_file || options_.check_virtuals_in_implementations) { |
117 bool warn_on_inline_bodies = !implementation_file; | 133 bool warn_on_inline_bodies = !implementation_file; |
118 | 134 |
119 // Check that all virtual methods are marked accordingly with both | 135 // Check that all virtual methods are marked accordingly with both |
120 // virtual and OVERRIDE. | 136 // virtual and OVERRIDE. |
121 CheckVirtualMethods(record_location, record, warn_on_inline_bodies); | 137 CheckVirtualMethods(record_location, record, warn_on_inline_bodies); |
122 } | 138 } |
123 | 139 |
124 CheckRefCountedDtors(record_location, record); | 140 CheckRefCountedDtors(record_location, record); |
| 141 |
| 142 if (options_.check_weak_ptr_factory_order) |
| 143 CheckWeakPtrFactoryMembers(record_location, record); |
125 } | 144 } |
126 | 145 |
127 private: | 146 private: |
128 // The type of problematic ref-counting pattern that was encountered. | 147 // The type of problematic ref-counting pattern that was encountered. |
129 enum RefcountIssue { | 148 enum RefcountIssue { |
130 None, | 149 None, |
131 ImplicitDestructor, | 150 ImplicitDestructor, |
132 PublicDestructor | 151 PublicDestructor |
133 }; | 152 }; |
134 | 153 |
135 bool check_base_classes_; | 154 FindBadConstructsOptions options_; |
136 bool check_virtuals_in_implementations_; | |
137 | 155 |
138 unsigned diag_method_requires_override_; | 156 unsigned diag_method_requires_override_; |
139 unsigned diag_method_requires_virtual_; | 157 unsigned diag_method_requires_virtual_; |
140 unsigned diag_no_explicit_dtor_; | 158 unsigned diag_no_explicit_dtor_; |
141 unsigned diag_public_dtor_; | 159 unsigned diag_public_dtor_; |
142 unsigned diag_protected_non_virtual_dtor_; | 160 unsigned diag_protected_non_virtual_dtor_; |
| 161 unsigned diag_weak_ptr_factory_order_; |
143 unsigned diag_note_inheritance_; | 162 unsigned diag_note_inheritance_; |
144 unsigned diag_note_implicit_dtor_; | 163 unsigned diag_note_implicit_dtor_; |
145 unsigned diag_note_public_dtor_; | 164 unsigned diag_note_public_dtor_; |
146 unsigned diag_note_protected_non_virtual_dtor_; | 165 unsigned diag_note_protected_non_virtual_dtor_; |
147 | 166 |
148 // Prints errors if the constructor/destructor weight is too heavy. | 167 // Prints errors if the constructor/destructor weight is too heavy. |
149 void CheckCtorDtorWeight(SourceLocation record_location, | 168 void CheckCtorDtorWeight(SourceLocation record_location, |
150 CXXRecordDecl* record) { | 169 CXXRecordDecl* record) { |
151 // We don't handle anonymous structs. If this record doesn't have a | 170 // We don't handle anonymous structs. If this record doesn't have a |
152 // name, it's of the form: | 171 // name, it's of the form: |
(...skipping 354 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
507 unsigned DiagnosticForIssue(RefcountIssue issue) { | 526 unsigned DiagnosticForIssue(RefcountIssue issue) { |
508 switch (issue) { | 527 switch (issue) { |
509 case ImplicitDestructor: | 528 case ImplicitDestructor: |
510 return diag_no_explicit_dtor_; | 529 return diag_no_explicit_dtor_; |
511 case PublicDestructor: | 530 case PublicDestructor: |
512 return diag_public_dtor_; | 531 return diag_public_dtor_; |
513 case None: | 532 case None: |
514 assert(false && "Do not call DiagnosticForIssue with issue None"); | 533 assert(false && "Do not call DiagnosticForIssue with issue None"); |
515 return 0; | 534 return 0; |
516 } | 535 } |
| 536 assert(false); |
| 537 return 0; |
517 } | 538 } |
518 | 539 |
519 // Check |record| to determine if it has any problematic refcounting | 540 // Check |record| to determine if it has any problematic refcounting |
520 // issues and, if so, print them as warnings/errors based on the current | 541 // issues and, if so, print them as warnings/errors based on the current |
521 // value of getErrorLevel(). | 542 // value of getErrorLevel(). |
522 // | 543 // |
523 // If |record| is a C++ class, and if it inherits from one of the Chromium | 544 // If |record| is a C++ class, and if it inherits from one of the Chromium |
524 // ref-counting classes (base::RefCounted / base::RefCountedThreadSafe), | 545 // ref-counting classes (base::RefCounted / base::RefCountedThreadSafe), |
525 // ensure that there are no public destructors in the class hierarchy. This | 546 // ensure that there are no public destructors in the class hierarchy. This |
526 // is to guard against accidentally stack-allocating a RefCounted class or | 547 // is to guard against accidentally stack-allocating a RefCounted class or |
(...skipping 46 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
573 // friend class base::Refcounted<RefCountedInterface>; | 594 // friend class base::Refcounted<RefCountedInterface>; |
574 // virtual ~RefCountedInterface() {} | 595 // virtual ~RefCountedInterface() {} |
575 // }; | 596 // }; |
576 // | 597 // |
577 // While RefCountedInterface is "safe", in that its destructor is | 598 // While RefCountedInterface is "safe", in that its destructor is |
578 // private, it's possible to do the following "unsafe" code: | 599 // private, it's possible to do the following "unsafe" code: |
579 // scoped_refptr<RefCountedInterface> some_class( | 600 // scoped_refptr<RefCountedInterface> some_class( |
580 // new RefCountedInterface); | 601 // new RefCountedInterface); |
581 // // Calls SomeInterface::~SomeInterface(), which is unsafe. | 602 // // Calls SomeInterface::~SomeInterface(), which is unsafe. |
582 // delete static_cast<SomeInterface*>(some_class.get()); | 603 // delete static_cast<SomeInterface*>(some_class.get()); |
583 if (!check_base_classes_) | 604 if (!options_.check_base_classes) |
584 return; | 605 return; |
585 | 606 |
586 // Find all public destructors. This will record the class hierarchy | 607 // Find all public destructors. This will record the class hierarchy |
587 // that leads to the public destructor in |dtor_paths|. | 608 // that leads to the public destructor in |dtor_paths|. |
588 CXXBasePaths dtor_paths; | 609 CXXBasePaths dtor_paths; |
589 if (!record->lookupInBases( | 610 if (!record->lookupInBases( |
590 &FindBadConstructsConsumer::HasPublicDtorCallback, this, | 611 &FindBadConstructsConsumer::HasPublicDtorCallback, this, |
591 dtor_paths)) { | 612 dtor_paths)) { |
592 return; | 613 return; |
593 } | 614 } |
(...skipping 13 matching lines...) Expand all Loading... |
607 diagnostic().Report(loc, diag_note_implicit_dtor_) << problem_record; | 628 diagnostic().Report(loc, diag_note_implicit_dtor_) << problem_record; |
608 PrintInheritanceChain(*it); | 629 PrintInheritanceChain(*it); |
609 } else if (issue == PublicDestructor) { | 630 } else if (issue == PublicDestructor) { |
610 diagnostic().Report(record_location, diag_public_dtor_); | 631 diagnostic().Report(record_location, diag_public_dtor_); |
611 PrintInheritanceChain(refcounted_path.front()); | 632 PrintInheritanceChain(refcounted_path.front()); |
612 diagnostic().Report(loc, diag_note_public_dtor_); | 633 diagnostic().Report(loc, diag_note_public_dtor_); |
613 PrintInheritanceChain(*it); | 634 PrintInheritanceChain(*it); |
614 } | 635 } |
615 } | 636 } |
616 } | 637 } |
| 638 |
| 639 // Check for any problems with WeakPtrFactory class members. This currently |
| 640 // only checks that any WeakPtrFactory<T> member of T appears as the last |
| 641 // data member in T. We could consider checking for bad uses of |
| 642 // WeakPtrFactory to refer to other data members, but that would require |
| 643 // looking at the initializer list in constructors to see what the factory |
| 644 // points to. |
| 645 // Note, if we later add other unrelated checks of data members, we should |
| 646 // consider collapsing them in to one loop to avoid iterating over the data |
| 647 // members more than once. |
| 648 void CheckWeakPtrFactoryMembers(SourceLocation record_location, |
| 649 CXXRecordDecl* record) { |
| 650 // Skip anonymous structs. |
| 651 if (record->getIdentifier() == NULL) |
| 652 return; |
| 653 |
| 654 // Iterate through members of the class. |
| 655 RecordDecl::field_iterator iter(record->field_begin()), |
| 656 the_end(record->field_end()); |
| 657 SourceLocation weak_ptr_factory_location; // Invalid initially. |
| 658 for (; iter != the_end; ++iter) { |
| 659 // If we enter the loop but have already seen a matching WeakPtrFactory, |
| 660 // it means there is at least one member after the factory. |
| 661 if (weak_ptr_factory_location.isValid()) { |
| 662 diagnostic().Report(weak_ptr_factory_location, |
| 663 diag_weak_ptr_factory_order_); |
| 664 } |
| 665 const TemplateSpecializationType* template_spec_type = |
| 666 iter->getType().getTypePtr()->getAs<TemplateSpecializationType>(); |
| 667 if (template_spec_type) { |
| 668 const TemplateDecl* template_decl = |
| 669 template_spec_type->getTemplateName().getAsTemplateDecl(); |
| 670 if (template_decl && template_spec_type->getNumArgs() >= 1) { |
| 671 if (template_decl->getNameAsString().compare("WeakPtrFactory") == 0 && |
| 672 GetNamespace(template_decl) == "base") { |
| 673 const TemplateArgument& arg = template_spec_type->getArg(0); |
| 674 if (arg.getAsType().getTypePtr()->getAsCXXRecordDecl() == |
| 675 record->getTypeForDecl()->getAsCXXRecordDecl()) { |
| 676 weak_ptr_factory_location = iter->getLocation(); |
| 677 } |
| 678 } |
| 679 } |
| 680 } |
| 681 } |
| 682 } |
617 }; | 683 }; |
618 | 684 |
| 685 |
619 class FindBadConstructsAction : public PluginASTAction { | 686 class FindBadConstructsAction : public PluginASTAction { |
620 public: | 687 public: |
621 FindBadConstructsAction() | 688 FindBadConstructsAction() { |
622 : check_base_classes_(false), | |
623 check_virtuals_in_implementations_(true), | |
624 check_url_directory_(false) { | |
625 } | 689 } |
626 | 690 |
627 protected: | 691 protected: |
628 // Overridden from PluginASTAction: | 692 // Overridden from PluginASTAction: |
629 virtual ASTConsumer* CreateASTConsumer(CompilerInstance& instance, | 693 virtual ASTConsumer* CreateASTConsumer(CompilerInstance& instance, |
630 llvm::StringRef ref) { | 694 llvm::StringRef ref) { |
631 return new FindBadConstructsConsumer( | 695 return new FindBadConstructsConsumer(instance, options_); |
632 instance, check_base_classes_, check_virtuals_in_implementations_, | |
633 check_url_directory_); | |
634 } | 696 } |
635 | 697 |
636 virtual bool ParseArgs(const CompilerInstance& instance, | 698 virtual bool ParseArgs(const CompilerInstance& instance, |
637 const std::vector<std::string>& args) { | 699 const std::vector<std::string>& args) { |
638 bool parsed = true; | 700 bool parsed = true; |
639 | 701 |
640 for (size_t i = 0; i < args.size() && parsed; ++i) { | 702 for (size_t i = 0; i < args.size() && parsed; ++i) { |
641 if (args[i] == "skip-virtuals-in-implementations") { | 703 if (args[i] == "skip-virtuals-in-implementations") { |
642 // TODO(rsleevi): Remove this once http://crbug.com/115047 is fixed. | 704 // TODO(rsleevi): Remove this once http://crbug.com/115047 is fixed. |
643 check_virtuals_in_implementations_ = false; | 705 options_.check_virtuals_in_implementations = false; |
644 } else if (args[i] == "check-base-classes") { | 706 } else if (args[i] == "check-base-classes") { |
645 // TODO(rsleevi): Remove this once http://crbug.com/123295 is fixed. | 707 // TODO(rsleevi): Remove this once http://crbug.com/123295 is fixed. |
646 check_base_classes_ = true; | 708 options_.check_base_classes = true; |
647 } else if (args[i] == "check-url-directory") { | 709 } else if (args[i] == "check-url-directory") { |
648 // TODO(tfarina): Remove this once http://crbug.com/229660 is fixed. | 710 // TODO(tfarina): Remove this once http://crbug.com/229660 is fixed. |
649 check_url_directory_ = true; | 711 options_.check_url_directory = true; |
| 712 } else if (args[i] == "check-weak-ptr-factory-order") { |
| 713 // TODO(dmichael): Remove this once http://crbug.com/303818 is fixed. |
| 714 options_.check_weak_ptr_factory_order = true; |
650 } else { | 715 } else { |
651 parsed = false; | 716 parsed = false; |
652 llvm::errs() << "Unknown clang plugin argument: " << args[i] << "\n"; | 717 llvm::errs() << "Unknown clang plugin argument: " << args[i] << "\n"; |
653 } | 718 } |
654 } | 719 } |
655 | 720 |
656 return parsed; | 721 return parsed; |
657 } | 722 } |
658 | 723 |
659 private: | 724 private: |
660 bool check_base_classes_; | 725 FindBadConstructsOptions options_; |
661 bool check_virtuals_in_implementations_; | |
662 bool check_url_directory_; | |
663 }; | 726 }; |
664 | 727 |
665 } // namespace | 728 } // namespace |
666 | 729 |
667 static FrontendPluginRegistry::Add<FindBadConstructsAction> | 730 static FrontendPluginRegistry::Add<FindBadConstructsAction> |
668 X("find-bad-constructs", "Finds bad C++ constructs"); | 731 X("find-bad-constructs", "Finds bad C++ constructs"); |
OLD | NEW |