Path: blob/main/contrib/llvm-project/clang/lib/Analysis/UninitializedValues.cpp
35234 views
//===- UninitializedValues.cpp - Find Uninitialized Values ----------------===//1//2// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.3// See https://llvm.org/LICENSE.txt for license information.4// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception5//6//===----------------------------------------------------------------------===//7//8// This file implements uninitialized values analysis for source-level CFGs.9//10//===----------------------------------------------------------------------===//1112#include "clang/Analysis/Analyses/UninitializedValues.h"13#include "clang/AST/Attr.h"14#include "clang/AST/Decl.h"15#include "clang/AST/DeclBase.h"16#include "clang/AST/Expr.h"17#include "clang/AST/OperationKinds.h"18#include "clang/AST/Stmt.h"19#include "clang/AST/StmtObjC.h"20#include "clang/AST/StmtVisitor.h"21#include "clang/AST/Type.h"22#include "clang/Analysis/Analyses/PostOrderCFGView.h"23#include "clang/Analysis/AnalysisDeclContext.h"24#include "clang/Analysis/CFG.h"25#include "clang/Analysis/DomainSpecific/ObjCNoReturn.h"26#include "clang/Analysis/FlowSensitive/DataflowWorklist.h"27#include "clang/Basic/LLVM.h"28#include "llvm/ADT/BitVector.h"29#include "llvm/ADT/DenseMap.h"30#include "llvm/ADT/PackedVector.h"31#include "llvm/ADT/SmallBitVector.h"32#include "llvm/ADT/SmallVector.h"33#include "llvm/Support/Casting.h"34#include <algorithm>35#include <cassert>36#include <optional>3738using namespace clang;3940#define DEBUG_LOGGING 04142static bool recordIsNotEmpty(const RecordDecl *RD) {43// We consider a record decl to be empty if it contains only unnamed bit-44// fields, zero-width fields, and fields of empty record type.45for (const auto *FD : RD->fields()) {46if (FD->isUnnamedBitField())47continue;48if (FD->isZeroSize(FD->getASTContext()))49continue;50// The only case remaining to check is for a field declaration of record51// type and whether that record itself is empty.52if (const auto *FieldRD = FD->getType()->getAsRecordDecl();53!FieldRD || recordIsNotEmpty(FieldRD))54return true;55}56return false;57}5859static bool isTrackedVar(const VarDecl *vd, const DeclContext *dc) {60if (vd->isLocalVarDecl() && !vd->hasGlobalStorage() &&61!vd->isExceptionVariable() && !vd->isInitCapture() && !vd->isImplicit() &&62vd->getDeclContext() == dc) {63QualType ty = vd->getType();64if (const auto *RD = ty->getAsRecordDecl())65return recordIsNotEmpty(RD);66return ty->isScalarType() || ty->isVectorType() || ty->isRVVSizelessBuiltinType();67}68return false;69}7071//------------------------------------------------------------------------====//72// DeclToIndex: a mapping from Decls we track to value indices.73//====------------------------------------------------------------------------//7475namespace {7677class DeclToIndex {78llvm::DenseMap<const VarDecl *, unsigned> map;7980public:81DeclToIndex() = default;8283/// Compute the actual mapping from declarations to bits.84void computeMap(const DeclContext &dc);8586/// Return the number of declarations in the map.87unsigned size() const { return map.size(); }8889/// Returns the bit vector index for a given declaration.90std::optional<unsigned> getValueIndex(const VarDecl *d) const;91};9293} // namespace9495void DeclToIndex::computeMap(const DeclContext &dc) {96unsigned count = 0;97DeclContext::specific_decl_iterator<VarDecl> I(dc.decls_begin()),98E(dc.decls_end());99for ( ; I != E; ++I) {100const VarDecl *vd = *I;101if (isTrackedVar(vd, &dc))102map[vd] = count++;103}104}105106std::optional<unsigned> DeclToIndex::getValueIndex(const VarDecl *d) const {107llvm::DenseMap<const VarDecl *, unsigned>::const_iterator I = map.find(d);108if (I == map.end())109return std::nullopt;110return I->second;111}112113//------------------------------------------------------------------------====//114// CFGBlockValues: dataflow values for CFG blocks.115//====------------------------------------------------------------------------//116117// These values are defined in such a way that a merge can be done using118// a bitwise OR.119enum Value { Unknown = 0x0, /* 00 */120Initialized = 0x1, /* 01 */121Uninitialized = 0x2, /* 10 */122MayUninitialized = 0x3 /* 11 */ };123124static bool isUninitialized(const Value v) {125return v >= Uninitialized;126}127128static bool isAlwaysUninit(const Value v) {129return v == Uninitialized;130}131132namespace {133134using ValueVector = llvm::PackedVector<Value, 2, llvm::SmallBitVector>;135136class CFGBlockValues {137const CFG &cfg;138SmallVector<ValueVector, 8> vals;139ValueVector scratch;140DeclToIndex declToIndex;141142public:143CFGBlockValues(const CFG &cfg);144145unsigned getNumEntries() const { return declToIndex.size(); }146147void computeSetOfDeclarations(const DeclContext &dc);148149ValueVector &getValueVector(const CFGBlock *block) {150return vals[block->getBlockID()];151}152153void setAllScratchValues(Value V);154void mergeIntoScratch(ValueVector const &source, bool isFirst);155bool updateValueVectorWithScratch(const CFGBlock *block);156157bool hasNoDeclarations() const {158return declToIndex.size() == 0;159}160161void resetScratch();162163ValueVector::reference operator[](const VarDecl *vd);164165Value getValue(const CFGBlock *block, const CFGBlock *dstBlock,166const VarDecl *vd) {167std::optional<unsigned> idx = declToIndex.getValueIndex(vd);168return getValueVector(block)[*idx];169}170};171172} // namespace173174CFGBlockValues::CFGBlockValues(const CFG &c) : cfg(c), vals(0) {}175176void CFGBlockValues::computeSetOfDeclarations(const DeclContext &dc) {177declToIndex.computeMap(dc);178unsigned decls = declToIndex.size();179scratch.resize(decls);180unsigned n = cfg.getNumBlockIDs();181if (!n)182return;183vals.resize(n);184for (auto &val : vals)185val.resize(decls);186}187188#if DEBUG_LOGGING189static void printVector(const CFGBlock *block, ValueVector &bv,190unsigned num) {191llvm::errs() << block->getBlockID() << " :";192for (const auto &i : bv)193llvm::errs() << ' ' << i;194llvm::errs() << " : " << num << '\n';195}196#endif197198void CFGBlockValues::setAllScratchValues(Value V) {199for (unsigned I = 0, E = scratch.size(); I != E; ++I)200scratch[I] = V;201}202203void CFGBlockValues::mergeIntoScratch(ValueVector const &source,204bool isFirst) {205if (isFirst)206scratch = source;207else208scratch |= source;209}210211bool CFGBlockValues::updateValueVectorWithScratch(const CFGBlock *block) {212ValueVector &dst = getValueVector(block);213bool changed = (dst != scratch);214if (changed)215dst = scratch;216#if DEBUG_LOGGING217printVector(block, scratch, 0);218#endif219return changed;220}221222void CFGBlockValues::resetScratch() {223scratch.reset();224}225226ValueVector::reference CFGBlockValues::operator[](const VarDecl *vd) {227return scratch[*declToIndex.getValueIndex(vd)];228}229230//------------------------------------------------------------------------====//231// Classification of DeclRefExprs as use or initialization.232//====------------------------------------------------------------------------//233234namespace {235236class FindVarResult {237const VarDecl *vd;238const DeclRefExpr *dr;239240public:241FindVarResult(const VarDecl *vd, const DeclRefExpr *dr) : vd(vd), dr(dr) {}242243const DeclRefExpr *getDeclRefExpr() const { return dr; }244const VarDecl *getDecl() const { return vd; }245};246247} // namespace248249static const Expr *stripCasts(ASTContext &C, const Expr *Ex) {250while (Ex) {251Ex = Ex->IgnoreParenNoopCasts(C);252if (const auto *CE = dyn_cast<CastExpr>(Ex)) {253if (CE->getCastKind() == CK_LValueBitCast) {254Ex = CE->getSubExpr();255continue;256}257}258break;259}260return Ex;261}262263/// If E is an expression comprising a reference to a single variable, find that264/// variable.265static FindVarResult findVar(const Expr *E, const DeclContext *DC) {266if (const auto *DRE =267dyn_cast<DeclRefExpr>(stripCasts(DC->getParentASTContext(), E)))268if (const auto *VD = dyn_cast<VarDecl>(DRE->getDecl()))269if (isTrackedVar(VD, DC))270return FindVarResult(VD, DRE);271return FindVarResult(nullptr, nullptr);272}273274namespace {275276/// Classify each DeclRefExpr as an initialization or a use. Any277/// DeclRefExpr which isn't explicitly classified will be assumed to have278/// escaped the analysis and will be treated as an initialization.279class ClassifyRefs : public StmtVisitor<ClassifyRefs> {280public:281enum Class {282Init,283Use,284SelfInit,285ConstRefUse,286Ignore287};288289private:290const DeclContext *DC;291llvm::DenseMap<const DeclRefExpr *, Class> Classification;292293bool isTrackedVar(const VarDecl *VD) const {294return ::isTrackedVar(VD, DC);295}296297void classify(const Expr *E, Class C);298299public:300ClassifyRefs(AnalysisDeclContext &AC) : DC(cast<DeclContext>(AC.getDecl())) {}301302void VisitDeclStmt(DeclStmt *DS);303void VisitUnaryOperator(UnaryOperator *UO);304void VisitBinaryOperator(BinaryOperator *BO);305void VisitCallExpr(CallExpr *CE);306void VisitCastExpr(CastExpr *CE);307void VisitOMPExecutableDirective(OMPExecutableDirective *ED);308309void operator()(Stmt *S) { Visit(S); }310311Class get(const DeclRefExpr *DRE) const {312llvm::DenseMap<const DeclRefExpr*, Class>::const_iterator I313= Classification.find(DRE);314if (I != Classification.end())315return I->second;316317const auto *VD = dyn_cast<VarDecl>(DRE->getDecl());318if (!VD || !isTrackedVar(VD))319return Ignore;320321return Init;322}323};324325} // namespace326327static const DeclRefExpr *getSelfInitExpr(VarDecl *VD) {328if (VD->getType()->isRecordType())329return nullptr;330if (Expr *Init = VD->getInit()) {331const auto *DRE =332dyn_cast<DeclRefExpr>(stripCasts(VD->getASTContext(), Init));333if (DRE && DRE->getDecl() == VD)334return DRE;335}336return nullptr;337}338339void ClassifyRefs::classify(const Expr *E, Class C) {340// The result of a ?: could also be an lvalue.341E = E->IgnoreParens();342if (const auto *CO = dyn_cast<ConditionalOperator>(E)) {343classify(CO->getTrueExpr(), C);344classify(CO->getFalseExpr(), C);345return;346}347348if (const auto *BCO = dyn_cast<BinaryConditionalOperator>(E)) {349classify(BCO->getFalseExpr(), C);350return;351}352353if (const auto *OVE = dyn_cast<OpaqueValueExpr>(E)) {354classify(OVE->getSourceExpr(), C);355return;356}357358if (const auto *ME = dyn_cast<MemberExpr>(E)) {359if (const auto *VD = dyn_cast<VarDecl>(ME->getMemberDecl())) {360if (!VD->isStaticDataMember())361classify(ME->getBase(), C);362}363return;364}365366if (const auto *BO = dyn_cast<BinaryOperator>(E)) {367switch (BO->getOpcode()) {368case BO_PtrMemD:369case BO_PtrMemI:370classify(BO->getLHS(), C);371return;372case BO_Comma:373classify(BO->getRHS(), C);374return;375default:376return;377}378}379380FindVarResult Var = findVar(E, DC);381if (const DeclRefExpr *DRE = Var.getDeclRefExpr())382Classification[DRE] = std::max(Classification[DRE], C);383}384385void ClassifyRefs::VisitDeclStmt(DeclStmt *DS) {386for (auto *DI : DS->decls()) {387auto *VD = dyn_cast<VarDecl>(DI);388if (VD && isTrackedVar(VD))389if (const DeclRefExpr *DRE = getSelfInitExpr(VD))390Classification[DRE] = SelfInit;391}392}393394void ClassifyRefs::VisitBinaryOperator(BinaryOperator *BO) {395// Ignore the evaluation of a DeclRefExpr on the LHS of an assignment. If this396// is not a compound-assignment, we will treat it as initializing the variable397// when TransferFunctions visits it. A compound-assignment does not affect398// whether a variable is uninitialized, and there's no point counting it as a399// use.400if (BO->isCompoundAssignmentOp())401classify(BO->getLHS(), Use);402else if (BO->getOpcode() == BO_Assign || BO->getOpcode() == BO_Comma)403classify(BO->getLHS(), Ignore);404}405406void ClassifyRefs::VisitUnaryOperator(UnaryOperator *UO) {407// Increment and decrement are uses despite there being no lvalue-to-rvalue408// conversion.409if (UO->isIncrementDecrementOp())410classify(UO->getSubExpr(), Use);411}412413void ClassifyRefs::VisitOMPExecutableDirective(OMPExecutableDirective *ED) {414for (Stmt *S : OMPExecutableDirective::used_clauses_children(ED->clauses()))415classify(cast<Expr>(S), Use);416}417418static bool isPointerToConst(const QualType &QT) {419return QT->isAnyPointerType() && QT->getPointeeType().isConstQualified();420}421422static bool hasTrivialBody(CallExpr *CE) {423if (FunctionDecl *FD = CE->getDirectCallee()) {424if (FunctionTemplateDecl *FTD = FD->getPrimaryTemplate())425return FTD->getTemplatedDecl()->hasTrivialBody();426return FD->hasTrivialBody();427}428return false;429}430431void ClassifyRefs::VisitCallExpr(CallExpr *CE) {432// Classify arguments to std::move as used.433if (CE->isCallToStdMove()) {434// RecordTypes are handled in SemaDeclCXX.cpp.435if (!CE->getArg(0)->getType()->isRecordType())436classify(CE->getArg(0), Use);437return;438}439bool isTrivialBody = hasTrivialBody(CE);440// If a value is passed by const pointer to a function,441// we should not assume that it is initialized by the call, and we442// conservatively do not assume that it is used.443// If a value is passed by const reference to a function,444// it should already be initialized.445for (CallExpr::arg_iterator I = CE->arg_begin(), E = CE->arg_end();446I != E; ++I) {447if ((*I)->isGLValue()) {448if ((*I)->getType().isConstQualified())449classify((*I), isTrivialBody ? Ignore : ConstRefUse);450} else if (isPointerToConst((*I)->getType())) {451const Expr *Ex = stripCasts(DC->getParentASTContext(), *I);452const auto *UO = dyn_cast<UnaryOperator>(Ex);453if (UO && UO->getOpcode() == UO_AddrOf)454Ex = UO->getSubExpr();455classify(Ex, Ignore);456}457}458}459460void ClassifyRefs::VisitCastExpr(CastExpr *CE) {461if (CE->getCastKind() == CK_LValueToRValue)462classify(CE->getSubExpr(), Use);463else if (const auto *CSE = dyn_cast<CStyleCastExpr>(CE)) {464if (CSE->getType()->isVoidType()) {465// Squelch any detected load of an uninitialized value if466// we cast it to void.467// e.g. (void) x;468classify(CSE->getSubExpr(), Ignore);469}470}471}472473//------------------------------------------------------------------------====//474// Transfer function for uninitialized values analysis.475//====------------------------------------------------------------------------//476477namespace {478479class TransferFunctions : public StmtVisitor<TransferFunctions> {480CFGBlockValues &vals;481const CFG &cfg;482const CFGBlock *block;483AnalysisDeclContext ∾484const ClassifyRefs &classification;485ObjCNoReturn objCNoRet;486UninitVariablesHandler &handler;487488public:489TransferFunctions(CFGBlockValues &vals, const CFG &cfg,490const CFGBlock *block, AnalysisDeclContext &ac,491const ClassifyRefs &classification,492UninitVariablesHandler &handler)493: vals(vals), cfg(cfg), block(block), ac(ac),494classification(classification), objCNoRet(ac.getASTContext()),495handler(handler) {}496497void reportUse(const Expr *ex, const VarDecl *vd);498void reportConstRefUse(const Expr *ex, const VarDecl *vd);499500void VisitBinaryOperator(BinaryOperator *bo);501void VisitBlockExpr(BlockExpr *be);502void VisitCallExpr(CallExpr *ce);503void VisitDeclRefExpr(DeclRefExpr *dr);504void VisitDeclStmt(DeclStmt *ds);505void VisitGCCAsmStmt(GCCAsmStmt *as);506void VisitObjCForCollectionStmt(ObjCForCollectionStmt *FS);507void VisitObjCMessageExpr(ObjCMessageExpr *ME);508void VisitOMPExecutableDirective(OMPExecutableDirective *ED);509510bool isTrackedVar(const VarDecl *vd) {511return ::isTrackedVar(vd, cast<DeclContext>(ac.getDecl()));512}513514FindVarResult findVar(const Expr *ex) {515return ::findVar(ex, cast<DeclContext>(ac.getDecl()));516}517518UninitUse getUninitUse(const Expr *ex, const VarDecl *vd, Value v) {519UninitUse Use(ex, isAlwaysUninit(v));520521assert(isUninitialized(v));522if (Use.getKind() == UninitUse::Always)523return Use;524525// If an edge which leads unconditionally to this use did not initialize526// the variable, we can say something stronger than 'may be uninitialized':527// we can say 'either it's used uninitialized or you have dead code'.528//529// We track the number of successors of a node which have been visited, and530// visit a node once we have visited all of its successors. Only edges where531// the variable might still be uninitialized are followed. Since a variable532// can't transfer from being initialized to being uninitialized, this will533// trace out the subgraph which inevitably leads to the use and does not534// initialize the variable. We do not want to skip past loops, since their535// non-termination might be correlated with the initialization condition.536//537// For example:538//539// void f(bool a, bool b) {540// block1: int n;541// if (a) {542// block2: if (b)543// block3: n = 1;544// block4: } else if (b) {545// block5: while (!a) {546// block6: do_work(&a);547// n = 2;548// }549// }550// block7: if (a)551// block8: g();552// block9: return n;553// }554//555// Starting from the maybe-uninitialized use in block 9:556// * Block 7 is not visited because we have only visited one of its two557// successors.558// * Block 8 is visited because we've visited its only successor.559// From block 8:560// * Block 7 is visited because we've now visited both of its successors.561// From block 7:562// * Blocks 1, 2, 4, 5, and 6 are not visited because we didn't visit all563// of their successors (we didn't visit 4, 3, 5, 6, and 5, respectively).564// * Block 3 is not visited because it initializes 'n'.565// Now the algorithm terminates, having visited blocks 7 and 8, and having566// found the frontier is blocks 2, 4, and 5.567//568// 'n' is definitely uninitialized for two edges into block 7 (from blocks 2569// and 4), so we report that any time either of those edges is taken (in570// each case when 'b == false'), 'n' is used uninitialized.571SmallVector<const CFGBlock*, 32> Queue;572SmallVector<unsigned, 32> SuccsVisited(cfg.getNumBlockIDs(), 0);573Queue.push_back(block);574// Specify that we've already visited all successors of the starting block.575// This has the dual purpose of ensuring we never add it to the queue, and576// of marking it as not being a candidate element of the frontier.577SuccsVisited[block->getBlockID()] = block->succ_size();578while (!Queue.empty()) {579const CFGBlock *B = Queue.pop_back_val();580581// If the use is always reached from the entry block, make a note of that.582if (B == &cfg.getEntry())583Use.setUninitAfterCall();584585for (CFGBlock::const_pred_iterator I = B->pred_begin(), E = B->pred_end();586I != E; ++I) {587const CFGBlock *Pred = *I;588if (!Pred)589continue;590591Value AtPredExit = vals.getValue(Pred, B, vd);592if (AtPredExit == Initialized)593// This block initializes the variable.594continue;595if (AtPredExit == MayUninitialized &&596vals.getValue(B, nullptr, vd) == Uninitialized) {597// This block declares the variable (uninitialized), and is reachable598// from a block that initializes the variable. We can't guarantee to599// give an earlier location for the diagnostic (and it appears that600// this code is intended to be reachable) so give a diagnostic here601// and go no further down this path.602Use.setUninitAfterDecl();603continue;604}605606unsigned &SV = SuccsVisited[Pred->getBlockID()];607if (!SV) {608// When visiting the first successor of a block, mark all NULL609// successors as having been visited.610for (CFGBlock::const_succ_iterator SI = Pred->succ_begin(),611SE = Pred->succ_end();612SI != SE; ++SI)613if (!*SI)614++SV;615}616617if (++SV == Pred->succ_size())618// All paths from this block lead to the use and don't initialize the619// variable.620Queue.push_back(Pred);621}622}623624// Scan the frontier, looking for blocks where the variable was625// uninitialized.626for (const auto *Block : cfg) {627unsigned BlockID = Block->getBlockID();628const Stmt *Term = Block->getTerminatorStmt();629if (SuccsVisited[BlockID] && SuccsVisited[BlockID] < Block->succ_size() &&630Term) {631// This block inevitably leads to the use. If we have an edge from here632// to a post-dominator block, and the variable is uninitialized on that633// edge, we have found a bug.634for (CFGBlock::const_succ_iterator I = Block->succ_begin(),635E = Block->succ_end(); I != E; ++I) {636const CFGBlock *Succ = *I;637if (Succ && SuccsVisited[Succ->getBlockID()] >= Succ->succ_size() &&638vals.getValue(Block, Succ, vd) == Uninitialized) {639// Switch cases are a special case: report the label to the caller640// as the 'terminator', not the switch statement itself. Suppress641// situations where no label matched: we can't be sure that's642// possible.643if (isa<SwitchStmt>(Term)) {644const Stmt *Label = Succ->getLabel();645if (!Label || !isa<SwitchCase>(Label))646// Might not be possible.647continue;648UninitUse::Branch Branch;649Branch.Terminator = Label;650Branch.Output = 0; // Ignored.651Use.addUninitBranch(Branch);652} else {653UninitUse::Branch Branch;654Branch.Terminator = Term;655Branch.Output = I - Block->succ_begin();656Use.addUninitBranch(Branch);657}658}659}660}661}662663return Use;664}665};666667} // namespace668669void TransferFunctions::reportUse(const Expr *ex, const VarDecl *vd) {670Value v = vals[vd];671if (isUninitialized(v))672handler.handleUseOfUninitVariable(vd, getUninitUse(ex, vd, v));673}674675void TransferFunctions::reportConstRefUse(const Expr *ex, const VarDecl *vd) {676Value v = vals[vd];677if (isAlwaysUninit(v))678handler.handleConstRefUseOfUninitVariable(vd, getUninitUse(ex, vd, v));679}680681void TransferFunctions::VisitObjCForCollectionStmt(ObjCForCollectionStmt *FS) {682// This represents an initialization of the 'element' value.683if (const auto *DS = dyn_cast<DeclStmt>(FS->getElement())) {684const auto *VD = cast<VarDecl>(DS->getSingleDecl());685if (isTrackedVar(VD))686vals[VD] = Initialized;687}688}689690void TransferFunctions::VisitOMPExecutableDirective(691OMPExecutableDirective *ED) {692for (Stmt *S : OMPExecutableDirective::used_clauses_children(ED->clauses())) {693assert(S && "Expected non-null used-in-clause child.");694Visit(S);695}696if (!ED->isStandaloneDirective())697Visit(ED->getStructuredBlock());698}699700void TransferFunctions::VisitBlockExpr(BlockExpr *be) {701const BlockDecl *bd = be->getBlockDecl();702for (const auto &I : bd->captures()) {703const VarDecl *vd = I.getVariable();704if (!isTrackedVar(vd))705continue;706if (I.isByRef()) {707vals[vd] = Initialized;708continue;709}710reportUse(be, vd);711}712}713714void TransferFunctions::VisitCallExpr(CallExpr *ce) {715if (Decl *Callee = ce->getCalleeDecl()) {716if (Callee->hasAttr<ReturnsTwiceAttr>()) {717// After a call to a function like setjmp or vfork, any variable which is718// initialized anywhere within this function may now be initialized. For719// now, just assume such a call initializes all variables. FIXME: Only720// mark variables as initialized if they have an initializer which is721// reachable from here.722vals.setAllScratchValues(Initialized);723}724else if (Callee->hasAttr<AnalyzerNoReturnAttr>()) {725// Functions labeled like "analyzer_noreturn" are often used to denote726// "panic" functions that in special debug situations can still return,727// but for the most part should not be treated as returning. This is a728// useful annotation borrowed from the static analyzer that is useful for729// suppressing branch-specific false positives when we call one of these730// functions but keep pretending the path continues (when in reality the731// user doesn't care).732vals.setAllScratchValues(Unknown);733}734}735}736737void TransferFunctions::VisitDeclRefExpr(DeclRefExpr *dr) {738switch (classification.get(dr)) {739case ClassifyRefs::Ignore:740break;741case ClassifyRefs::Use:742reportUse(dr, cast<VarDecl>(dr->getDecl()));743break;744case ClassifyRefs::Init:745vals[cast<VarDecl>(dr->getDecl())] = Initialized;746break;747case ClassifyRefs::SelfInit:748handler.handleSelfInit(cast<VarDecl>(dr->getDecl()));749break;750case ClassifyRefs::ConstRefUse:751reportConstRefUse(dr, cast<VarDecl>(dr->getDecl()));752break;753}754}755756void TransferFunctions::VisitBinaryOperator(BinaryOperator *BO) {757if (BO->getOpcode() == BO_Assign) {758FindVarResult Var = findVar(BO->getLHS());759if (const VarDecl *VD = Var.getDecl())760vals[VD] = Initialized;761}762}763764void TransferFunctions::VisitDeclStmt(DeclStmt *DS) {765for (auto *DI : DS->decls()) {766auto *VD = dyn_cast<VarDecl>(DI);767if (VD && isTrackedVar(VD)) {768if (getSelfInitExpr(VD)) {769// If the initializer consists solely of a reference to itself, we770// explicitly mark the variable as uninitialized. This allows code771// like the following:772//773// int x = x;774//775// to deliberately leave a variable uninitialized. Different analysis776// clients can detect this pattern and adjust their reporting777// appropriately, but we need to continue to analyze subsequent uses778// of the variable.779vals[VD] = Uninitialized;780} else if (VD->getInit()) {781// Treat the new variable as initialized.782vals[VD] = Initialized;783} else {784// No initializer: the variable is now uninitialized. This matters785// for cases like:786// while (...) {787// int n;788// use(n);789// n = 0;790// }791// FIXME: Mark the variable as uninitialized whenever its scope is792// left, since its scope could be re-entered by a jump over the793// declaration.794vals[VD] = Uninitialized;795}796}797}798}799800void TransferFunctions::VisitGCCAsmStmt(GCCAsmStmt *as) {801// An "asm goto" statement is a terminator that may initialize some variables.802if (!as->isAsmGoto())803return;804805ASTContext &C = ac.getASTContext();806for (const Expr *O : as->outputs()) {807const Expr *Ex = stripCasts(C, O);808809// Strip away any unary operators. Invalid l-values are reported by other810// semantic analysis passes.811while (const auto *UO = dyn_cast<UnaryOperator>(Ex))812Ex = stripCasts(C, UO->getSubExpr());813814// Mark the variable as potentially uninitialized for those cases where815// it's used on an indirect path, where it's not guaranteed to be816// defined.817if (const VarDecl *VD = findVar(Ex).getDecl())818if (vals[VD] != Initialized)819vals[VD] = MayUninitialized;820}821}822823void TransferFunctions::VisitObjCMessageExpr(ObjCMessageExpr *ME) {824// If the Objective-C message expression is an implicit no-return that825// is not modeled in the CFG, set the tracked dataflow values to Unknown.826if (objCNoRet.isImplicitNoReturn(ME)) {827vals.setAllScratchValues(Unknown);828}829}830831//------------------------------------------------------------------------====//832// High-level "driver" logic for uninitialized values analysis.833//====------------------------------------------------------------------------//834835static bool runOnBlock(const CFGBlock *block, const CFG &cfg,836AnalysisDeclContext &ac, CFGBlockValues &vals,837const ClassifyRefs &classification,838llvm::BitVector &wasAnalyzed,839UninitVariablesHandler &handler) {840wasAnalyzed[block->getBlockID()] = true;841vals.resetScratch();842// Merge in values of predecessor blocks.843bool isFirst = true;844for (CFGBlock::const_pred_iterator I = block->pred_begin(),845E = block->pred_end(); I != E; ++I) {846const CFGBlock *pred = *I;847if (!pred)848continue;849if (wasAnalyzed[pred->getBlockID()]) {850vals.mergeIntoScratch(vals.getValueVector(pred), isFirst);851isFirst = false;852}853}854// Apply the transfer function.855TransferFunctions tf(vals, cfg, block, ac, classification, handler);856for (const auto &I : *block) {857if (std::optional<CFGStmt> cs = I.getAs<CFGStmt>())858tf.Visit(const_cast<Stmt *>(cs->getStmt()));859}860CFGTerminator terminator = block->getTerminator();861if (auto *as = dyn_cast_or_null<GCCAsmStmt>(terminator.getStmt()))862if (as->isAsmGoto())863tf.Visit(as);864return vals.updateValueVectorWithScratch(block);865}866867namespace {868869/// PruneBlocksHandler is a special UninitVariablesHandler that is used870/// to detect when a CFGBlock has any *potential* use of an uninitialized871/// variable. It is mainly used to prune out work during the final872/// reporting pass.873struct PruneBlocksHandler : public UninitVariablesHandler {874/// Records if a CFGBlock had a potential use of an uninitialized variable.875llvm::BitVector hadUse;876877/// Records if any CFGBlock had a potential use of an uninitialized variable.878bool hadAnyUse = false;879880/// The current block to scribble use information.881unsigned currentBlock = 0;882883PruneBlocksHandler(unsigned numBlocks) : hadUse(numBlocks, false) {}884885~PruneBlocksHandler() override = default;886887void handleUseOfUninitVariable(const VarDecl *vd,888const UninitUse &use) override {889hadUse[currentBlock] = true;890hadAnyUse = true;891}892893void handleConstRefUseOfUninitVariable(const VarDecl *vd,894const UninitUse &use) override {895hadUse[currentBlock] = true;896hadAnyUse = true;897}898899/// Called when the uninitialized variable analysis detects the900/// idiom 'int x = x'. All other uses of 'x' within the initializer901/// are handled by handleUseOfUninitVariable.902void handleSelfInit(const VarDecl *vd) override {903hadUse[currentBlock] = true;904hadAnyUse = true;905}906};907908} // namespace909910void clang::runUninitializedVariablesAnalysis(911const DeclContext &dc,912const CFG &cfg,913AnalysisDeclContext &ac,914UninitVariablesHandler &handler,915UninitVariablesAnalysisStats &stats) {916CFGBlockValues vals(cfg);917vals.computeSetOfDeclarations(dc);918if (vals.hasNoDeclarations())919return;920921stats.NumVariablesAnalyzed = vals.getNumEntries();922923// Precompute which expressions are uses and which are initializations.924ClassifyRefs classification(ac);925cfg.VisitBlockStmts(classification);926927// Mark all variables uninitialized at the entry.928const CFGBlock &entry = cfg.getEntry();929ValueVector &vec = vals.getValueVector(&entry);930const unsigned n = vals.getNumEntries();931for (unsigned j = 0; j < n; ++j) {932vec[j] = Uninitialized;933}934935// Proceed with the workist.936ForwardDataflowWorklist worklist(cfg, ac);937llvm::BitVector previouslyVisited(cfg.getNumBlockIDs());938worklist.enqueueSuccessors(&cfg.getEntry());939llvm::BitVector wasAnalyzed(cfg.getNumBlockIDs(), false);940wasAnalyzed[cfg.getEntry().getBlockID()] = true;941PruneBlocksHandler PBH(cfg.getNumBlockIDs());942943while (const CFGBlock *block = worklist.dequeue()) {944PBH.currentBlock = block->getBlockID();945946// Did the block change?947bool changed = runOnBlock(block, cfg, ac, vals,948classification, wasAnalyzed, PBH);949++stats.NumBlockVisits;950if (changed || !previouslyVisited[block->getBlockID()])951worklist.enqueueSuccessors(block);952previouslyVisited[block->getBlockID()] = true;953}954955if (!PBH.hadAnyUse)956return;957958// Run through the blocks one more time, and report uninitialized variables.959for (const auto *block : cfg)960if (PBH.hadUse[block->getBlockID()]) {961runOnBlock(block, cfg, ac, vals, classification, wasAnalyzed, handler);962++stats.NumBlockVisits;963}964}965966UninitVariablesHandler::~UninitVariablesHandler() = default;967968969