diff options
Diffstat (limited to 'contrib/llvm/tools/clang/lib/Analysis/UninitializedValues.cpp')
-rw-r--r-- | contrib/llvm/tools/clang/lib/Analysis/UninitializedValues.cpp | 326 |
1 files changed, 171 insertions, 155 deletions
diff --git a/contrib/llvm/tools/clang/lib/Analysis/UninitializedValues.cpp b/contrib/llvm/tools/clang/lib/Analysis/UninitializedValues.cpp index 1d6959d..9e98560 100644 --- a/contrib/llvm/tools/clang/lib/Analysis/UninitializedValues.cpp +++ b/contrib/llvm/tools/clang/lib/Analysis/UninitializedValues.cpp @@ -123,13 +123,7 @@ public: bool hasNoDeclarations() const { return declToIndex.size() == 0; } - - bool hasEntry(const VarDecl *vd) const { - return declToIndex.getValueIndex(vd).hasValue(); - } - - bool hasValues(const CFGBlock *block); - + void resetScratch(); ValueVector &getScratch() { return scratch; } @@ -170,7 +164,7 @@ ValueVector &CFGBlockValues::lazyCreate(ValueVector *&bv) { /// This function pattern matches for a '&&' or '||' that appears at /// the beginning of a CFGBlock that also (1) has a terminator and /// (2) has no other elements. If such an expression is found, it is returned. -static BinaryOperator *getLogicalOperatorInChain(const CFGBlock *block) { +static const BinaryOperator *getLogicalOperatorInChain(const CFGBlock *block) { if (block->empty()) return 0; @@ -178,7 +172,7 @@ static BinaryOperator *getLogicalOperatorInChain(const CFGBlock *block) { if (!cstmt) return 0; - BinaryOperator *b = llvm::dyn_cast_or_null<BinaryOperator>(cstmt->getStmt()); + const BinaryOperator *b = dyn_cast_or_null<BinaryOperator>(cstmt->getStmt()); if (!b || !b->isLogicalOp()) return 0; @@ -209,11 +203,6 @@ ValueVector &CFGBlockValues::getValueVector(const CFGBlock *block, return lazyCreate(vals[idx].first); } -bool CFGBlockValues::hasValues(const CFGBlock *block) { - unsigned idx = block->getBlockID(); - return vals[idx].second != 0; -} - BVPair &CFGBlockValues::getValueVectors(const clang::CFGBlock *block, bool shouldLazyCreate) { unsigned idx = block->getBlockID(); @@ -223,13 +212,6 @@ BVPair &CFGBlockValues::getValueVectors(const clang::CFGBlock *block, return vals[idx]; } -void CFGBlockValues::mergeIntoScratch(ValueVector const &source, - bool isFirst) { - if (isFirst) - scratch = source; - else - scratch |= source; -} #if 0 static void printVector(const CFGBlock *block, ValueVector &bv, unsigned num) { @@ -240,8 +222,24 @@ static void printVector(const CFGBlock *block, ValueVector &bv, } llvm::errs() << " : " << num << '\n'; } + +static void printVector(const char *name, ValueVector const &bv) { + llvm::errs() << name << " : "; + for (unsigned i = 0; i < bv.size(); ++i) { + llvm::errs() << ' ' << bv[i]; + } + llvm::errs() << "\n"; +} #endif +void CFGBlockValues::mergeIntoScratch(ValueVector const &source, + bool isFirst) { + if (isFirst) + scratch = source; + else + scratch |= source; +} + bool CFGBlockValues::updateValueVectorWithScratch(const CFGBlock *block) { ValueVector &dst = getValueVector(block, 0); bool changed = (dst != scratch); @@ -283,7 +281,7 @@ ValueVector::reference CFGBlockValues::operator[](const VarDecl *vd) { namespace { class DataflowWorklist { - llvm::SmallVector<const CFGBlock *, 20> worklist; + SmallVector<const CFGBlock *, 20> worklist; llvm::BitVector enqueuedBlocks; public: DataflowWorklist(const CFG &cfg) : enqueuedBlocks(cfg.getNumBlockIDs()) {} @@ -336,23 +334,34 @@ public: const VarDecl *getDecl() const { return vd; } }; -class TransferFunctions : public CFGRecStmtVisitor<TransferFunctions> { +class TransferFunctions : public StmtVisitor<TransferFunctions> { CFGBlockValues &vals; const CFG &cfg; AnalysisContext ∾ UninitVariablesHandler *handler; - const DeclRefExpr *currentDR; - const Expr *currentVoidCast; - const bool flagBlockUses; + + /// The last DeclRefExpr seen when analyzing a block. Used to + /// cheat when detecting cases when the address of a variable is taken. + DeclRefExpr *lastDR; + + /// The last lvalue-to-rvalue conversion of a variable whose value + /// was uninitialized. Normally this results in a warning, but it is + /// possible to either silence the warning in some cases, or we + /// propagate the uninitialized value. + CastExpr *lastLoad; + + /// For some expressions, we want to ignore any post-processing after + /// visitation. + bool skipProcessUses; + public: TransferFunctions(CFGBlockValues &vals, const CFG &cfg, AnalysisContext &ac, - UninitVariablesHandler *handler, - bool flagBlockUses) - : vals(vals), cfg(cfg), ac(ac), handler(handler), currentDR(0), - currentVoidCast(0), flagBlockUses(flagBlockUses) {} + UninitVariablesHandler *handler) + : vals(vals), cfg(cfg), ac(ac), handler(handler), + lastDR(0), lastLoad(0), + skipProcessUses(false) {} - const CFG &getCFG() { return cfg; } void reportUninit(const DeclRefExpr *ex, const VarDecl *vd, bool isAlwaysUninit); @@ -362,53 +371,59 @@ public: void VisitUnaryOperator(UnaryOperator *uo); void VisitBinaryOperator(BinaryOperator *bo); void VisitCastExpr(CastExpr *ce); - void VisitUnaryExprOrTypeTraitExpr(UnaryExprOrTypeTraitExpr *se); - void VisitCXXTypeidExpr(CXXTypeidExpr *E); - void BlockStmt_VisitObjCForCollectionStmt(ObjCForCollectionStmt *fs); + void VisitObjCForCollectionStmt(ObjCForCollectionStmt *fs); + void Visit(Stmt *s); bool isTrackedVar(const VarDecl *vd) { return ::isTrackedVar(vd, cast<DeclContext>(ac.getDecl())); } FindVarResult findBlockVarDecl(Expr *ex); + + void ProcessUses(Stmt *s = 0); }; } +static const Expr *stripCasts(ASTContext &C, const Expr *Ex) { + while (Ex) { + Ex = Ex->IgnoreParenNoopCasts(C); + if (const CastExpr *CE = dyn_cast<CastExpr>(Ex)) { + if (CE->getCastKind() == CK_LValueBitCast) { + Ex = CE->getSubExpr(); + continue; + } + } + break; + } + return Ex; +} + void TransferFunctions::reportUninit(const DeclRefExpr *ex, const VarDecl *vd, bool isAlwaysUnit) { if (handler) handler->handleUseOfUninitVariable(ex, vd, isAlwaysUnit); } -FindVarResult TransferFunctions::findBlockVarDecl(Expr* ex) { - if (DeclRefExpr* dr = dyn_cast<DeclRefExpr>(ex->IgnoreParenCasts())) +FindVarResult TransferFunctions::findBlockVarDecl(Expr *ex) { + if (DeclRefExpr *dr = dyn_cast<DeclRefExpr>(ex->IgnoreParenCasts())) if (VarDecl *vd = dyn_cast<VarDecl>(dr->getDecl())) if (isTrackedVar(vd)) return FindVarResult(vd, dr); return FindVarResult(0, 0); } -void TransferFunctions::BlockStmt_VisitObjCForCollectionStmt( - ObjCForCollectionStmt *fs) { - - Visit(fs->getCollection()); - +void TransferFunctions::VisitObjCForCollectionStmt(ObjCForCollectionStmt *fs) { // This represents an initialization of the 'element' value. Stmt *element = fs->getElement(); - const VarDecl* vd = 0; + const VarDecl *vd = 0; - if (DeclStmt* ds = dyn_cast<DeclStmt>(element)) { + if (DeclStmt *ds = dyn_cast<DeclStmt>(element)) { vd = cast<VarDecl>(ds->getSingleDecl()); if (!isTrackedVar(vd)) vd = 0; - } - else { + } else { // Initialize the value of the reference variable. const FindVarResult &res = findBlockVarDecl(cast<Expr>(element)); vd = res.getDecl(); - if (!vd) { - Visit(element); - return; - } } if (vd) @@ -416,14 +431,10 @@ void TransferFunctions::BlockStmt_VisitObjCForCollectionStmt( } void TransferFunctions::VisitBlockExpr(BlockExpr *be) { - if (!flagBlockUses || !handler) - return; const BlockDecl *bd = be->getBlockDecl(); for (BlockDecl::capture_const_iterator i = bd->capture_begin(), e = bd->capture_end() ; i != e; ++i) { const VarDecl *vd = i->getVariable(); - if (!vd->hasLocalStorage()) - continue; if (!isTrackedVar(vd)) continue; if (i->isByRef()) { @@ -431,19 +442,27 @@ void TransferFunctions::VisitBlockExpr(BlockExpr *be) { continue; } Value v = vals[vd]; - if (isUninitialized(v)) + if (handler && isUninitialized(v)) handler->handleUseOfUninitVariable(be, vd, isAlwaysUninit(v)); } } +void TransferFunctions::VisitDeclRefExpr(DeclRefExpr *dr) { + // Record the last DeclRefExpr seen. This is an lvalue computation. + // We use this value to later detect if a variable "escapes" the analysis. + if (const VarDecl *vd = dyn_cast<VarDecl>(dr->getDecl())) + if (isTrackedVar(vd)) { + ProcessUses(); + lastDR = dr; + } +} + void TransferFunctions::VisitDeclStmt(DeclStmt *ds) { for (DeclStmt::decl_iterator DI = ds->decl_begin(), DE = ds->decl_end(); DI != DE; ++DI) { if (VarDecl *vd = dyn_cast<VarDecl>(*DI)) { if (isTrackedVar(vd)) { if (Expr *init = vd->getInit()) { - Visit(init); - // If the initializer consists solely of a reference to itself, we // explicitly mark the variable as uninitialized. This allows code // like the following: @@ -454,56 +473,48 @@ void TransferFunctions::VisitDeclStmt(DeclStmt *ds) { // clients can detect this pattern and adjust their reporting // appropriately, but we need to continue to analyze subsequent uses // of the variable. - DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(init->IgnoreParenImpCasts()); - vals[vd] = (DRE && DRE->getDecl() == vd) ? Uninitialized - : Initialized; + if (init == lastLoad) { + const DeclRefExpr *DR + = cast<DeclRefExpr>(stripCasts(ac.getASTContext(), + lastLoad->getSubExpr())); + if (DR->getDecl() == vd) { + // int x = x; + // Propagate uninitialized value, but don't immediately report + // a problem. + vals[vd] = Uninitialized; + lastLoad = 0; + lastDR = 0; + if (handler) + handler->handleSelfInit(vd); + return; + } + } + + // All other cases: treat the new variable as initialized. + // This is a minor optimization to reduce the propagation + // of the analysis, since we will have already reported + // the use of the uninitialized value (which visiting the + // initializer). + vals[vd] = Initialized; } - } else if (Stmt *init = vd->getInit()) { - Visit(init); } } } } -void TransferFunctions::VisitDeclRefExpr(DeclRefExpr *dr) { - // We assume that DeclRefExprs wrapped in an lvalue-to-rvalue cast - // cannot be block-level expressions. Therefore, we determine if - // a DeclRefExpr is involved in a "load" by comparing it to the current - // DeclRefExpr found when analyzing the last lvalue-to-rvalue CastExpr. - // If a DeclRefExpr is not involved in a load, we are essentially computing - // its address, either for assignment to a reference or via the '&' operator. - // In such cases, treat the variable as being initialized, since this - // analysis isn't powerful enough to do alias tracking. - if (dr != currentDR) - if (const VarDecl *vd = dyn_cast<VarDecl>(dr->getDecl())) - if (isTrackedVar(vd)) - vals[vd] = Initialized; -} - void TransferFunctions::VisitBinaryOperator(clang::BinaryOperator *bo) { if (bo->isAssignmentOp()) { const FindVarResult &res = findBlockVarDecl(bo->getLHS()); - if (const VarDecl* vd = res.getDecl()) { - // We assume that DeclRefExprs wrapped in a BinaryOperator "assignment" - // cannot be block-level expressions. Therefore, we determine if - // a DeclRefExpr is involved in a "load" by comparing it to the current - // DeclRefExpr found when analyzing the last lvalue-to-rvalue CastExpr. - SaveAndRestore<const DeclRefExpr*> lastDR(currentDR, - res.getDeclRefExpr()); - Visit(bo->getRHS()); - Visit(bo->getLHS()); - + if (const VarDecl *vd = res.getDecl()) { ValueVector::reference val = vals[vd]; if (isUninitialized(val)) { if (bo->getOpcode() != BO_Assign) reportUninit(res.getDeclRefExpr(), vd, isAlwaysUninit(val)); - val = Initialized; + else + val = Initialized; } - return; } } - Visit(bo->getRHS()); - Visit(bo->getLHS()); } void TransferFunctions::VisitUnaryOperator(clang::UnaryOperator *uo) { @@ -514,86 +525,88 @@ void TransferFunctions::VisitUnaryOperator(clang::UnaryOperator *uo) { case clang::UO_PreInc: { const FindVarResult &res = findBlockVarDecl(uo->getSubExpr()); if (const VarDecl *vd = res.getDecl()) { - // We assume that DeclRefExprs wrapped in a unary operator ++/-- - // cannot be block-level expressions. Therefore, we determine if - // a DeclRefExpr is involved in a "load" by comparing it to the current - // DeclRefExpr found when analyzing the last lvalue-to-rvalue CastExpr. - SaveAndRestore<const DeclRefExpr*> lastDR(currentDR, - res.getDeclRefExpr()); - Visit(uo->getSubExpr()); + assert(res.getDeclRefExpr() == lastDR); + // We null out lastDR to indicate we have fully processed it + // and we don't want the auto-value setting in Visit(). + lastDR = 0; ValueVector::reference val = vals[vd]; - if (isUninitialized(val)) { + if (isUninitialized(val)) reportUninit(res.getDeclRefExpr(), vd, isAlwaysUninit(val)); - // Don't cascade warnings. - val = Initialized; - } - return; } break; } default: break; } - Visit(uo->getSubExpr()); } void TransferFunctions::VisitCastExpr(clang::CastExpr *ce) { if (ce->getCastKind() == CK_LValueToRValue) { const FindVarResult &res = findBlockVarDecl(ce->getSubExpr()); - if (const VarDecl *vd = res.getDecl()) { - // We assume that DeclRefExprs wrapped in an lvalue-to-rvalue cast - // cannot be block-level expressions. Therefore, we determine if - // a DeclRefExpr is involved in a "load" by comparing it to the current - // DeclRefExpr found when analyzing the last lvalue-to-rvalue CastExpr. - // Here we update 'currentDR' to be the one associated with this - // lvalue-to-rvalue cast. Then, when we analyze the DeclRefExpr, we - // will know that we are not computing its lvalue for other purposes - // than to perform a load. - SaveAndRestore<const DeclRefExpr*> lastDR(currentDR, - res.getDeclRefExpr()); - Visit(ce->getSubExpr()); - if (currentVoidCast != ce) { - Value val = vals[vd]; - if (isUninitialized(val)) { - reportUninit(res.getDeclRefExpr(), vd, isAlwaysUninit(val)); - // Don't cascade warnings. - vals[vd] = Initialized; - } - } - return; + if (res.getDecl()) { + assert(res.getDeclRefExpr() == lastDR); + lastLoad = ce; } } + else if (ce->getCastKind() == CK_NoOp || + ce->getCastKind() == CK_LValueBitCast) { + skipProcessUses = true; + } else if (CStyleCastExpr *cse = dyn_cast<CStyleCastExpr>(ce)) { if (cse->getType()->isVoidType()) { // e.g. (void) x; - SaveAndRestore<const Expr *> - lastVoidCast(currentVoidCast, cse->getSubExpr()->IgnoreParens()); - Visit(cse->getSubExpr()); - return; + if (lastLoad == cse->getSubExpr()) { + // Squelch any detected load of an uninitialized value if + // we cast it to void. + lastLoad = 0; + lastDR = 0; + } } } - Visit(ce->getSubExpr()); } -void TransferFunctions::VisitUnaryExprOrTypeTraitExpr( - UnaryExprOrTypeTraitExpr *se) { - if (se->getKind() == UETT_SizeOf) { - if (se->getType()->isConstantSizeType()) +void TransferFunctions::Visit(clang::Stmt *s) { + skipProcessUses = false; + StmtVisitor<TransferFunctions>::Visit(s); + if (!skipProcessUses) + ProcessUses(s); +} + +void TransferFunctions::ProcessUses(Stmt *s) { + // This method is typically called after visiting a CFGElement statement + // in the CFG. We delay processing of reporting many loads of uninitialized + // values until here. + if (lastLoad) { + // If we just visited the lvalue-to-rvalue cast, there is nothing + // left to do. + if (lastLoad == s) + return; + + const DeclRefExpr *DR = + cast<DeclRefExpr>(stripCasts(ac.getASTContext(), + lastLoad->getSubExpr())); + const VarDecl *VD = cast<VarDecl>(DR->getDecl()); + + // If we reach here, we may have seen a load of an uninitialized value + // and it hasn't been casted to void or otherwise handled. In this + // situation, report the incident. + if (isUninitialized(vals[VD])) + reportUninit(DR, VD, isAlwaysUninit(vals[VD])); + + lastLoad = 0; + + if (DR == lastDR) { + lastDR = 0; return; - // Handle VLAs. - Visit(se->getArgumentExpr()); + } } -} -void TransferFunctions::VisitCXXTypeidExpr(CXXTypeidExpr *E) { - // typeid(expression) is potentially evaluated when the argument is - // a glvalue of polymorphic type. (C++ 5.2.8p2-3) - if (!E->isTypeOperand() && E->Classify(ac.getASTContext()).isGLValue()) { - QualType SubExprTy = E->getExprOperand()->getType(); - if (const RecordType *Record = SubExprTy->getAs<RecordType>()) - if (cast<CXXRecordDecl>(Record->getDecl())->isPolymorphic()) - Visit(E->getExprOperand()); + // Any other uses of 'lastDR' involve taking an lvalue of variable. + // In this case, it "escapes" the analysis. + if (lastDR && lastDR != s) { + vals[cast<VarDecl>(lastDR->getDecl())] = Initialized; + lastDR = 0; } } @@ -604,8 +617,7 @@ void TransferFunctions::VisitCXXTypeidExpr(CXXTypeidExpr *E) { static bool runOnBlock(const CFGBlock *block, const CFG &cfg, AnalysisContext &ac, CFGBlockValues &vals, llvm::BitVector &wasAnalyzed, - UninitVariablesHandler *handler = 0, - bool flagBlockUses = false) { + UninitVariablesHandler *handler = 0) { wasAnalyzed[block->getBlockID()] = true; @@ -623,8 +635,7 @@ static bool runOnBlock(const CFGBlock *block, const CFG &cfg, vals.mergeIntoScratch(*(vB.second ? vB.second : vB.first), false); valsAB.first = vA.first; valsAB.second = &vals.getScratch(); - } - else { + } else { // Merge the 'T' bits from the first and second. assert(b->getOpcode() == BO_LOr); vals.mergeIntoScratch(*vA.first, true); @@ -640,17 +651,21 @@ static bool runOnBlock(const CFGBlock *block, const CFG &cfg, bool isFirst = true; for (CFGBlock::const_pred_iterator I = block->pred_begin(), E = block->pred_end(); I != E; ++I) { - vals.mergeIntoScratch(vals.getValueVector(*I, block), isFirst); - isFirst = false; + const CFGBlock *pred = *I; + if (wasAnalyzed[pred->getBlockID()]) { + vals.mergeIntoScratch(vals.getValueVector(pred, block), isFirst); + isFirst = false; + } } // Apply the transfer function. - TransferFunctions tf(vals, cfg, ac, handler, flagBlockUses); + TransferFunctions tf(vals, cfg, ac, handler); for (CFGBlock::const_iterator I = block->begin(), E = block->end(); I != E; ++I) { if (const CFGStmt *cs = dyn_cast<CFGStmt>(&*I)) { - tf.BlockStmt_Visit(cs->getStmt()); + tf.Visit(const_cast<Stmt*>(cs->getStmt())); } } + tf.ProcessUses(); return vals.updateValueVectorWithScratch(block); } @@ -685,6 +700,7 @@ void clang::runUninitializedVariablesAnalysis( llvm::BitVector previouslyVisited(cfg.getNumBlockIDs()); worklist.enqueueSuccessors(&cfg.getEntry()); llvm::BitVector wasAnalyzed(cfg.getNumBlockIDs(), false); + wasAnalyzed[cfg.getEntry().getBlockID()] = true; while (const CFGBlock *block = worklist.dequeue()) { // Did the block change? @@ -697,9 +713,9 @@ void clang::runUninitializedVariablesAnalysis( // Run through the blocks one more time, and report uninitialized variabes. for (CFG::const_iterator BI = cfg.begin(), BE = cfg.end(); BI != BE; ++BI) { - if (wasAnalyzed[(*BI)->getBlockID()]) { - runOnBlock(*BI, cfg, ac, vals, wasAnalyzed, &handler, - /* flagBlockUses */ true); + const CFGBlock *block = *BI; + if (wasAnalyzed[block->getBlockID()]) { + runOnBlock(block, cfg, ac, vals, wasAnalyzed, &handler); ++stats.NumBlockVisits; } } |