//== PseudoConstantAnalysis.cpp - Find Pseudoconstants in the AST-*- C++ -*-==// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// // // This file tracks the usage of variables in a Decl body to see if they are // never written to, implying that they constant. This is useful in static // analysis to see if a developer might have intended a variable to be const. // //===----------------------------------------------------------------------===// #include "clang/Analysis/Analyses/PseudoConstantAnalysis.h" #include "clang/AST/Decl.h" #include "clang/AST/Expr.h" #include "clang/AST/Stmt.h" #include "llvm/ADT/SmallPtrSet.h" #include using namespace clang; typedef llvm::SmallPtrSet VarDeclSet; PseudoConstantAnalysis::PseudoConstantAnalysis(const Stmt *DeclBody) : DeclBody(DeclBody), Analyzed(false) { NonConstantsImpl = new VarDeclSet; UsedVarsImpl = new VarDeclSet; } PseudoConstantAnalysis::~PseudoConstantAnalysis() { delete (VarDeclSet*)NonConstantsImpl; delete (VarDeclSet*)UsedVarsImpl; } // Returns true if the given ValueDecl is never written to in the given DeclBody bool PseudoConstantAnalysis::isPseudoConstant(const VarDecl *VD) { // Only local and static variables can be pseudoconstants if (!VD->hasLocalStorage() && !VD->isStaticLocal()) return false; if (!Analyzed) { RunAnalysis(); Analyzed = true; } VarDeclSet *NonConstants = (VarDeclSet*)NonConstantsImpl; return !NonConstants->count(VD); } // Returns true if the variable was used (self assignments don't count) bool PseudoConstantAnalysis::wasReferenced(const VarDecl *VD) { if (!Analyzed) { RunAnalysis(); Analyzed = true; } VarDeclSet *UsedVars = (VarDeclSet*)UsedVarsImpl; return UsedVars->count(VD); } // Returns a Decl from a (Block)DeclRefExpr (if any) const Decl *PseudoConstantAnalysis::getDecl(const Expr *E) { if (const DeclRefExpr *DR = dyn_cast(E)) return DR->getDecl(); else return nullptr; } void PseudoConstantAnalysis::RunAnalysis() { std::deque WorkList; VarDeclSet *NonConstants = (VarDeclSet*)NonConstantsImpl; VarDeclSet *UsedVars = (VarDeclSet*)UsedVarsImpl; // Start with the top level statement of the function WorkList.push_back(DeclBody); while (!WorkList.empty()) { const Stmt *Head = WorkList.front(); WorkList.pop_front(); if (const Expr *Ex = dyn_cast(Head)) Head = Ex->IgnoreParenCasts(); switch (Head->getStmtClass()) { // Case 1: Assignment operators modifying VarDecls case Stmt::BinaryOperatorClass: { const BinaryOperator *BO = cast(Head); // Look for a Decl on the LHS const Decl *LHSDecl = getDecl(BO->getLHS()->IgnoreParenCasts()); if (!LHSDecl) break; // We found a binary operator with a DeclRefExpr on the LHS. We now check // for any of the assignment operators, implying that this Decl is being // written to. switch (BO->getOpcode()) { // Self-assignments don't count as use of a variable case BO_Assign: { // Look for a DeclRef on the RHS const Decl *RHSDecl = getDecl(BO->getRHS()->IgnoreParenCasts()); // If the Decls match, we have self-assignment if (LHSDecl == RHSDecl) // Do not visit the children continue; LLVM_FALLTHROUGH; } case BO_AddAssign: case BO_SubAssign: case BO_MulAssign: case BO_DivAssign: case BO_AndAssign: case BO_OrAssign: case BO_XorAssign: case BO_ShlAssign: case BO_ShrAssign: { const VarDecl *VD = dyn_cast(LHSDecl); // The DeclRefExpr is being assigned to - mark it as non-constant if (VD) NonConstants->insert(VD); break; } default: break; } break; } // Case 2: Pre/post increment/decrement and address of case Stmt::UnaryOperatorClass: { const UnaryOperator *UO = cast(Head); // Look for a DeclRef in the subexpression const Decl *D = getDecl(UO->getSubExpr()->IgnoreParenCasts()); if (!D) break; // We found a unary operator with a DeclRef as a subexpression. We now // check for any of the increment/decrement operators, as well as // addressOf. switch (UO->getOpcode()) { case UO_PostDec: case UO_PostInc: case UO_PreDec: case UO_PreInc: // The DeclRef is being changed - mark it as non-constant case UO_AddrOf: { // If we are taking the address of the DeclRefExpr, assume it is // non-constant. const VarDecl *VD = dyn_cast(D); if (VD) NonConstants->insert(VD); break; } default: break; } break; } // Case 3: Reference Declarations case Stmt::DeclStmtClass: { const DeclStmt *DS = cast(Head); // Iterate over each decl and see if any of them contain reference decls for (const auto *I : DS->decls()) { // We only care about VarDecls const VarDecl *VD = dyn_cast(I); if (!VD) continue; // We found a VarDecl; make sure it is a reference type if (!VD->getType().getTypePtr()->isReferenceType()) continue; // Try to find a Decl in the initializer const Decl *D = getDecl(VD->getInit()->IgnoreParenCasts()); if (!D) break; // If the reference is to another var, add the var to the non-constant // list if (const VarDecl *RefVD = dyn_cast(D)) { NonConstants->insert(RefVD); continue; } } break; } // Case 4: Variable references case Stmt::DeclRefExprClass: { const DeclRefExpr *DR = cast(Head); if (const VarDecl *VD = dyn_cast(DR->getDecl())) { // Add the Decl to the used list UsedVars->insert(VD); continue; } break; } // Case 5: Block expressions case Stmt::BlockExprClass: { const BlockExpr *B = cast(Head); // Add the body of the block to the list WorkList.push_back(B->getBody()); continue; } default: break; } // switch (head->getStmtClass()) // Add all substatements to the worklist for (const Stmt *SubStmt : Head->children()) if (SubStmt) WorkList.push_back(SubStmt); } // while (!WorkList.empty()) }