clang API Documentation

PseudoConstantAnalysis.cpp
Go to the documentation of this file.
00001 //== PseudoConstantAnalysis.cpp - Find Pseudoconstants in the AST-*- C++ -*-==//
00002 //
00003 //                     The LLVM Compiler Infrastructure
00004 //
00005 // This file is distributed under the University of Illinois Open Source
00006 // License. See LICENSE.TXT for details.
00007 //
00008 //===----------------------------------------------------------------------===//
00009 //
00010 // This file tracks the usage of variables in a Decl body to see if they are
00011 // never written to, implying that they constant. This is useful in static
00012 // analysis to see if a developer might have intended a variable to be const.
00013 //
00014 //===----------------------------------------------------------------------===//
00015 
00016 #include "clang/Analysis/Analyses/PseudoConstantAnalysis.h"
00017 #include "clang/AST/Decl.h"
00018 #include "clang/AST/Expr.h"
00019 #include "clang/AST/Stmt.h"
00020 #include "llvm/ADT/SmallPtrSet.h"
00021 #include <deque>
00022 
00023 using namespace clang;
00024 
00025 // The number of ValueDecls we want to keep track of by default (per-function)
00026 #define VARDECL_SET_SIZE 256
00027 typedef llvm::SmallPtrSet<const VarDecl*, VARDECL_SET_SIZE> VarDeclSet;
00028 
00029 PseudoConstantAnalysis::PseudoConstantAnalysis(const Stmt *DeclBody) :
00030       DeclBody(DeclBody), Analyzed(false) {
00031   NonConstantsImpl = new VarDeclSet;
00032   UsedVarsImpl = new VarDeclSet;
00033 }
00034 
00035 PseudoConstantAnalysis::~PseudoConstantAnalysis() {
00036   delete (VarDeclSet*)NonConstantsImpl;
00037   delete (VarDeclSet*)UsedVarsImpl;
00038 }
00039 
00040 // Returns true if the given ValueDecl is never written to in the given DeclBody
00041 bool PseudoConstantAnalysis::isPseudoConstant(const VarDecl *VD) {
00042   // Only local and static variables can be pseudoconstants
00043   if (!VD->hasLocalStorage() && !VD->isStaticLocal())
00044     return false;
00045 
00046   if (!Analyzed) {
00047     RunAnalysis();
00048     Analyzed = true;
00049   }
00050 
00051   VarDeclSet *NonConstants = (VarDeclSet*)NonConstantsImpl;
00052 
00053   return !NonConstants->count(VD);
00054 }
00055 
00056 // Returns true if the variable was used (self assignments don't count)
00057 bool PseudoConstantAnalysis::wasReferenced(const VarDecl *VD) {
00058   if (!Analyzed) {
00059     RunAnalysis();
00060     Analyzed = true;
00061   }
00062 
00063   VarDeclSet *UsedVars = (VarDeclSet*)UsedVarsImpl;
00064 
00065   return UsedVars->count(VD);
00066 }
00067 
00068 // Returns a Decl from a (Block)DeclRefExpr (if any)
00069 const Decl *PseudoConstantAnalysis::getDecl(const Expr *E) {
00070   if (const DeclRefExpr *DR = dyn_cast<DeclRefExpr>(E))
00071     return DR->getDecl();
00072   else
00073     return nullptr;
00074 }
00075 
00076 void PseudoConstantAnalysis::RunAnalysis() {
00077   std::deque<const Stmt *> WorkList;
00078   VarDeclSet *NonConstants = (VarDeclSet*)NonConstantsImpl;
00079   VarDeclSet *UsedVars = (VarDeclSet*)UsedVarsImpl;
00080 
00081   // Start with the top level statement of the function
00082   WorkList.push_back(DeclBody);
00083 
00084   while (!WorkList.empty()) {
00085     const Stmt *Head = WorkList.front();
00086     WorkList.pop_front();
00087 
00088     if (const Expr *Ex = dyn_cast<Expr>(Head))
00089       Head = Ex->IgnoreParenCasts();
00090 
00091     switch (Head->getStmtClass()) {
00092     // Case 1: Assignment operators modifying VarDecls
00093     case Stmt::BinaryOperatorClass: {
00094       const BinaryOperator *BO = cast<BinaryOperator>(Head);
00095       // Look for a Decl on the LHS
00096       const Decl *LHSDecl = getDecl(BO->getLHS()->IgnoreParenCasts());
00097       if (!LHSDecl)
00098         break;
00099 
00100       // We found a binary operator with a DeclRefExpr on the LHS. We now check
00101       // for any of the assignment operators, implying that this Decl is being
00102       // written to.
00103       switch (BO->getOpcode()) {
00104       // Self-assignments don't count as use of a variable
00105       case BO_Assign: {
00106         // Look for a DeclRef on the RHS
00107         const Decl *RHSDecl = getDecl(BO->getRHS()->IgnoreParenCasts());
00108 
00109         // If the Decls match, we have self-assignment
00110         if (LHSDecl == RHSDecl)
00111           // Do not visit the children
00112           continue;
00113 
00114       }
00115       case BO_AddAssign:
00116       case BO_SubAssign:
00117       case BO_MulAssign:
00118       case BO_DivAssign:
00119       case BO_AndAssign:
00120       case BO_OrAssign:
00121       case BO_XorAssign:
00122       case BO_ShlAssign:
00123       case BO_ShrAssign: {
00124         const VarDecl *VD = dyn_cast<VarDecl>(LHSDecl);
00125         // The DeclRefExpr is being assigned to - mark it as non-constant
00126         if (VD)
00127           NonConstants->insert(VD);
00128         break;
00129       }
00130 
00131       default:
00132         break;
00133       }
00134       break;
00135     }
00136 
00137     // Case 2: Pre/post increment/decrement and address of
00138     case Stmt::UnaryOperatorClass: {
00139       const UnaryOperator *UO = cast<UnaryOperator>(Head);
00140 
00141       // Look for a DeclRef in the subexpression
00142       const Decl *D = getDecl(UO->getSubExpr()->IgnoreParenCasts());
00143       if (!D)
00144         break;
00145 
00146       // We found a unary operator with a DeclRef as a subexpression. We now
00147       // check for any of the increment/decrement operators, as well as
00148       // addressOf.
00149       switch (UO->getOpcode()) {
00150       case UO_PostDec:
00151       case UO_PostInc:
00152       case UO_PreDec:
00153       case UO_PreInc:
00154         // The DeclRef is being changed - mark it as non-constant
00155       case UO_AddrOf: {
00156         // If we are taking the address of the DeclRefExpr, assume it is
00157         // non-constant.
00158         const VarDecl *VD = dyn_cast<VarDecl>(D);
00159         if (VD)
00160           NonConstants->insert(VD);
00161         break;
00162       }
00163 
00164       default:
00165         break;
00166       }
00167       break;
00168     }
00169 
00170     // Case 3: Reference Declarations
00171     case Stmt::DeclStmtClass: {
00172       const DeclStmt *DS = cast<DeclStmt>(Head);
00173       // Iterate over each decl and see if any of them contain reference decls
00174       for (const auto *I : DS->decls()) {
00175         // We only care about VarDecls
00176         const VarDecl *VD = dyn_cast<VarDecl>(I);
00177         if (!VD)
00178           continue;
00179 
00180         // We found a VarDecl; make sure it is a reference type
00181         if (!VD->getType().getTypePtr()->isReferenceType())
00182           continue;
00183 
00184         // Try to find a Decl in the initializer
00185         const Decl *D = getDecl(VD->getInit()->IgnoreParenCasts());
00186         if (!D)
00187           break;
00188 
00189         // If the reference is to another var, add the var to the non-constant
00190         // list
00191         if (const VarDecl *RefVD = dyn_cast<VarDecl>(D)) {
00192           NonConstants->insert(RefVD);
00193           continue;
00194         }
00195       }
00196       break;
00197     }
00198 
00199     // Case 4: Variable references
00200     case Stmt::DeclRefExprClass: {
00201       const DeclRefExpr *DR = cast<DeclRefExpr>(Head);
00202       if (const VarDecl *VD = dyn_cast<VarDecl>(DR->getDecl())) {
00203         // Add the Decl to the used list
00204         UsedVars->insert(VD);
00205         continue;
00206       }
00207       break;
00208     }
00209 
00210     // Case 5: Block expressions
00211     case Stmt::BlockExprClass: {
00212       const BlockExpr *B = cast<BlockExpr>(Head);
00213       // Add the body of the block to the list
00214       WorkList.push_back(B->getBody());
00215       continue;
00216     }
00217 
00218     default:
00219       break;
00220     } // switch (head->getStmtClass())
00221 
00222     // Add all substatements to the worklist
00223     for (Stmt::const_child_range I = Head->children(); I; ++I)
00224       if (*I)
00225         WorkList.push_back(*I);
00226   } // while (!WorkList.empty())
00227 }