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 // A general interface for filtering and only acting on classes in Chromium C++ | 5 // A general interface for filtering and only acting on classes in Chromium C++ |
6 // code. | 6 // code. |
7 | 7 |
8 #include "ChromeClassTester.h" | 8 #include "ChromeClassTester.h" |
9 | 9 |
10 #include <sys/param.h> | 10 #include <sys/param.h> |
11 | 11 |
12 #include "clang/AST/AST.h" | 12 #include "clang/AST/AST.h" |
| 13 #include "clang/AST/Expr.h" |
| 14 #include "clang/AST/RecursiveASTVisitor.h" |
13 #include "clang/Basic/FileManager.h" | 15 #include "clang/Basic/FileManager.h" |
14 #include "clang/Basic/SourceManager.h" | 16 #include "clang/Basic/SourceManager.h" |
15 | 17 |
16 using namespace clang; | 18 using namespace clang; |
17 | 19 |
18 namespace { | 20 namespace { |
19 | 21 |
| 22 class FunctionVisitor : public RecursiveASTVisitor<FunctionVisitor> { |
| 23 public: |
| 24 |
| 25 bool shouldVisitTemplateInstantiations() { return true; } |
| 26 |
| 27 bool VisitFunctionDecl(FunctionDecl* fun) { |
| 28 if (fun->doesThisDeclarationHaveABody()) |
| 29 funcs_.push_back(fun); |
| 30 return true; |
| 31 } |
| 32 |
| 33 std::vector<FunctionDecl*> funcs_; |
| 34 }; |
| 35 |
| 36 bool isBasePassedCall(const CallExpr* CE, DeclRefExpr*& DREOut) { |
| 37 const FunctionDecl* D = CE->getDirectCallee(); |
| 38 if (!D) return false; |
| 39 |
| 40 const NamespaceDecl *ND = dyn_cast<NamespaceDecl>(D->getDeclContext()); |
| 41 if (!ND) return false; |
| 42 if (ND->getName() != "base") return false; |
| 43 if (ND->getDeclContext()->getDeclKind() != Decl::TranslationUnit) |
| 44 return false; |
| 45 |
| 46 //auto FD = dyn_cast<FunctionDecl>(D); |
| 47 if (!D->getIdentifier()) return false; |
| 48 if (D->getName() != "Passed") return false; |
| 49 if (D->getNumParams() != 1) return false; |
| 50 |
| 51 const Expr *Arg = CE->getArg(0)->IgnoreParenImpCasts(); |
| 52 DeclRefExpr* DRE = nullptr; |
| 53 if (const UnaryOperator* Op = dyn_cast<UnaryOperator>(Arg)) { |
| 54 // This branch is for base::Passed(&s) |
| 55 if (Op->getOpcode() == UO_AddrOf) |
| 56 DRE = dyn_cast<DeclRefExpr>(Op->getSubExpr()->IgnoreParenImpCasts()); |
| 57 } else { |
| 58 // This branch is for base::Passed(s.Pass()) |
| 59 // Look through: |
| 60 // CXXBindTemporaryExpr |
| 61 // CXXConstructExpr |
| 62 // `-CXXConstructExpr |
| 63 // `-MaterializeTemporaryExpr |
| 64 // `-ImplicitCastExpr |
| 65 // `-ImplicitCastExpr |
| 66 // `-CXXMemberCallExpr scoped_ptr<int>::RValue |
| 67 // `-MemberExpr 0x104080510 .operator RValue |
| 68 // `-CXXMemberCallExpr base::scoped_ptr<int> |
| 69 // `-MemberExpr .Pass |
| 70 // `-DeclRefExpr base::scoped_ptr<int> |
| 71 if (const CXXBindTemporaryExpr *TE = dyn_cast<CXXBindTemporaryExpr>(Arg)) |
| 72 Arg = TE->getSubExpr(); |
| 73 while (const CXXConstructExpr *CE = dyn_cast<CXXConstructExpr>(Arg)) { |
| 74 if (CE->getNumArgs() != 1) |
| 75 return false; |
| 76 Arg = CE->getArg(0)->IgnoreParenImpCasts(); |
| 77 } |
| 78 while (const CXXMemberCallExpr* MC = dyn_cast<CXXMemberCallExpr>(Arg)) { |
| 79 CXXMethodDecl *MD = MC->getMethodDecl(); |
| 80 if (MD->getIdentifier() && MD->getName() == "Pass") { |
| 81 DRE = dyn_cast<DeclRefExpr>( |
| 82 MC->getImplicitObjectArgument()->IgnoreParenImpCasts()); |
| 83 break; |
| 84 } |
| 85 Arg = cast<MemberExpr>(MC->getCallee())->getBase()->IgnoreParenImpCasts(); |
| 86 if (const CXXBindTemporaryExpr *TE = dyn_cast<CXXBindTemporaryExpr>(Arg)) |
| 87 Arg = TE->getSubExpr(); |
| 88 } |
| 89 } |
| 90 |
| 91 if (!DRE) return false; // FIXME: or complain? What would this be? |
| 92 |
| 93 DREOut = DRE; |
| 94 return true; |
| 95 } |
| 96 |
| 97 typedef std::map<const Decl*, DeclRefExpr*> UseMap; |
| 98 |
| 99 // |Expr| is a Stmt due to StmtExprs. |
| 100 void AddAll(std::vector<const DeclRefExpr*>* out, UseMap* m, Stmt* SExpr) { |
| 101 if (auto CE = dyn_cast<CallExpr>(SExpr)) { |
| 102 // Don't push the DRE from within a base::Passed() CallExpr! |
| 103 DeclRefExpr* DRE; |
| 104 if (isBasePassedCall(CE, DRE)) { |
| 105 (*m)[DRE->getDecl()] = DRE; |
| 106 return; |
| 107 } |
| 108 // FIXME: What if it's a base::Passed() call taking something weird? |
| 109 } |
| 110 else if (auto DRE = dyn_cast<DeclRefExpr>(SExpr)) |
| 111 out->push_back(DRE); |
| 112 |
| 113 for (Stmt* C : SExpr->children()) { |
| 114 if (!C) continue; |
| 115 AddAll(out, m, C); |
| 116 } |
| 117 } |
| 118 |
| 119 void CheckExpr(ChromeClassTester& CCT, Expr* S, ASTContext& ctx) { |
| 120 // Recursively collect all expressions in arguments |
| 121 // (this has some false positives, as it stuffs things like "a ? b : c" into |
| 122 // the flat vector even though that has a sequence point). |
| 123 std::vector<const DeclRefExpr*> ArgExprs; |
| 124 UseMap uses; |
| 125 AddAll(&ArgExprs, &uses, S); |
| 126 |
| 127 // Find all declrefexprs that are both: |
| 128 // 1.) Passed to base::Passed() (which invalidates them) |
| 129 // 2.) Used in some other way (e.g. have their .get() called) |
| 130 |
| 131 for (const DeclRefExpr* E : ArgExprs) { |
| 132 auto it = uses.find(E->getDecl()); |
| 133 if (it != uses.end()) { |
| 134 CCT.diagnostic().Report(E->getLocation(), |
| 135 CCT.diag_might_be_gone_); |
| 136 CCT.diagnostic().Report(it->second->getLocation(), |
| 137 CCT.diag_might_be_gone_note_here_); |
| 138 } |
| 139 } |
| 140 } |
| 141 |
| 142 void CheckStmts(ChromeClassTester& CCT, Stmt* S, ASTContext& ctx) { |
| 143 for (auto* C : S->children()) { |
| 144 if (!C) continue; |
| 145 |
| 146 if (auto* Call = dyn_cast<Expr>(C)) |
| 147 CheckExpr(CCT, Call, ctx); |
| 148 else |
| 149 CheckStmts(CCT, C, ctx); |
| 150 } |
| 151 } |
| 152 |
20 bool starts_with(const std::string& one, const std::string& two) { | 153 bool starts_with(const std::string& one, const std::string& two) { |
21 return one.compare(0, two.size(), two) == 0; | 154 return one.compare(0, two.size(), two) == 0; |
22 } | 155 } |
23 | 156 |
24 std::string lstrip(const std::string& one, const std::string& two) { | 157 std::string lstrip(const std::string& one, const std::string& two) { |
25 if (starts_with(one, two)) | 158 if (starts_with(one, two)) |
26 return one.substr(two.size()); | 159 return one.substr(two.size()); |
27 return one; | 160 return one; |
28 } | 161 } |
29 | 162 |
30 bool ends_with(const std::string& one, const std::string& two) { | 163 bool ends_with(const std::string& one, const std::string& two) { |
31 if (two.size() > one.size()) | 164 if (two.size() > one.size()) |
32 return false; | 165 return false; |
33 | 166 |
34 return one.compare(one.size() - two.size(), two.size(), two) == 0; | 167 return one.compare(one.size() - two.size(), two.size(), two) == 0; |
35 } | 168 } |
36 | 169 |
37 } // namespace | 170 } // namespace |
38 | 171 |
39 ChromeClassTester::ChromeClassTester(CompilerInstance& instance) | 172 ChromeClassTester::ChromeClassTester(CompilerInstance& instance) |
40 : instance_(instance), | 173 : instance_(instance), |
41 diagnostic_(instance.getDiagnostics()) { | 174 diagnostic_(instance.getDiagnostics()) { |
| 175 |
| 176 diag_might_be_gone_ = |
| 177 diagnostic().getCustomDiagID(DiagnosticsEngine::Warning, |
| 178 "reference might be destroyed here"); |
| 179 diag_might_be_gone_note_here_ = |
| 180 diagnostic().getCustomDiagID(DiagnosticsEngine::Note, |
| 181 "invalidated here"); |
42 BuildBannedLists(); | 182 BuildBannedLists(); |
43 } | 183 } |
44 | 184 |
45 ChromeClassTester::~ChromeClassTester() {} | 185 ChromeClassTester::~ChromeClassTester() {} |
46 | 186 |
47 void ChromeClassTester::HandleTagDeclDefinition(TagDecl* tag) { | 187 void ChromeClassTester::HandleTagDeclDefinition(TagDecl* tag) { |
48 pending_class_decls_.push_back(tag); | 188 pending_class_decls_.push_back(tag); |
49 } | 189 } |
50 | 190 |
51 bool ChromeClassTester::HandleTopLevelDecl(DeclGroupRef group_ref) { | 191 bool ChromeClassTester::HandleTopLevelDecl(DeclGroupRef group_ref) { |
52 for (size_t i = 0; i < pending_class_decls_.size(); ++i) | 192 for (size_t i = 0; i < pending_class_decls_.size(); ++i) |
53 CheckTag(pending_class_decls_[i]); | 193 CheckTag(pending_class_decls_[i]); |
54 pending_class_decls_.clear(); | 194 pending_class_decls_.clear(); |
55 | 195 |
56 return true; // true means continue parsing. | 196 return true; // true means continue parsing. |
57 } | 197 } |
58 | 198 |
| 199 void ChromeClassTester::HandleTranslationUnit(ASTContext& Context) { |
| 200 FunctionVisitor visitor; |
| 201 visitor.TraverseDecl(Context.getTranslationUnitDecl()); |
| 202 |
| 203 // Now all function decls are in visitor.funcs_. |
| 204 for (auto* Fun : visitor.funcs_) { |
| 205 Stmt* Body = Fun->getBody(); |
| 206 assert(Body && "Only definitions should be passed here"); |
| 207 CheckStmts(*this, Body, Context); |
| 208 } |
| 209 } |
| 210 |
59 void ChromeClassTester::CheckTag(TagDecl* tag) { | 211 void ChromeClassTester::CheckTag(TagDecl* tag) { |
60 // We handle class types here where we have semantic information. We can only | 212 // We handle class types here where we have semantic information. We can only |
61 // check structs/classes/enums here, but we get a bunch of nice semantic | 213 // check structs/classes/enums here, but we get a bunch of nice semantic |
62 // information instead of just parsing information. | 214 // information instead of just parsing information. |
63 | 215 |
64 if (CXXRecordDecl* record = dyn_cast<CXXRecordDecl>(tag)) { | 216 if (CXXRecordDecl* record = dyn_cast<CXXRecordDecl>(tag)) { |
65 // If this is a POD or a class template or a type dependent on a | 217 // If this is a POD or a class template or a type dependent on a |
66 // templated class, assume there's no ctor/dtor/virtual method | 218 // templated class, assume there's no ctor/dtor/virtual method |
67 // optimization that we can do. | 219 // optimization that we can do. |
68 if (record->isPOD() || | 220 if (record->isPOD() || |
(...skipping 239 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
308 PresumedLoc ploc = source_manager.getPresumedLoc(spelling_location); | 460 PresumedLoc ploc = source_manager.getPresumedLoc(spelling_location); |
309 if (ploc.isInvalid()) { | 461 if (ploc.isInvalid()) { |
310 // If we're in an invalid location, we're looking at things that aren't | 462 // If we're in an invalid location, we're looking at things that aren't |
311 // actually stated in the source. | 463 // actually stated in the source. |
312 return false; | 464 return false; |
313 } | 465 } |
314 | 466 |
315 *filename = ploc.getFilename(); | 467 *filename = ploc.getFilename(); |
316 return true; | 468 return true; |
317 } | 469 } |
OLD | NEW |