-//== PsuedoConstantAnalysis.h - Find Psuedo-constants in the AST -*- C++ -*-==//
+//== PseudoConstantAnalysis.h - Find Pseudo-constants in the AST -*- C++ -*-==//
//
// The LLVM Compiler Infrastructure
//
//
//===----------------------------------------------------------------------===//
-#ifndef LLVM_CLANG_ANALYSIS_PSUEDOCONSTANTANALYSIS
-#define LLVM_CLANG_ANALYSIS_PSUEDOCONSTANTANALYSIS
+#ifndef LLVM_CLANG_ANALYSIS_PSEUDOCONSTANTANALYSIS
+#define LLVM_CLANG_ANALYSIS_PSEUDOCONSTANTANALYSIS
#include "clang/AST/Stmt.h"
-// The number of ValueDecls we want to keep track of by default (per-function)
-#define VALUEDECL_SET_SIZE 256
-
namespace clang {
-class PsuedoConstantAnalysis {
+class PseudoConstantAnalysis {
public:
- PsuedoConstantAnalysis(const Stmt *DeclBody) :
- DeclBody(DeclBody), Analyzed(false) {}
- bool isPsuedoConstant(const ValueDecl *VD);
+ PseudoConstantAnalysis(const Stmt *DeclBody);
+ ~PseudoConstantAnalysis();
+
+ bool isPseudoConstant(const VarDecl *VD);
private:
void RunAnalysis();
// for storing the result of analyzed ValueDecls
- llvm::SmallPtrSet<const ValueDecl*, VALUEDECL_SET_SIZE> NonConstants;
+ void *NonConstantsImpl;
const Stmt *DeclBody;
bool Analyzed;
class CFGBlock;
class LiveVariables;
class ParentMap;
-class PsuedoConstantAnalysis;
+class PseudoConstantAnalysis;
class ImplicitParamDecl;
class LocationContextManager;
class StackFrameContext;
bool builtCFG, builtCompleteCFG;
LiveVariables *liveness;
ParentMap *PM;
- PsuedoConstantAnalysis *PCA;
+ PseudoConstantAnalysis *PCA;
llvm::DenseMap<const BlockDecl*,void*> *ReferencedBlockVars;
llvm::BumpPtrAllocator A;
bool UseUnoptimizedCFG;
CFG *getUnoptimizedCFG();
ParentMap &getParentMap();
- PsuedoConstantAnalysis *getPsuedoConstantAnalysis();
+ PseudoConstantAnalysis *getPseudoConstantAnalysis();
LiveVariables *getLiveVariables();
typedef const VarDecl * const * referenced_decls_iterator;
#include "clang/AST/ParentMap.h"
#include "clang/AST/StmtVisitor.h"
#include "clang/Analysis/Analyses/LiveVariables.h"
-#include "clang/Analysis/Analyses/PsuedoConstantAnalysis.h"
+#include "clang/Analysis/Analyses/PseudoConstantAnalysis.h"
#include "clang/Analysis/AnalysisContext.h"
#include "clang/Analysis/CFG.h"
#include "clang/Analysis/Support/BumpVector.h"
return *PM;
}
-PsuedoConstantAnalysis *AnalysisContext::getPsuedoConstantAnalysis() {
+PseudoConstantAnalysis *AnalysisContext::getPseudoConstantAnalysis() {
if (!PCA)
- PCA = new PsuedoConstantAnalysis(getBody());
+ PCA = new PseudoConstantAnalysis(getBody());
return PCA;
}
FormatString.cpp
LiveVariables.cpp
PrintfFormatString.cpp
- PsuedoConstantAnalysis.cpp
+ PseudoConstantAnalysis.cpp
ReachableCode.cpp
ScanfFormatString.cpp
UninitializedValues.cpp
-//== PsuedoConstantAnalysis.cpp - Find Psuedoconstants in the AST-*- C++ -*-==//
+//== PseudoConstantAnalysis.cpp - Find Pseudoconstants in the AST-*- C++ -*-==//
//
// The LLVM Compiler Infrastructure
//
//
//===----------------------------------------------------------------------===//
-#include "clang/Analysis/Analyses/PsuedoConstantAnalysis.h"
+#include "clang/Analysis/Analyses/PseudoConstantAnalysis.h"
#include "clang/AST/Decl.h"
#include "clang/AST/Expr.h"
#include "clang/AST/Stmt.h"
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;
+}
+
+PseudoConstantAnalysis::~PseudoConstantAnalysis() {
+ delete (VarDeclSet*)NonConstantsImpl;
+}
+
// Returns true if the given ValueDecl is never written to in the given DeclBody
-bool PsuedoConstantAnalysis::isPsuedoConstant(const ValueDecl *VD) {
+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;
}
- return !NonConstants.count(VD);
+ VarDeclSet *NonConstants = (VarDeclSet*)NonConstantsImpl;
+
+ return !NonConstants->count(VD);
}
-void PsuedoConstantAnalysis::RunAnalysis() {
+void PseudoConstantAnalysis::RunAnalysis() {
std::deque<const Stmt *> WorkList;
+ VarDeclSet *NonConstants = (VarDeclSet*)NonConstantsImpl;
// Start with the top level statement of the function
WorkList.push_back(DeclBody);
case BinaryOperator::OrAssign:
case BinaryOperator::XorAssign:
case BinaryOperator::ShlAssign:
- case BinaryOperator::ShrAssign:
+ case BinaryOperator::ShrAssign: {
// The DeclRefExpr is being assigned to - mark it as non-constant
- NonConstants.insert(DR->getDecl());
- continue; // Continue without looking at children
+ const VarDecl *VD = dyn_cast<VarDecl>(DR->getDecl());
+ if (VD)
+ NonConstants->insert(VD);
+ break;
+ }
default:
break;
case UnaryOperator::PreDec:
case UnaryOperator::PreInc:
// The DeclRefExpr is being changed - mark it as non-constant
- case UnaryOperator::AddrOf:
+ case UnaryOperator::AddrOf: {
// If we are taking the address of the DeclRefExpr, assume it is
// non-constant.
- NonConstants.insert(DR->getDecl());
+ 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;
+
+ // Ignore VarDecls without a body
+ if (!VD->getBody())
+ continue;
+
+ // If the reference is to another var, add the var to the non-constant
+ // list
+ if (const DeclRefExpr *DR = dyn_cast<DeclRefExpr>(VD->getBody()))
+ if (const VarDecl *RefVD = dyn_cast<VarDecl>(DR->getDecl()))
+ NonConstants->insert(RefVD);
+ }
+ }
+
default:
break;
} // switch (head->getStmtClass())
#include "GRExprEngineExperimentalChecks.h"
#include "clang/Analysis/CFGStmtMap.h"
-#include "clang/Analysis/Analyses/PsuedoConstantAnalysis.h"
+#include "clang/Analysis/Analyses/PseudoConstantAnalysis.h"
#include "clang/Checker/BugReporter/BugType.h"
#include "clang/Checker/PathSensitive/CheckerHelpers.h"
#include "clang/Checker/PathSensitive/CheckerVisitor.h"
static bool CanVary(const Expr *Ex, AnalysisContext *AC);
static bool isConstantOrPseudoConstant(const DeclRefExpr *DR,
AnalysisContext *AC);
+ static bool containsNonLocalVarDecl(const Stmt *S);
// Hash table
typedef llvm::DenseMap<const BinaryOperator *,
const Expr *LHS = B->getLHS();
const Expr *RHS = B->getRHS();
- // Check if either side can vary. We only need to calculate this when we have
- // no assumption.
- bool LHSCanVary = true, RHSCanVary = true;
+ // At this stage we can calculate whether each side contains a false positive
+ // that applies to all operators. We only need to calculate this the first
+ // time.
+ bool LHSContainsFalsePositive = false, RHSContainsFalsePositive = false;
if (A == Possible) {
- LHSCanVary = CanVary(LHS, AC);
- RHSCanVary = CanVary(RHS, AC);
+ // An expression contains a false positive if it can't vary, or if it
+ // contains a known false positive VarDecl.
+ LHSContainsFalsePositive = !CanVary(LHS, AC)
+ || containsNonLocalVarDecl(LHS);
+ RHSContainsFalsePositive = !CanVary(RHS, AC)
+ || containsNonLocalVarDecl(RHS);
}
const GRState *state = C.getState();
case BinaryOperator::Xor:
case BinaryOperator::LOr:
case BinaryOperator::LAnd:
- if (LHSVal != RHSVal || !LHSCanVary || !RHSCanVary)
+ if (LHSVal != RHSVal || LHSContainsFalsePositive
+ || RHSContainsFalsePositive)
break;
UpdateAssumption(A, Equal);
return;
case BinaryOperator::Div:
case BinaryOperator::LOr:
case BinaryOperator::LAnd:
- if (!RHSVal.isConstant(1) || !RHSCanVary)
+ if (!RHSVal.isConstant(1) || RHSContainsFalsePositive)
break;
UpdateAssumption(A, RHSis1);
return;
case BinaryOperator::Mul:
case BinaryOperator::LOr:
case BinaryOperator::LAnd:
- if (!LHSVal.isConstant(1) || !LHSCanVary)
+ if (!LHSVal.isConstant(1) || LHSContainsFalsePositive)
break;
UpdateAssumption(A, LHSis1);
return;
case BinaryOperator::Shr:
case BinaryOperator::LOr:
case BinaryOperator::LAnd:
- if (!RHSVal.isConstant(0) || !RHSCanVary)
+ if (!RHSVal.isConstant(0) || RHSContainsFalsePositive)
break;
UpdateAssumption(A, RHSis0);
return;
case BinaryOperator::Shr:
case BinaryOperator::LOr:
case BinaryOperator::LAnd:
- if (!LHSVal.isConstant(0) || !LHSCanVary)
+ if (!LHSVal.isConstant(0) || LHSContainsFalsePositive)
break;
UpdateAssumption(A, LHSis0);
return;
if (isa<EnumConstantDecl>(DR->getDecl()))
return true;
- // Check for a static variable
- // FIXME: Analysis should model static vars
- if (const VarDecl *VD = dyn_cast<VarDecl>(DR->getDecl()))
- if (VD->isStaticLocal())
- return true;
+ const VarDecl *VD = dyn_cast<VarDecl>(DR->getDecl());
+ if (!VD)
+ return true;
- // Check if the Decl behaves like a constant
- PsuedoConstantAnalysis *PCA = AC->getPsuedoConstantAnalysis();
- if (PCA->isPsuedoConstant(DR->getDecl()))
+ // Check if the Decl behaves like a constant. This check also takes care of
+ // static variables, which can only change between function calls if they are
+ // modified in the AST.
+ PseudoConstantAnalysis *PCA = AC->getPseudoConstantAnalysis();
+ if (PCA->isPseudoConstant(VD))
return true;
return false;
}
+
+// Recursively find any substatements containing VarDecl's with storage other
+// than local
+bool IdempotentOperationChecker::containsNonLocalVarDecl(const Stmt *S) {
+ const DeclRefExpr *DR = dyn_cast<DeclRefExpr>(S);
+
+ if (DR)
+ if (const VarDecl *VD = dyn_cast<VarDecl>(DR->getDecl()))
+ if (!VD->hasLocalStorage())
+ return true;
+
+ for (Stmt::const_child_iterator I = S->child_begin(); I != S->child_end();
+ ++I)
+ if (const Stmt *child = *I)
+ if (containsNonLocalVarDecl(child))
+ return true;
+
+ return false;
+}
-// RUN: %clang_cc1 -analyze -analyzer-store=region -analyzer-constraints=range -fblocks -verify -analyzer-opt-analyze-nested-blocks -analyzer-check-objc-mem -analyzer-check-idempotent-operations -verify %s
+// RUN: %clang_cc1 -analyze -analyzer-store=region -analyzer-constraints=range -fblocks -analyzer-opt-analyze-nested-blocks -analyzer-check-objc-mem -analyzer-check-idempotent-operations -verify %s
// Basic tests
test(zero << x); // expected-warning {{The left operand to '<<' is always 0}}
test(zero >> x); // expected-warning {{The left operand to '>>' is always 0}}
- // Overwrite the values so these aren't marked as psuedoconstants
+ // Overwrite the values so these aren't marked as Pseudoconstants
x = 1;
zero = 2;
one = 3;
return nonparam;
}
-// Psuedo-constants (vars only read) and constants should not be reported
+// Pseudo-constants (vars only read) and constants should not be reported
unsigned false4() {
// Trivial constant
- const int height = 1; // no-warning
- // Psuedo-constant (never changes after decl)
- int width = height; // no-warning
+ const int height = 1;
+
+ // Pseudo-constant (never changes after decl)
+ int width = height;
+
+ // Pseudo-constant (blockvar)
+ __block int a = 0;
+ int b = 10;
+ a *= b; // no-warning
+ test(a);
return width * 10; // no-warning
}
+
+// Static vars are common false positives
+int false5() {
+ static int test = 0;
+ int a = 56;
+ a *= test; // no-warning
+ test++;
+ return a;
+}
+
+// Non-local storage vars are considered false positives
+int globalInt = 1;
+int false6() {
+ int localInt = 23;
+
+ localInt /= globalInt;
+
+ return localInt;
+}