| 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
|
|
|