#ifndef LLVM_CLANG_UNINIT_VALS_H
#define LLVM_CLANG_UNINIT_VALS_H
+#include "llvm/ADT/SmallVector.h"
+
namespace clang {
class AnalysisDeclContext;
class Expr;
class VarDecl;
+/// A use of a variable, which might be uninitialized.
+class UninitUse {
+public:
+ struct Branch {
+ const Stmt *Terminator;
+ unsigned Output;
+ };
+
+private:
+ /// The expression which uses this variable.
+ const Expr *User;
+
+ /// Does this use always see an uninitialized value?
+ bool AlwaysUninit;
+
+ /// This use is always uninitialized if it occurs after any of these branches
+ /// is taken.
+ llvm::SmallVector<Branch, 2> UninitBranches;
+
+public:
+ UninitUse(const Expr *User, bool AlwaysUninit) :
+ User(User), AlwaysUninit(AlwaysUninit) {}
+
+ void addUninitBranch(Branch B) {
+ UninitBranches.push_back(B);
+ }
+
+ /// Get the expression containing the uninitialized use.
+ const Expr *getUser() const { return User; }
+
+ /// The kind of uninitialized use.
+ enum Kind {
+ /// The use might be uninitialized.
+ Maybe,
+ /// The use is uninitialized whenever a certain branch is taken.
+ Sometimes,
+ /// The use is always uninitialized.
+ Always
+ };
+
+ /// Get the kind of uninitialized use.
+ Kind getKind() const {
+ return AlwaysUninit ? Always :
+ !branch_empty() ? Sometimes : Maybe;
+ }
+
+ typedef llvm::SmallVectorImpl<Branch>::const_iterator branch_iterator;
+ /// Branches which inevitably result in the variable being used uninitialized.
+ branch_iterator branch_begin() const { return UninitBranches.begin(); }
+ branch_iterator branch_end() const { return UninitBranches.end(); }
+ bool branch_empty() const { return UninitBranches.empty(); }
+};
+
class UninitVariablesHandler {
public:
UninitVariablesHandler() {}
virtual ~UninitVariablesHandler();
/// Called when the uninitialized variable is used at the given expression.
- virtual void handleUseOfUninitVariable(const Expr *ex,
- const VarDecl *vd,
- bool isAlwaysUninit) {}
+ virtual void handleUseOfUninitVariable(const VarDecl *vd,
+ const UninitUse &use) {}
/// Called when the uninitialized variable analysis detects the
/// idiom 'int x = x'. All other uses of 'x' within the initializer
def : DiagGroup<"type-limits">;
def Unicode : DiagGroup<"unicode">;
-def Uninitialized : DiagGroup<"uninitialized">;
def UninitializedMaybe : DiagGroup<"conditional-uninitialized">;
+def UninitializedSometimes : DiagGroup<"sometimes-uninitialized">;
+def Uninitialized : DiagGroup<"uninitialized", [UninitializedSometimes]>;
def UnknownPragmas : DiagGroup<"unknown-pragmas">;
def NSobjectAttribute : DiagGroup<"NSObject-attribute">;
def UnknownAttributes : DiagGroup<"attributes">;
def warn_uninit_var : Warning<
"variable %0 is uninitialized when used here">,
InGroup<Uninitialized>, DefaultIgnore;
+def warn_sometimes_uninit_var : Warning<
+ "variable %0 is sometimes uninitialized when used here">,
+ InGroup<UninitializedSometimes>, DefaultIgnore;
def warn_maybe_uninit_var :
Warning<"variable %0 may be uninitialized when used here">,
InGroup<UninitializedMaybe>, DefaultIgnore;
def warn_uninit_var_captured_by_block : Warning<
"variable %0 is uninitialized when captured by block">,
InGroup<Uninitialized>, DefaultIgnore;
+def warn_sometimes_uninit_var_captured_by_block : Warning<
+ "variable %0 is sometimes uninitialized when captured by block">,
+ InGroup<UninitializedSometimes>, DefaultIgnore;
def warn_maybe_uninit_var_captured_by_block : Warning<
"variable %0 may be uninitialized when captured by block">,
InGroup<UninitializedMaybe>, DefaultIgnore;
def warn_uninit_byref_blockvar_captured_by_block : Warning<
"block pointer variable %0 is uninitialized when captured by block">,
InGroup<Uninitialized>, DefaultIgnore;
+def note_sometimes_uninit_var_branch : Note<
+ "uninitialized use occurs whenever "
+ "%select{'%1' condition is %select{true|false}2|"
+ "'%1' loop %select{is entered|exits because its condition is false}2|"
+ "'%1' loop %select{condition is true|exits because its condition is false}2|"
+ "switch %1 is taken}0">;
def note_block_var_fixit_add_initialization : Note<
"maybe you meant to use __block %0">;
def note_var_fixit_add_initialization : Note<
ValueVector &getScratch() { return scratch; }
ValueVector::reference operator[](const VarDecl *vd);
+
+ Value getValue(const CFGBlock *block, const CFGBlock *dstBlock,
+ const VarDecl *vd) {
+ const llvm::Optional<unsigned> &idx = declToIndex.getValueIndex(vd);
+ assert(idx.hasValue());
+ return getValueVector(block, dstBlock)[idx.getValue()];
+ }
};
} // end anonymous namespace
class TransferFunctions : public StmtVisitor<TransferFunctions> {
CFGBlockValues &vals;
const CFG &cfg;
+ const CFGBlock *block;
AnalysisDeclContext ∾
UninitVariablesHandler *handler;
public:
TransferFunctions(CFGBlockValues &vals, const CFG &cfg,
- AnalysisDeclContext &ac,
+ const CFGBlock *block, AnalysisDeclContext &ac,
UninitVariablesHandler *handler)
- : vals(vals), cfg(cfg), ac(ac), handler(handler),
+ : vals(vals), cfg(cfg), block(block), ac(ac), handler(handler),
lastDR(0), lastLoad(0),
skipProcessUses(false) {}
void VisitCastExpr(CastExpr *ce);
void VisitObjCForCollectionStmt(ObjCForCollectionStmt *fs);
void Visit(Stmt *s);
-
+
bool isTrackedVar(const VarDecl *vd) {
return ::isTrackedVar(vd, cast<DeclContext>(ac.getDecl()));
}
-
+
+ UninitUse getUninitUse(const Expr *ex, const VarDecl *vd, Value v) {
+ UninitUse Use(ex, isAlwaysUninit(v));
+
+ assert(isUninitialized(v));
+ if (Use.getKind() == UninitUse::Always)
+ return Use;
+
+ // If an edge which leads unconditionally to this use did not initialize
+ // the variable, we can say something stronger than 'may be uninitialized':
+ // we can say 'either it's used uninitialized or you have dead code'.
+ //
+ // We track the number of successors of a node which have been visited, and
+ // visit a node once we have visited all of its successors. Only edges where
+ // the variable might still be uninitialized are followed. Since a variable
+ // can't transfer from being initialized to being uninitialized, this will
+ // trace out the subgraph which inevitably leads to the use and does not
+ // initialize the variable. We do not want to skip past loops, since their
+ // non-termination might be correlated with the initialization condition.
+ //
+ // For example:
+ //
+ // void f(bool a, bool b) {
+ // block1: int n;
+ // if (a) {
+ // block2: if (b)
+ // block3: n = 1;
+ // block4: } else if (b) {
+ // block5: while (!a) {
+ // block6: do_work(&a);
+ // n = 2;
+ // }
+ // }
+ // block7: if (a)
+ // block8: g();
+ // block9: return n;
+ // }
+ //
+ // Starting from the maybe-uninitialized use in block 9:
+ // * Block 7 is not visited because we have only visited one of its two
+ // successors.
+ // * Block 8 is visited because we've visited its only successor.
+ // From block 8:
+ // * Block 7 is visited because we've now visited both of its successors.
+ // From block 7:
+ // * Blocks 1, 2, 4, 5, and 6 are not visited because we didn't visit all
+ // of their successors (we didn't visit 4, 3, 5, 6, and 5, respectively).
+ // * Block 3 is not visited because it initializes 'n'.
+ // Now the algorithm terminates, having visited blocks 7 and 8, and having
+ // found the frontier is blocks 2, 4, and 5.
+ //
+ // 'n' is definitely uninitialized for two edges into block 7 (from blocks 2
+ // and 4), so we report that any time either of those edges is taken (in
+ // each case when 'b == false'), 'n' is used uninitialized.
+ llvm::SmallVector<const CFGBlock*, 32> Queue;
+ llvm::SmallVector<unsigned, 32> SuccsVisited(cfg.getNumBlockIDs(), 0);
+ Queue.push_back(block);
+ // Specify that we've already visited all successors of the starting block.
+ // This has the dual purpose of ensuring we never add it to the queue, and
+ // of marking it as not being a candidate element of the frontier.
+ SuccsVisited[block->getBlockID()] = block->succ_size();
+ while (!Queue.empty()) {
+ const CFGBlock *B = Queue.back();
+ Queue.pop_back();
+ for (CFGBlock::const_pred_iterator I = B->pred_begin(), E = B->pred_end();
+ I != E; ++I) {
+ const CFGBlock *Pred = *I;
+ if (vals.getValue(Pred, B, vd) == Initialized)
+ // This block initializes the variable.
+ continue;
+
+ if (++SuccsVisited[Pred->getBlockID()] == Pred->succ_size())
+ // All paths from this block lead to the use and don't initialize the
+ // variable.
+ Queue.push_back(Pred);
+ }
+ }
+
+ // Scan the frontier, looking for blocks where the variable was
+ // uninitialized.
+ for (CFG::const_iterator BI = cfg.begin(), BE = cfg.end(); BI != BE; ++BI) {
+ const CFGBlock *Block = *BI;
+ unsigned BlockID = Block->getBlockID();
+ const Stmt *Term = Block->getTerminator();
+ if (SuccsVisited[BlockID] && SuccsVisited[BlockID] < Block->succ_size() &&
+ Term) {
+ // This block inevitably leads to the use. If we have an edge from here
+ // to a post-dominator block, and the variable is uninitialized on that
+ // edge, we have found a bug.
+ for (CFGBlock::const_succ_iterator I = Block->succ_begin(),
+ E = Block->succ_end(); I != E; ++I) {
+ const CFGBlock *Succ = *I;
+ if (Succ && SuccsVisited[Succ->getBlockID()] >= Succ->succ_size() &&
+ vals.getValue(Block, Succ, vd) == Uninitialized) {
+ // Switch cases are a special case: report the label to the caller
+ // as the 'terminator', not the switch statement itself. Suppress
+ // situations where no label matched: we can't be sure that's
+ // possible.
+ if (isa<SwitchStmt>(Term)) {
+ const Stmt *Label = Succ->getLabel();
+ if (!Label || !isa<SwitchCase>(Label))
+ // Might not be possible.
+ continue;
+ UninitUse::Branch Branch;
+ Branch.Terminator = Label;
+ Branch.Output = 0; // Ignored.
+ Use.addUninitBranch(Branch);
+ } else {
+ UninitUse::Branch Branch;
+ Branch.Terminator = Term;
+ Branch.Output = I - Block->succ_begin();
+ Use.addUninitBranch(Branch);
+ }
+ }
+ }
+ }
+ }
+
+ return Use;
+ }
+
FindVarResult findBlockVarDecl(Expr *ex);
void ProcessUses(Stmt *s = 0);
return;
Value v = vals[vd];
if (isUninitialized(v))
- handler->handleUseOfUninitVariable(ex, vd, isAlwaysUninit(v));
+ handler->handleUseOfUninitVariable(vd, getUninitUse(ex, vd, v));
}
FindVarResult TransferFunctions::findBlockVarDecl(Expr *ex) {
}
}
// Apply the transfer function.
- TransferFunctions tf(vals, cfg, ac, handler);
+ TransferFunctions tf(vals, cfg, block, ac, handler);
for (CFGBlock::const_iterator I = block->begin(), E = block->end();
I != E; ++I) {
if (const CFGStmt *cs = dyn_cast<CFGStmt>(&*I)) {
return true;
}
+/// NoteUninitBranches -- Helper function to produce notes for branches which
+/// inevitably lead to an uninitialized variable use.
+static void NoteUninitBranches(Sema &S, const UninitUse &Use) {
+ for (UninitUse::branch_iterator I = Use.branch_begin(), E = Use.branch_end();
+ I != E; ++I) {
+ const Stmt *Term = I->Terminator;
+ unsigned DiagKind;
+ SourceRange Range;
+ const char *Str;
+ switch (Term->getStmtClass()) {
+ default:
+ // Don't know how to report this.
+ continue;
+
+ // "condition is true / condition is false".
+ case Stmt::IfStmtClass:
+ DiagKind = 0;
+ Str = "if";
+ Range = cast<IfStmt>(Term)->getCond()->getSourceRange();
+ break;
+ case Stmt::ConditionalOperatorClass:
+ DiagKind = 0;
+ Str = "?:";
+ Range = cast<ConditionalOperator>(Term)->getCond()->getSourceRange();
+ break;
+ case Stmt::BinaryOperatorClass: {
+ const BinaryOperator *BO = cast<BinaryOperator>(Term);
+ if (!BO->isLogicalOp())
+ continue;
+ DiagKind = 0;
+ Str = BO->getOpcodeStr();
+ Range = BO->getLHS()->getSourceRange();
+ break;
+ }
+
+ // "loop is entered / loop is exited".
+ case Stmt::WhileStmtClass:
+ DiagKind = 1;
+ Str = "while";
+ Range = cast<WhileStmt>(Term)->getCond()->getSourceRange();
+ break;
+ case Stmt::ForStmtClass:
+ DiagKind = 1;
+ Str = "for";
+ Range = cast<ForStmt>(Term)->getCond()->getSourceRange();
+ break;
+ case Stmt::CXXForRangeStmtClass:
+ DiagKind = 1;
+ Str = "for";
+ Range = cast<CXXForRangeStmt>(Term)->getCond()->getSourceRange();
+ break;
+
+ // "condition is true / loop is exited".
+ case Stmt::DoStmtClass:
+ DiagKind = 2;
+ Str = "do";
+ Range = cast<DoStmt>(Term)->getCond()->getSourceRange();
+ break;
+
+ // "switch case is taken".
+ case Stmt::CaseStmtClass:
+ DiagKind = 3;
+ Str = "case";
+ Range = cast<CaseStmt>(Term)->getLHS()->getSourceRange();
+ break;
+ case Stmt::DefaultStmtClass:
+ DiagKind = 3;
+ Str = "default";
+ Range = cast<DefaultStmt>(Term)->getDefaultLoc();
+ break;
+ }
+
+ S.Diag(Range.getBegin(), diag::note_sometimes_uninit_var_branch)
+ << DiagKind << Str << I->Output << Range;
+ }
+}
+
/// DiagnoseUninitializedUse -- Helper function for diagnosing uses of an
/// uninitialized variable. This manages the different forms of diagnostic
/// emitted for particular types of uses. Returns true if the use was diagnosed
-/// as a warning. If a pariticular use is one we omit warnings for, returns
+/// as a warning. If a particular use is one we omit warnings for, returns
/// false.
static bool DiagnoseUninitializedUse(Sema &S, const VarDecl *VD,
- const Expr *E, bool isAlwaysUninit,
+ const UninitUse &Use,
bool alwaysReportSelfInit = false) {
- if (const DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(E)) {
+ if (const DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(Use.getUser())) {
// Inspect the initializer of the variable declaration which is
// being referenced prior to its initialization. We emit
// specialized diagnostics for self-initialization, and we
}
}
- S.Diag(DRE->getLocStart(), isAlwaysUninit ? diag::warn_uninit_var
- : diag::warn_maybe_uninit_var)
+ unsigned DiagID = 0;
+ switch (Use.getKind()) {
+ case UninitUse::Always: DiagID = diag::warn_uninit_var; break;
+ case UninitUse::Sometimes: DiagID = diag::warn_sometimes_uninit_var; break;
+ case UninitUse::Maybe: DiagID = diag::warn_maybe_uninit_var; break;
+ }
+ S.Diag(DRE->getLocStart(), DiagID)
<< VD->getDeclName() << DRE->getSourceRange();
+ NoteUninitBranches(S, Use);
} else {
- const BlockExpr *BE = cast<BlockExpr>(E);
+ const BlockExpr *BE = cast<BlockExpr>(Use.getUser());
if (VD->getType()->isBlockPointerType() &&
!VD->hasAttr<BlocksAttr>())
S.Diag(BE->getLocStart(), diag::warn_uninit_byref_blockvar_captured_by_block)
<< VD->getDeclName();
- else
- S.Diag(BE->getLocStart(),
- isAlwaysUninit ? diag::warn_uninit_var_captured_by_block
- : diag::warn_maybe_uninit_var_captured_by_block)
- << VD->getDeclName();
+ else {
+ unsigned DiagID = 0;
+ switch (Use.getKind()) {
+ case UninitUse::Always:
+ DiagID = diag::warn_uninit_var_captured_by_block;
+ break;
+ case UninitUse::Sometimes:
+ DiagID = diag::warn_sometimes_uninit_var_captured_by_block;
+ break;
+ case UninitUse::Maybe:
+ DiagID = diag::warn_maybe_uninit_var_captured_by_block;
+ break;
+ }
+ S.Diag(BE->getLocStart(), DiagID) << VD->getDeclName();
+ NoteUninitBranches(S, Use);
+ }
}
// Report where the variable was declared when the use wasn't within
}
-typedef std::pair<const Expr*, bool> UninitUse;
-
namespace {
struct SLocSort {
bool operator()(const UninitUse &a, const UninitUse &b) {
- SourceLocation aLoc = a.first->getLocStart();
- SourceLocation bLoc = b.first->getLocStart();
+ // Prefer a more confident report over a less confident one.
+ if (a.getKind() != b.getKind())
+ return a.getKind() > b.getKind();
+ SourceLocation aLoc = a.getUser()->getLocStart();
+ SourceLocation bLoc = b.getUser()->getLocStart();
return aLoc.getRawEncoding() < bLoc.getRawEncoding();
}
};
return V;
}
- void handleUseOfUninitVariable(const Expr *ex, const VarDecl *vd,
- bool isAlwaysUninit) {
- getUses(vd).first->push_back(std::make_pair(ex, isAlwaysUninit));
+ void handleUseOfUninitVariable(const VarDecl *vd, const UninitUse &use) {
+ getUses(vd).first->push_back(use);
}
void handleSelfInit(const VarDecl *vd) {
// variable, but the root cause is an idiomatic self-init. We want
// to report the diagnostic at the self-init since that is the root cause.
if (!vec->empty() && hasSelfInit && hasAlwaysUninitializedUse(vec))
- DiagnoseUninitializedUse(S, vd, vd->getInit()->IgnoreParenCasts(),
- /* isAlwaysUninit */ true,
+ DiagnoseUninitializedUse(S, vd,
+ UninitUse(vd->getInit()->IgnoreParenCasts(),
+ /* isAlwaysUninit */ true),
/* alwaysReportSelfInit */ true);
else {
// Sort the uses by their SourceLocations. While not strictly
for (UsesVec::iterator vi = vec->begin(), ve = vec->end(); vi != ve;
++vi) {
- if (DiagnoseUninitializedUse(S, vd, vi->first,
- /*isAlwaysUninit=*/vi->second))
+ // If we have self-init, downgrade all uses to 'may be uninitialized'.
+ UninitUse Use = hasSelfInit ? UninitUse(vi->getUser(), false) : *vi;
+
+ if (DiagnoseUninitializedUse(S, vd, Use))
// Skip further diagnostics for this variable. We try to warn only
// on the first point at which a variable is used uninitialized.
break;
private:
static bool hasAlwaysUninitializedUse(const UsesVec* vec) {
for (UsesVec::const_iterator i = vec->begin(), e = vec->end(); i != e; ++i) {
- if (i->second) {
+ if (i->getKind() == UninitUse::Always) {
return true;
}
}
}
if (Diags.getDiagnosticLevel(diag::warn_uninit_var, D->getLocStart())
+ != DiagnosticsEngine::Ignored ||
+ Diags.getDiagnosticLevel(diag::warn_sometimes_uninit_var,D->getLocStart())
!= DiagnosticsEngine::Ignored ||
Diags.getDiagnosticLevel(diag::warn_maybe_uninit_var, D->getLocStart())
!= DiagnosticsEngine::Ignored) {
--- /dev/null
+// RUN: %clang_cc1 -std=gnu++11 -Wsometimes-uninitialized -verify %s
+
+bool maybe();
+
+int test_if_false(bool b) {
+ int x; // expected-note {{variable}}
+ if (b) x = 1; // expected-note {{whenever 'if' condition is false}}
+ return x; // expected-warning {{sometimes uninit}}
+}
+
+int test_if_true(bool b) {
+ int x; // expected-note {{variable}}
+ if (b) {} // expected-note {{whenever 'if' condition is true}}
+ else x = 1;
+ return x; // expected-warning {{sometimes uninit}}
+}
+
+int test_while_false(bool b) {
+ int x; // expected-note {{variable}}
+ while (b) { // expected-note {{whenever 'while' loop exits because its condition is false}}
+ if (maybe()) {
+ x = 1;
+ break;
+ }
+ };
+ return x; // expected-warning {{sometimes uninit}}
+}
+
+int test_while_true(bool b) {
+ int x; // expected-note {{variable}}
+ while (b) { // expected-note {{whenever 'while' loop is entered}}
+label:
+ return x; // expected-warning {{sometimes uninit}}
+ }
+ x = 0;
+ goto label;
+}
+
+int test_do_while_false(bool b) {
+ int x; // expected-note {{variable}}
+ do {
+ if (maybe()) {
+ x = 1;
+ break;
+ }
+ } while (b); // expected-note {{whenever 'do' loop exits because its condition is false}}
+ return x; // expected-warning {{sometimes uninit}}
+}
+
+int test_do_while_true(bool b) {
+ int x; // expected-note {{variable}}
+goto label2;
+ do {
+label1:
+ return x; // expected-warning {{sometimes uninit}}
+label2: ;
+ } while (b); // expected-note {{whenever 'do' loop condition is true}}
+ x = 0;
+ goto label1;
+}
+
+int test_for_false(int k) {
+ int x; // expected-note {{variable}}
+ for (int n = 0;
+ n < k; // expected-note {{whenever 'for' loop exits because its condition is false}}
+ ++n) {
+ if (maybe()) {
+ x = n;
+ break;
+ }
+ }
+ return x; // expected-warning {{sometimes uninit}}
+}
+
+int test_for_true(int k) {
+ int x; // expected-note {{variable}}
+ int n = 0;
+ for (;
+ n < k; // expected-note {{whenever 'for' loop is entered}}
+ ++n) {
+label:
+ return x; // expected-warning {{sometimes uninit}}
+ }
+ x = 1;
+ goto label;
+}
+
+int test_for_range_false(int k) {
+ int arr[3] = { 1, 2, 3 };
+ int x; // expected-note {{variable}}
+ for (int &a : arr) { // expected-note {{whenever 'for' loop exits because its condition is false}}
+ if (a == k) {
+ x = &a - arr;
+ break;
+ }
+ }
+ return x; // expected-warning {{sometimes uninit}}
+}
+
+int test_for_range_true(int k) {
+ int arr[3] = { 1, 2, 3 };
+ int x; // expected-note {{variable}}
+ for (int &a : arr) { // expected-note {{whenever 'for' loop is entered}}
+ goto label;
+ }
+ x = 0;
+label:
+ return x; // expected-warning {{sometimes uninit}}
+}
+
+int test_conditional_false(int k) {
+ int x; // expected-note {{variable}}
+ (void)(
+ maybe() // expected-note {{whenever '?:' condition is false}}
+ ? x = 1 : 0);
+ return x; // expected-warning {{sometimes uninit}}
+}
+
+int test_conditional_true(int k) {
+ int x; // expected-note {{variable}}
+ (void)(
+ maybe() // expected-note {{whenever '?:' condition is true}}
+ ? 0 : x = 1);
+ return x; // expected-warning {{sometimes uninit}}
+}
+
+int test_logical_and_false(int k) {
+ int x; // expected-note {{variable}}
+ maybe() // expected-note {{whenever '&&' condition is false}}
+ && (x = 1);
+ return x; // expected-warning {{sometimes uninit}}
+}
+
+int test_logical_and_true(int k) {
+ int x; // expected-note {{variable}}
+ maybe() // expected-note {{whenever '&&' condition is true}}
+ && ({ goto skip_init; 0; });
+ x = 1;
+skip_init:
+ return x; // expected-warning {{sometimes uninit}}
+}
+
+int test_logical_or_false(int k) {
+ int x; // expected-note {{variable}}
+ maybe() // expected-note {{whenever '||' condition is false}}
+ || ({ goto skip_init; 0; });
+ x = 1;
+skip_init:
+ return x; // expected-warning {{sometimes uninit}}
+}
+
+int test_logical_or_true(int k) {
+ int x; // expected-note {{variable}}
+ maybe() // expected-note {{whenever '||' condition is true}}
+ || (x = 1);
+ return x; // expected-warning {{sometimes uninit}}
+}
+
+int test_switch_case(int k) {
+ int x; // expected-note {{variable}}
+ switch (k) {
+ case 0:
+ x = 0;
+ break;
+ case 1: // expected-note {{whenever switch case is taken}}
+ break;
+ }
+ return x; // expected-warning {{sometimes uninit}}
+}
+
+int test_switch_default(int k) {
+ int x; // expected-note {{variable}}
+ switch (k) {
+ case 0:
+ x = 0;
+ break;
+ case 1:
+ x = 1;
+ break;
+ default: // expected-note {{whenever switch default is taken}}
+ break;
+ }
+ return x; // expected-warning {{sometimes uninit}}
+}
+
+int test_switch_suppress_1(int k) {
+ int x;
+ switch (k) {
+ case 0:
+ x = 0;
+ break;
+ case 1:
+ x = 1;
+ break;
+ }
+ return x; // no-warning
+}
+
+int test_switch_suppress_2(int k) {
+ int x;
+ switch (k) {
+ case 0:
+ case 1:
+ switch (k) {
+ case 0:
+ return 0;
+ case 1:
+ return 1;
+ }
+ case 2:
+ case 3:
+ x = 1;
+ }
+ return x; // no-warning
+}
+
+int test_multiple_notes(int k) {
+ int x; // expected-note {{variable}}
+ if (k > 0) {
+ if (k == 5)
+ x = 1;
+ else if (k == 2) // expected-note {{whenever 'if' condition is false}}
+ x = 2;
+ } else {
+ if (k == -5)
+ x = 3;
+ else if (k == -2) // expected-note {{whenever 'if' condition is false}}
+ x = 4;
+ }
+ return x; // expected-warning {{sometimes uninit}}
+}
+
+int test_no_false_positive_1(int k) {
+ int x;
+ if (k)
+ x = 5;
+ while (!k)
+ maybe();
+ return x;
+}
+
+int test_no_false_positive_2() {
+ int x;
+ bool b = false;
+ if (maybe()) {
+ x = 5;
+ b = true;
+ }
+ return b ? x : 0;
+}
+
+void test_null_pred_succ() {
+ int x; // expected-note {{variable}}
+ if (0) // expected-note {{whenever}}
+ foo: x = 0;
+ if (x) // expected-warning {{sometimes uninit}}
+ goto foo;
+}
int test7(int y) {
int x; // expected-note{{initialize the variable 'x' to silence this warning}}
- if (y)
+ if (y) // expected-note{{uninitialized use occurs whenever 'if' condition is false}}
x = 1;
- return x; // expected-warning{{variable 'x' may be uninitialized when used here}}
+ return x; // expected-warning{{variable 'x' is sometimes uninitialized when used here}}
}
int test7b(int y) {
int x = x; // expected-note{{variable 'x' is declared here}}
if (y)
x = 1;
- // Warn with "may be uninitialized" here (not "is uninitialized"), since the
- // self-initialization is intended to suppress a -Wuninitialized warning.
+ // Warn with "may be uninitialized" here (not "is sometimes uninitialized"),
+ // since the self-initialization is intended to suppress a -Wuninitialized
+ // warning.
return x; // expected-warning{{variable 'x' may be uninitialized when used here}}
}
int test41(int x) {
int y; // expected-note{{initialize the variable 'y' to silence this warning}}
- if (x) y = 1; // no-warning
- return y; // expected-warning {{variable 'y' may be uninitialized when used here}}
+ if (x) y = 1; // expected-note{{uninitialized use occurs whenever 'if' condition is false}}
+ return y; // expected-warning {{variable 'y' is sometimes uninitialized when used here}}
}
void test42() {