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

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

Issue 26303002: Make Clang plugin check WeakPtrFactory member order (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Clean up for review Created 7 years, 2 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 | « no previous file | tools/clang/plugins/tests/weak_ptr_factory.h » ('j') | 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) 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
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
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
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
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");
OLDNEW
« no previous file with comments | « no previous file | tools/clang/plugins/tests/weak_ptr_factory.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698