PseudoConstantAnalysis.cpp revision 2c497ccd842431b6d143be0fefb45821ad1940a7
1//== PseudoConstantAnalysis.cpp - Find Pseudoconstants in the AST-*- C++ -*-==//
2//
3//                     The LLVM Compiler Infrastructure
4//
5// This file is distributed under the University of Illinois Open Source
6// License. See LICENSE.TXT for details.
7//
8//===----------------------------------------------------------------------===//
9//
10// This file tracks the usage of variables in a Decl body to see if they are
11// never written to, implying that they constant. This is useful in static
12// analysis to see if a developer might have intended a variable to be const.
13//
14//===----------------------------------------------------------------------===//
15
16#include "clang/Analysis/Analyses/PseudoConstantAnalysis.h"
17#include "clang/AST/Decl.h"
18#include "clang/AST/Expr.h"
19#include "clang/AST/Stmt.h"
20#include <deque>
21
22using namespace clang;
23
24// The number of ValueDecls we want to keep track of by default (per-function)
25#define VARDECL_SET_SIZE 256
26typedef llvm::SmallPtrSet<const VarDecl*, VARDECL_SET_SIZE> VarDeclSet;
27
28PseudoConstantAnalysis::PseudoConstantAnalysis(const Stmt *DeclBody) :
29      DeclBody(DeclBody), Analyzed(false) {
30  NonConstantsImpl = new VarDeclSet;
31}
32
33PseudoConstantAnalysis::~PseudoConstantAnalysis() {
34  delete (VarDeclSet*)NonConstantsImpl;
35}
36
37// Returns true if the given ValueDecl is never written to in the given DeclBody
38bool PseudoConstantAnalysis::isPseudoConstant(const VarDecl *VD) {
39  // Only local and static variables can be pseudoconstants
40  if (!VD->hasLocalStorage() && !VD->isStaticLocal())
41    return false;
42
43  if (!Analyzed) {
44    RunAnalysis();
45    Analyzed = true;
46  }
47
48  VarDeclSet *NonConstants = (VarDeclSet*)NonConstantsImpl;
49
50  return !NonConstants->count(VD);
51}
52
53void PseudoConstantAnalysis::RunAnalysis() {
54  std::deque<const Stmt *> WorkList;
55  VarDeclSet *NonConstants = (VarDeclSet*)NonConstantsImpl;
56
57  // Start with the top level statement of the function
58  WorkList.push_back(DeclBody);
59
60  while (!WorkList.empty()) {
61    const Stmt* Head = WorkList.front();
62    WorkList.pop_front();
63
64    switch (Head->getStmtClass()) {
65    // Case 1: Assignment operators modifying ValueDecl
66    case Stmt::BinaryOperatorClass: {
67      const BinaryOperator *BO = cast<BinaryOperator>(Head);
68      const Expr *LHS = BO->getLHS()->IgnoreParenImpCasts();
69      const DeclRefExpr *DR = dyn_cast<DeclRefExpr>(LHS);
70
71      // We only care about DeclRefExprs on the LHS
72      if (!DR)
73        break;
74
75      // We found a binary operator with a DeclRefExpr on the LHS. We now check
76      // for any of the assignment operators, implying that this Decl is being
77      // written to.
78      switch (BO->getOpcode()) {
79      case BinaryOperator::Assign:
80      case BinaryOperator::AddAssign:
81      case BinaryOperator::SubAssign:
82      case BinaryOperator::MulAssign:
83      case BinaryOperator::DivAssign:
84      case BinaryOperator::AndAssign:
85      case BinaryOperator::OrAssign:
86      case BinaryOperator::XorAssign:
87      case BinaryOperator::ShlAssign:
88      case BinaryOperator::ShrAssign: {
89        // The DeclRefExpr is being assigned to - mark it as non-constant
90        const VarDecl *VD = dyn_cast<VarDecl>(DR->getDecl());
91        if (VD)
92          NonConstants->insert(VD);
93        break;
94      }
95
96      default:
97        break;
98      }
99      break;
100    }
101
102    // Case 2: Pre/post increment/decrement and address of
103    case Stmt::UnaryOperatorClass: {
104      const UnaryOperator *UO = cast<UnaryOperator>(Head);
105      const Expr *SubExpr = UO->getSubExpr()->IgnoreParenImpCasts();
106      const DeclRefExpr *DR = dyn_cast<DeclRefExpr>(SubExpr);
107
108      // We only care about DeclRefExprs in the subexpression
109      if (!DR)
110        break;
111
112      // We found a unary operator with a DeclRefExpr as a subexpression. We now
113      // check for any of the increment/decrement operators, as well as
114      // addressOf.
115      switch (UO->getOpcode()) {
116      case UnaryOperator::PostDec:
117      case UnaryOperator::PostInc:
118      case UnaryOperator::PreDec:
119      case UnaryOperator::PreInc:
120        // The DeclRefExpr is being changed - mark it as non-constant
121      case UnaryOperator::AddrOf: {
122        // If we are taking the address of the DeclRefExpr, assume it is
123        // non-constant.
124        const VarDecl *VD = dyn_cast<VarDecl>(DR->getDecl());
125        if (VD)
126          NonConstants->insert(VD);
127        break;
128      }
129
130      default:
131        break;
132      }
133      break;
134    }
135
136    // Case 3: Reference Declarations
137    case Stmt::DeclStmtClass: {
138      const DeclStmt *DS = cast<DeclStmt>(Head);
139      // Iterate over each decl and see if any of them contain reference decls
140      for (DeclStmt::const_decl_iterator I = DS->decl_begin(), E = DS->decl_end();
141          I != E; ++I) {
142        // We only care about VarDecls
143        const VarDecl *VD = dyn_cast<VarDecl>(*I);
144        if (!VD)
145          continue;
146
147        // We found a VarDecl; make sure it is a reference type
148        if (!VD->getType().getTypePtr()->isReferenceType())
149          continue;
150
151        // Ignore VarDecls without a body
152        if (!VD->getBody())
153          continue;
154
155        // If the reference is to another var, add the var to the non-constant
156        // list
157        if (const DeclRefExpr *DR = dyn_cast<DeclRefExpr>(VD->getBody()))
158          if (const VarDecl *RefVD = dyn_cast<VarDecl>(DR->getDecl()))
159            NonConstants->insert(RefVD);
160      }
161    }
162
163      default:
164        break;
165    } // switch (head->getStmtClass())
166
167    // Add all substatements to the worklist
168    for (Stmt::const_child_iterator I = Head->child_begin(),
169        E = Head->child_end(); I != E; ++I)
170      if (*I)
171        WorkList.push_back(*I);
172  } // while (!WorkList.empty())
173}
174