diff options
author | Chris Lattner <sabre@nondot.org> | 2008-03-15 23:59:48 +0000 |
---|---|---|
committer | Chris Lattner <sabre@nondot.org> | 2008-03-15 23:59:48 +0000 |
commit | bda0b626e74513950405c27525af87e214e605e2 (patch) | |
tree | 60149b18fd68ccc1281c62fe4387b5a1da39a5fa /lib/Analysis/UninitializedValues.cpp | |
parent | fbdeba1c530dc3534a6f5b788e43d1a43c260128 (diff) |
Make a major restructuring of the clang tree: introduce a top-level
lib dir and move all the libraries into it. This follows the main
llvm tree, and allows the libraries to be built in parallel. The
top level now enforces that all the libs are built before Driver,
but we don't care what order the libs are built in. This speeds
up parallel builds, particularly incremental ones.
git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@48402 91177308-0d34-0410-b5e6-96231b3b80d8
Diffstat (limited to 'lib/Analysis/UninitializedValues.cpp')
-rw-r--r-- | lib/Analysis/UninitializedValues.cpp | 277 |
1 files changed, 277 insertions, 0 deletions
diff --git a/lib/Analysis/UninitializedValues.cpp b/lib/Analysis/UninitializedValues.cpp new file mode 100644 index 0000000000..25a5ecb483 --- /dev/null +++ b/lib/Analysis/UninitializedValues.cpp @@ -0,0 +1,277 @@ +//==- UninitializedValues.cpp - Find Unintialized Values --------*- C++ --*-==// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file implements Uninitialized Values analysis for source-level CFGs. +// +//===----------------------------------------------------------------------===// + +#include "clang/Analysis/Analyses/UninitializedValues.h" +#include "clang/Analysis/Visitors/CFGRecStmtDeclVisitor.h" +#include "clang/Analysis/LocalCheckers.h" +#include "clang/Basic/Diagnostic.h" +#include "clang/AST/ASTContext.h" +#include "clang/Analysis/FlowSensitive/DataflowSolver.h" +#include "llvm/Support/Compiler.h" + +#include "llvm/ADT/SmallPtrSet.h" + +using namespace clang; + +//===----------------------------------------------------------------------===// +// Dataflow initialization logic. +//===----------------------------------------------------------------------===// + +namespace { + +class VISIBILITY_HIDDEN RegisterDecls + : public CFGRecStmtDeclVisitor<RegisterDecls> { + + UninitializedValues::AnalysisDataTy& AD; +public: + RegisterDecls(UninitializedValues::AnalysisDataTy& ad) : AD(ad) {} + + void VisitBlockVarDecl(BlockVarDecl* VD) { AD.Register(VD); } + CFG& getCFG() { return AD.getCFG(); } +}; + +} // end anonymous namespace + +void UninitializedValues::InitializeValues(const CFG& cfg) { + RegisterDecls R(getAnalysisData()); + cfg.VisitBlockStmts(R); +} + +//===----------------------------------------------------------------------===// +// Transfer functions. +//===----------------------------------------------------------------------===// + +namespace { +class VISIBILITY_HIDDEN TransferFuncs + : public CFGStmtVisitor<TransferFuncs,bool> { + + UninitializedValues::ValTy V; + UninitializedValues::AnalysisDataTy& AD; +public: + TransferFuncs(UninitializedValues::AnalysisDataTy& ad) : AD(ad) { + V.resetValues(AD); + } + + UninitializedValues::ValTy& getVal() { return V; } + CFG& getCFG() { return AD.getCFG(); } + + bool VisitDeclRefExpr(DeclRefExpr* DR); + bool VisitBinaryOperator(BinaryOperator* B); + bool VisitUnaryOperator(UnaryOperator* U); + bool VisitStmt(Stmt* S); + bool VisitCallExpr(CallExpr* C); + bool VisitDeclStmt(DeclStmt* D); + bool VisitConditionalOperator(ConditionalOperator* C); + + bool Visit(Stmt *S); + bool BlockStmt_VisitExpr(Expr* E); + + BlockVarDecl* FindBlockVarDecl(Stmt* S); +}; + +static const bool Initialized = true; +static const bool Uninitialized = false; + +bool TransferFuncs::VisitDeclRefExpr(DeclRefExpr* DR) { + if (BlockVarDecl* VD = dyn_cast<BlockVarDecl>(DR->getDecl())) { + if (AD.Observer) AD.Observer->ObserveDeclRefExpr(V,AD,DR,VD); + + // Pseudo-hack to prevent cascade of warnings. If an accessed variable + // is uninitialized, then we are already going to flag a warning for + // this variable, which a "source" of uninitialized values. + // We can otherwise do a full "taint" of uninitialized values. The + // client has both options by toggling AD.FullUninitTaint. + + return AD.FullUninitTaint ? V(VD,AD) : Initialized; + } + else return Initialized; +} + +BlockVarDecl* TransferFuncs::FindBlockVarDecl(Stmt *S) { + for (;;) + if (ParenExpr* P = dyn_cast<ParenExpr>(S)) { + S = P->getSubExpr(); continue; + } + else if (DeclRefExpr* DR = dyn_cast<DeclRefExpr>(S)) { + if (BlockVarDecl* VD = dyn_cast<BlockVarDecl>(DR->getDecl())) + return VD; + else + return NULL; + } + else return NULL; +} + +bool TransferFuncs::VisitBinaryOperator(BinaryOperator* B) { + if (BlockVarDecl* VD = FindBlockVarDecl(B->getLHS())) + if (B->isAssignmentOp()) { + if (B->getOpcode() == BinaryOperator::Assign) + return V(VD,AD) = Visit(B->getRHS()); + else // Handle +=, -=, *=, etc. We do want '&', not '&&'. + return V(VD,AD) = Visit(B->getLHS()) & Visit(B->getRHS()); + } + + return VisitStmt(B); +} + +bool TransferFuncs::VisitDeclStmt(DeclStmt* S) { + for (ScopedDecl* D = S->getDecl(); D != NULL; D = D->getNextDeclarator()) + if (BlockVarDecl* VD = dyn_cast<BlockVarDecl>(D)) { + if (Stmt* I = VD->getInit()) + V(VD,AD) = AD.FullUninitTaint ? V(cast<Expr>(I),AD) : Initialized; + else { + // Special case for declarations of array types. For things like: + // + // char x[10]; + // + // we should treat "x" as being initialized, because the variable + // "x" really refers to the memory block. Clearly x[1] is + // uninitialized, but expressions like "(char *) x" really do refer to + // an initialized value. This simple dataflow analysis does not reason + // about the contents of arrays, although it could be potentially + // extended to do so if the array were of constant size. + if (VD->getType()->isArrayType()) + V(VD,AD) = Initialized; + else + V(VD,AD) = Uninitialized; + } + } + + return Uninitialized; // Value is never consumed. +} + +bool TransferFuncs::VisitCallExpr(CallExpr* C) { + VisitChildren(C); + return Initialized; +} + +bool TransferFuncs::VisitUnaryOperator(UnaryOperator* U) { + switch (U->getOpcode()) { + case UnaryOperator::AddrOf: + if (BlockVarDecl* VD = FindBlockVarDecl(U->getSubExpr())) + return V(VD,AD) = Initialized; + + break; + + case UnaryOperator::SizeOf: + return Initialized; + + default: + break; + } + + return Visit(U->getSubExpr()); +} + +bool TransferFuncs::VisitConditionalOperator(ConditionalOperator* C) { + Visit(C->getCond()); + + bool rhsResult = Visit(C->getRHS()); + // Handle the GNU extension for missing LHS. + if (Expr *lhs = C->getLHS()) + return Visit(lhs) & rhsResult; // Yes: we want &, not &&. + else + return rhsResult; +} + +bool TransferFuncs::VisitStmt(Stmt* S) { + bool x = Initialized; + + // We don't stop at the first subexpression that is Uninitialized because + // evaluating some subexpressions may result in propogating "Uninitialized" + // or "Initialized" to variables referenced in the other subexpressions. + for (Stmt::child_iterator I=S->child_begin(), E=S->child_end(); I!=E; ++I) + if (*I && Visit(*I) == Uninitialized) x = Uninitialized; + + return x; +} + +bool TransferFuncs::Visit(Stmt *S) { + if (AD.isTracked(static_cast<Expr*>(S))) return V(static_cast<Expr*>(S),AD); + else return static_cast<CFGStmtVisitor<TransferFuncs,bool>*>(this)->Visit(S); +} + +bool TransferFuncs::BlockStmt_VisitExpr(Expr* E) { + bool x = static_cast<CFGStmtVisitor<TransferFuncs,bool>*>(this)->Visit(E); + if (AD.isTracked(E)) V(E,AD) = x; + return x; +} + +} // end anonymous namespace + +//===----------------------------------------------------------------------===// +// Merge operator. +// +// In our transfer functions we take the approach that any +// combination of unintialized values, e.g. Unitialized + ___ = Unitialized. +// +// Merges take the opposite approach. +// +// In the merge of dataflow values we prefer unsoundness, and +// prefer false negatives to false positives. At merges, if a value for a +// tracked Decl is EVER initialized in any of the predecessors we treat it as +// initialized at the confluence point. +//===----------------------------------------------------------------------===// + +namespace { + typedef ExprDeclBitVector_Types::Union Merge; + typedef DataflowSolver<UninitializedValues,TransferFuncs,Merge> Solver; +} + +//===----------------------------------------------------------------------===// +// Unitialized values checker. Scan an AST and flag variable uses +//===----------------------------------------------------------------------===// + +UninitializedValues_ValueTypes::ObserverTy::~ObserverTy() {} + +namespace { +class VISIBILITY_HIDDEN UninitializedValuesChecker + : public UninitializedValues::ObserverTy { + + ASTContext &Ctx; + Diagnostic &Diags; + llvm::SmallPtrSet<BlockVarDecl*,10> AlreadyWarned; + +public: + UninitializedValuesChecker(ASTContext &ctx, Diagnostic &diags) + : Ctx(ctx), Diags(diags) {} + + virtual void ObserveDeclRefExpr(UninitializedValues::ValTy& V, + UninitializedValues::AnalysisDataTy& AD, + DeclRefExpr* DR, BlockVarDecl* VD) { + + assert ( AD.isTracked(VD) && "Unknown VarDecl."); + + if (V(VD,AD) == Uninitialized) + if (AlreadyWarned.insert(VD)) + Diags.Report(Ctx.getFullLoc(DR->getSourceRange().getBegin()), + diag::warn_uninit_val); + } +}; +} // end anonymous namespace + +namespace clang { +void CheckUninitializedValues(CFG& cfg, ASTContext &Ctx, Diagnostic &Diags, + bool FullUninitTaint) { + + // Compute the unitialized values information. + UninitializedValues U(cfg); + U.getAnalysisData().FullUninitTaint = FullUninitTaint; + Solver S(U); + S.runOnCFG(cfg); + + // Scan for DeclRefExprs that use uninitialized values. + UninitializedValuesChecker Observer(Ctx,Diags); + U.getAnalysisData().Observer = &Observer; + S.runOnAllBlocks(cfg); +} +} // end namespace clang |