- Added wasReferenced function to PseudoConstantAnalysis to determine if a variable was ever referenced in a function (outside of a self-assignment) - BlockDeclRefExpr referenced variables are now explicitly added to the non-constant list - Remove unnecessary ignore of implicit casts - Generalized parameter self-assign detection to detect deliberate self-assigns of variables to avoid unused variable warnings - Updated test cases with deliberate self-assignments - Fixed bug with C++ references and pseudoconstants - Added test case for C++ references and pseudoconstants llvm-svn: 111965
219 lines
6.9 KiB
C++
219 lines
6.9 KiB
C++
//== 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 <deque>
|
|
|
|
using namespace clang;
|
|
|
|
// The number of ValueDecls we want to keep track of by default (per-function)
|
|
#define VARDECL_SET_SIZE 256
|
|
typedef llvm::SmallPtrSet<const VarDecl*, VARDECL_SET_SIZE> 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);
|
|
}
|
|
|
|
void PseudoConstantAnalysis::RunAnalysis() {
|
|
std::deque<const Stmt *> 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();
|
|
|
|
switch (Head->getStmtClass()) {
|
|
// Case 1: Assignment operators modifying ValueDecl
|
|
case Stmt::BinaryOperatorClass: {
|
|
const BinaryOperator *BO = cast<BinaryOperator>(Head);
|
|
const Expr *LHS = BO->getLHS()->IgnoreParenCasts();
|
|
const DeclRefExpr *DR = dyn_cast<DeclRefExpr>(LHS);
|
|
|
|
// We only care about DeclRefExprs on the LHS
|
|
if (!DR)
|
|
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()) {
|
|
case BinaryOperator::Assign: {
|
|
const Expr *RHS = BO->getRHS()->IgnoreParenCasts();
|
|
if (const DeclRefExpr *RHSDecl = dyn_cast<DeclRefExpr>(RHS)) {
|
|
// Self-assignments don't count as use of a variable
|
|
if (DR->getDecl() == RHSDecl->getDecl())
|
|
// Do not visit the children
|
|
continue;
|
|
}
|
|
|
|
}
|
|
case BinaryOperator::AddAssign:
|
|
case BinaryOperator::SubAssign:
|
|
case BinaryOperator::MulAssign:
|
|
case BinaryOperator::DivAssign:
|
|
case BinaryOperator::AndAssign:
|
|
case BinaryOperator::OrAssign:
|
|
case BinaryOperator::XorAssign:
|
|
case BinaryOperator::ShlAssign:
|
|
case BinaryOperator::ShrAssign: {
|
|
// The DeclRefExpr is being assigned to - mark it as non-constant
|
|
const VarDecl *VD = dyn_cast<VarDecl>(DR->getDecl());
|
|
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<UnaryOperator>(Head);
|
|
const Expr *SubExpr = UO->getSubExpr()->IgnoreParenImpCasts();
|
|
const DeclRefExpr *DR = dyn_cast<DeclRefExpr>(SubExpr);
|
|
|
|
// We only care about DeclRefExprs in the subexpression
|
|
if (!DR)
|
|
break;
|
|
|
|
// We found a unary operator with a DeclRefExpr as a subexpression. We now
|
|
// check for any of the increment/decrement operators, as well as
|
|
// addressOf.
|
|
switch (UO->getOpcode()) {
|
|
case UnaryOperator::PostDec:
|
|
case UnaryOperator::PostInc:
|
|
case UnaryOperator::PreDec:
|
|
case UnaryOperator::PreInc:
|
|
// The DeclRefExpr is being changed - mark it as non-constant
|
|
case UnaryOperator::AddrOf: {
|
|
// If we are taking the address of the DeclRefExpr, assume it is
|
|
// non-constant.
|
|
const VarDecl *VD = dyn_cast<VarDecl>(DR->getDecl());
|
|
if (VD)
|
|
NonConstants->insert(VD);
|
|
break;
|
|
}
|
|
|
|
default:
|
|
break;
|
|
}
|
|
break;
|
|
}
|
|
|
|
// Case 3: Reference Declarations
|
|
case Stmt::DeclStmtClass: {
|
|
const DeclStmt *DS = cast<DeclStmt>(Head);
|
|
// Iterate over each decl and see if any of them contain reference decls
|
|
for (DeclStmt::const_decl_iterator I = DS->decl_begin(), E = DS->decl_end();
|
|
I != E; ++I) {
|
|
// We only care about VarDecls
|
|
const VarDecl *VD = dyn_cast<VarDecl>(*I);
|
|
if (!VD)
|
|
continue;
|
|
|
|
// We found a VarDecl; make sure it is a reference type
|
|
if (!VD->getType().getTypePtr()->isReferenceType())
|
|
continue;
|
|
|
|
// If the reference is to another var, add the var to the non-constant
|
|
// list
|
|
if (const DeclRefExpr *DR = dyn_cast<DeclRefExpr>(VD->getInit()))
|
|
if (const VarDecl *RefVD = dyn_cast<VarDecl>(DR->getDecl())) {
|
|
NonConstants->insert(RefVD);
|
|
continue;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
|
|
// Case 4: Block variable references
|
|
case Stmt::BlockDeclRefExprClass: {
|
|
// Any block variables are assumed to be non-constant
|
|
const BlockDeclRefExpr *BDR = cast<BlockDeclRefExpr>(Head);
|
|
if (const VarDecl *VD = dyn_cast<VarDecl>(BDR->getDecl())) {
|
|
NonConstants->insert(VD);
|
|
UsedVars->insert(VD);
|
|
continue;
|
|
}
|
|
break;
|
|
}
|
|
|
|
// Case 5: Variable references
|
|
case Stmt::DeclRefExprClass: {
|
|
const DeclRefExpr *DR = cast<DeclRefExpr>(Head);
|
|
if (const VarDecl *VD = dyn_cast<VarDecl>(DR->getDecl())) {
|
|
UsedVars->insert(VD);
|
|
continue;
|
|
}
|
|
break;
|
|
}
|
|
|
|
default:
|
|
break;
|
|
} // switch (head->getStmtClass())
|
|
|
|
// Add all substatements to the worklist
|
|
for (Stmt::const_child_iterator I = Head->child_begin(),
|
|
E = Head->child_end(); I != E; ++I)
|
|
if (*I)
|
|
WorkList.push_back(*I);
|
|
} // while (!WorkList.empty())
|
|
}
|