Index: tools/clang/plugins/ChromeClassTester.cpp |
diff --git a/tools/clang/plugins/ChromeClassTester.cpp b/tools/clang/plugins/ChromeClassTester.cpp |
index 5ce04e557f37ba1eadae7e4e41a7520da4203cd9..f717f57f2271c29ec212c3551c9b36f3dc2983ed 100644 |
--- a/tools/clang/plugins/ChromeClassTester.cpp |
+++ b/tools/clang/plugins/ChromeClassTester.cpp |
@@ -10,6 +10,8 @@ |
#include <sys/param.h> |
#include "clang/AST/AST.h" |
+#include "clang/AST/Expr.h" |
+#include "clang/AST/RecursiveASTVisitor.h" |
#include "clang/Basic/FileManager.h" |
#include "clang/Basic/SourceManager.h" |
@@ -17,6 +19,137 @@ using namespace clang; |
namespace { |
+class FunctionVisitor : public RecursiveASTVisitor<FunctionVisitor> { |
+ public: |
+ |
+ bool shouldVisitTemplateInstantiations() { return true; } |
+ |
+ bool VisitFunctionDecl(FunctionDecl* fun) { |
+ if (fun->doesThisDeclarationHaveABody()) |
+ funcs_.push_back(fun); |
+ return true; |
+ } |
+ |
+ std::vector<FunctionDecl*> funcs_; |
+}; |
+ |
+bool isBasePassedCall(const CallExpr* CE, DeclRefExpr*& DREOut) { |
+ const FunctionDecl* D = CE->getDirectCallee(); |
+ if (!D) return false; |
+ |
+ const NamespaceDecl *ND = dyn_cast<NamespaceDecl>(D->getDeclContext()); |
+ if (!ND) return false; |
+ if (ND->getName() != "base") return false; |
+ if (ND->getDeclContext()->getDeclKind() != Decl::TranslationUnit) |
+ return false; |
+ |
+ //auto FD = dyn_cast<FunctionDecl>(D); |
+ if (!D->getIdentifier()) return false; |
+ if (D->getName() != "Passed") return false; |
+ if (D->getNumParams() != 1) return false; |
+ |
+ const Expr *Arg = CE->getArg(0)->IgnoreParenImpCasts(); |
+ DeclRefExpr* DRE = nullptr; |
+ if (const UnaryOperator* Op = dyn_cast<UnaryOperator>(Arg)) { |
+ // This branch is for base::Passed(&s) |
+ if (Op->getOpcode() == UO_AddrOf) |
+ DRE = dyn_cast<DeclRefExpr>(Op->getSubExpr()->IgnoreParenImpCasts()); |
+ } else { |
+ // This branch is for base::Passed(s.Pass()) |
+ // Look through: |
+ // CXXBindTemporaryExpr |
+ // CXXConstructExpr |
+ // `-CXXConstructExpr |
+ // `-MaterializeTemporaryExpr |
+ // `-ImplicitCastExpr |
+ // `-ImplicitCastExpr |
+ // `-CXXMemberCallExpr scoped_ptr<int>::RValue |
+ // `-MemberExpr 0x104080510 .operator RValue |
+ // `-CXXMemberCallExpr base::scoped_ptr<int> |
+ // `-MemberExpr .Pass |
+ // `-DeclRefExpr base::scoped_ptr<int> |
+ if (const CXXBindTemporaryExpr *TE = dyn_cast<CXXBindTemporaryExpr>(Arg)) |
+ Arg = TE->getSubExpr(); |
+ while (const CXXConstructExpr *CE = dyn_cast<CXXConstructExpr>(Arg)) { |
+ if (CE->getNumArgs() != 1) |
+ return false; |
+ Arg = CE->getArg(0)->IgnoreParenImpCasts(); |
+ } |
+ while (const CXXMemberCallExpr* MC = dyn_cast<CXXMemberCallExpr>(Arg)) { |
+ CXXMethodDecl *MD = MC->getMethodDecl(); |
+ if (MD->getIdentifier() && MD->getName() == "Pass") { |
+ DRE = dyn_cast<DeclRefExpr>( |
+ MC->getImplicitObjectArgument()->IgnoreParenImpCasts()); |
+ break; |
+ } |
+ Arg = cast<MemberExpr>(MC->getCallee())->getBase()->IgnoreParenImpCasts(); |
+ if (const CXXBindTemporaryExpr *TE = dyn_cast<CXXBindTemporaryExpr>(Arg)) |
+ Arg = TE->getSubExpr(); |
+ } |
+ } |
+ |
+ if (!DRE) return false; // FIXME: or complain? What would this be? |
+ |
+ DREOut = DRE; |
+ return true; |
+} |
+ |
+typedef std::map<const Decl*, DeclRefExpr*> UseMap; |
+ |
+// |Expr| is a Stmt due to StmtExprs. |
+void AddAll(std::vector<const DeclRefExpr*>* out, UseMap* m, Stmt* SExpr) { |
+ if (auto CE = dyn_cast<CallExpr>(SExpr)) { |
+ // Don't push the DRE from within a base::Passed() CallExpr! |
+ DeclRefExpr* DRE; |
+ if (isBasePassedCall(CE, DRE)) { |
+ (*m)[DRE->getDecl()] = DRE; |
+ return; |
+ } |
+ // FIXME: What if it's a base::Passed() call taking something weird? |
+ } |
+ else if (auto DRE = dyn_cast<DeclRefExpr>(SExpr)) |
+ out->push_back(DRE); |
+ |
+ for (Stmt* C : SExpr->children()) { |
+ if (!C) continue; |
+ AddAll(out, m, C); |
+ } |
+} |
+ |
+void CheckExpr(ChromeClassTester& CCT, Expr* S, ASTContext& ctx) { |
+ // Recursively collect all expressions in arguments |
+ // (this has some false positives, as it stuffs things like "a ? b : c" into |
+ // the flat vector even though that has a sequence point). |
+ std::vector<const DeclRefExpr*> ArgExprs; |
+ UseMap uses; |
+ AddAll(&ArgExprs, &uses, S); |
+ |
+ // Find all declrefexprs that are both: |
+ // 1.) Passed to base::Passed() (which invalidates them) |
+ // 2.) Used in some other way (e.g. have their .get() called) |
+ |
+ for (const DeclRefExpr* E : ArgExprs) { |
+ auto it = uses.find(E->getDecl()); |
+ if (it != uses.end()) { |
+ CCT.diagnostic().Report(E->getLocation(), |
+ CCT.diag_might_be_gone_); |
+ CCT.diagnostic().Report(it->second->getLocation(), |
+ CCT.diag_might_be_gone_note_here_); |
+ } |
+ } |
+} |
+ |
+void CheckStmts(ChromeClassTester& CCT, Stmt* S, ASTContext& ctx) { |
+ for (auto* C : S->children()) { |
+ if (!C) continue; |
+ |
+ if (auto* Call = dyn_cast<Expr>(C)) |
+ CheckExpr(CCT, Call, ctx); |
+ else |
+ CheckStmts(CCT, C, ctx); |
+ } |
+} |
+ |
bool starts_with(const std::string& one, const std::string& two) { |
return one.compare(0, two.size(), two) == 0; |
} |
@@ -39,6 +172,13 @@ bool ends_with(const std::string& one, const std::string& two) { |
ChromeClassTester::ChromeClassTester(CompilerInstance& instance) |
: instance_(instance), |
diagnostic_(instance.getDiagnostics()) { |
+ |
+ diag_might_be_gone_ = |
+ diagnostic().getCustomDiagID(DiagnosticsEngine::Warning, |
+ "reference might be destroyed here"); |
+ diag_might_be_gone_note_here_ = |
+ diagnostic().getCustomDiagID(DiagnosticsEngine::Note, |
+ "invalidated here"); |
BuildBannedLists(); |
} |
@@ -56,6 +196,18 @@ bool ChromeClassTester::HandleTopLevelDecl(DeclGroupRef group_ref) { |
return true; // true means continue parsing. |
} |
+void ChromeClassTester::HandleTranslationUnit(ASTContext& Context) { |
+ FunctionVisitor visitor; |
+ visitor.TraverseDecl(Context.getTranslationUnitDecl()); |
+ |
+ // Now all function decls are in visitor.funcs_. |
+ for (auto* Fun : visitor.funcs_) { |
+ Stmt* Body = Fun->getBody(); |
+ assert(Body && "Only definitions should be passed here"); |
+ CheckStmts(*this, Body, Context); |
+ } |
+} |
+ |
void ChromeClassTester::CheckTag(TagDecl* tag) { |
// We handle class types here where we have semantic information. We can only |
// check structs/classes/enums here, but we get a bunch of nice semantic |