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

Unified Diff: tools/clang/plugins/ChromeClassTester.cpp

Issue 520283002: . Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: notjustcalls Created 6 years, 3 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 side-by-side diff with in-line comments
Download patch
« no previous file with comments | « tools/clang/plugins/ChromeClassTester.h ('k') | tools/clang/plugins/tests/base_passed.cpp » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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
« no previous file with comments | « tools/clang/plugins/ChromeClassTester.h ('k') | tools/clang/plugins/tests/base_passed.cpp » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698