From: Richard Smith Date: Fri, 25 May 2012 02:17:09 +0000 (+0000) Subject: Split a chunk of -Wconditional-uninitialized warnings out into a separate flag, X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=2815e1a075c74143a0b60a632090ece1dffa5c7c;p=clang Split a chunk of -Wconditional-uninitialized warnings out into a separate flag, -Wsometimes-uninitialized. This detects cases where an explicitly-written branch inevitably leads to an uninitialized variable use (so either the branch is dead code or there is an uninitialized use bug). This chunk of warnings tentatively lives within -Wuninitialized, in order to give it more visibility to existing Clang users. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@157458 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/include/clang/Analysis/Analyses/UninitializedValues.h b/include/clang/Analysis/Analyses/UninitializedValues.h index 4ee66986be..45ce4de1f8 100644 --- a/include/clang/Analysis/Analyses/UninitializedValues.h +++ b/include/clang/Analysis/Analyses/UninitializedValues.h @@ -15,6 +15,8 @@ #ifndef LLVM_CLANG_UNINIT_VALS_H #define LLVM_CLANG_UNINIT_VALS_H +#include "llvm/ADT/SmallVector.h" + namespace clang { class AnalysisDeclContext; @@ -23,15 +25,67 @@ class DeclContext; 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 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::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 diff --git a/include/clang/Basic/DiagnosticGroups.td b/include/clang/Basic/DiagnosticGroups.td index 8493257ba5..76f1e41495 100644 --- a/include/clang/Basic/DiagnosticGroups.td +++ b/include/clang/Basic/DiagnosticGroups.td @@ -214,8 +214,9 @@ def Trigraphs : DiagGroup<"trigraphs">; 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">; diff --git a/include/clang/Basic/DiagnosticSemaKinds.td b/include/clang/Basic/DiagnosticSemaKinds.td index e9f6dec562..2f64326222 100644 --- a/include/clang/Basic/DiagnosticSemaKinds.td +++ b/include/clang/Basic/DiagnosticSemaKinds.td @@ -1180,6 +1180,9 @@ def warn_uninit_self_reference_in_init : Warning< def warn_uninit_var : Warning< "variable %0 is uninitialized when used here">, InGroup, DefaultIgnore; +def warn_sometimes_uninit_var : Warning< + "variable %0 is sometimes uninitialized when used here">, + InGroup, DefaultIgnore; def warn_maybe_uninit_var : Warning<"variable %0 may be uninitialized when used here">, InGroup, DefaultIgnore; @@ -1188,12 +1191,21 @@ def note_uninit_var_def : Note< def warn_uninit_var_captured_by_block : Warning< "variable %0 is uninitialized when captured by block">, InGroup, DefaultIgnore; +def warn_sometimes_uninit_var_captured_by_block : Warning< + "variable %0 is sometimes uninitialized when captured by block">, + InGroup, DefaultIgnore; def warn_maybe_uninit_var_captured_by_block : Warning< "variable %0 may be uninitialized when captured by block">, InGroup, DefaultIgnore; def warn_uninit_byref_blockvar_captured_by_block : Warning< "block pointer variable %0 is uninitialized when captured by block">, InGroup, 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< diff --git a/lib/Analysis/UninitializedValues.cpp b/lib/Analysis/UninitializedValues.cpp index c1b9a96f03..13c6e4a783 100644 --- a/lib/Analysis/UninitializedValues.cpp +++ b/lib/Analysis/UninitializedValues.cpp @@ -128,6 +128,13 @@ public: ValueVector &getScratch() { return scratch; } ValueVector::reference operator[](const VarDecl *vd); + + Value getValue(const CFGBlock *block, const CFGBlock *dstBlock, + const VarDecl *vd) { + const llvm::Optional &idx = declToIndex.getValueIndex(vd); + assert(idx.hasValue()); + return getValueVector(block, dstBlock)[idx.getValue()]; + } }; } // end anonymous namespace @@ -338,6 +345,7 @@ public: class TransferFunctions : public StmtVisitor { CFGBlockValues &vals; const CFG &cfg; + const CFGBlock *block; AnalysisDeclContext ∾ UninitVariablesHandler *handler; @@ -357,9 +365,9 @@ class TransferFunctions : public StmtVisitor { 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) {} @@ -373,11 +381,131 @@ public: void VisitCastExpr(CastExpr *ce); void VisitObjCForCollectionStmt(ObjCForCollectionStmt *fs); void Visit(Stmt *s); - + bool isTrackedVar(const VarDecl *vd) { return ::isTrackedVar(vd, cast(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 Queue; + llvm::SmallVector 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(Term)) { + const Stmt *Label = Succ->getLabel(); + if (!Label || !isa(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); @@ -403,7 +531,7 @@ void TransferFunctions::reportUse(const Expr *ex, const VarDecl *vd) { 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) { @@ -652,7 +780,7 @@ static bool runOnBlock(const CFGBlock *block, const CFG &cfg, } } // 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(&*I)) { diff --git a/lib/Sema/AnalysisBasedWarnings.cpp b/lib/Sema/AnalysisBasedWarnings.cpp index 01deec1975..bbcd6a8e14 100644 --- a/lib/Sema/AnalysisBasedWarnings.cpp +++ b/lib/Sema/AnalysisBasedWarnings.cpp @@ -456,16 +456,93 @@ static bool SuggestInitializationFixit(Sema &S, const VarDecl *VD) { 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(Term)->getCond()->getSourceRange(); + break; + case Stmt::ConditionalOperatorClass: + DiagKind = 0; + Str = "?:"; + Range = cast(Term)->getCond()->getSourceRange(); + break; + case Stmt::BinaryOperatorClass: { + const BinaryOperator *BO = cast(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(Term)->getCond()->getSourceRange(); + break; + case Stmt::ForStmtClass: + DiagKind = 1; + Str = "for"; + Range = cast(Term)->getCond()->getSourceRange(); + break; + case Stmt::CXXForRangeStmtClass: + DiagKind = 1; + Str = "for"; + Range = cast(Term)->getCond()->getSourceRange(); + break; + + // "condition is true / loop is exited". + case Stmt::DoStmtClass: + DiagKind = 2; + Str = "do"; + Range = cast(Term)->getCond()->getSourceRange(); + break; + + // "switch case is taken". + case Stmt::CaseStmtClass: + DiagKind = 3; + Str = "case"; + Range = cast(Term)->getLHS()->getSourceRange(); + break; + case Stmt::DefaultStmtClass: + DiagKind = 3; + Str = "default"; + Range = cast(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(E)) { + if (const DeclRefExpr *DRE = dyn_cast(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 @@ -491,20 +568,37 @@ static bool DiagnoseUninitializedUse(Sema &S, const VarDecl *VD, } } - 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(E); + const BlockExpr *BE = cast(Use.getUser()); if (VD->getType()->isBlockPointerType() && !VD->hasAttr()) 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 @@ -700,13 +794,14 @@ static void DiagnoseSwitchLabelsFallthrough(Sema &S, AnalysisDeclContext &AC) { } -typedef std::pair 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(); } }; @@ -735,9 +830,8 @@ public: 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) { @@ -761,8 +855,9 @@ public: // 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 @@ -772,8 +867,10 @@ public: 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; @@ -789,7 +886,7 @@ public: 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; } } @@ -1130,6 +1227,8 @@ AnalysisBasedWarnings::IssueWarnings(sema::AnalysisBasedWarnings::Policy P, } 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) { diff --git a/test/Analysis/uninit-sometimes.cpp b/test/Analysis/uninit-sometimes.cpp new file mode 100644 index 0000000000..0c77f892e1 --- /dev/null +++ b/test/Analysis/uninit-sometimes.cpp @@ -0,0 +1,258 @@ +// 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; +} diff --git a/test/Sema/uninit-variables.c b/test/Sema/uninit-variables.c index 30db640323..1be8da8148 100644 --- a/test/Sema/uninit-variables.c +++ b/test/Sema/uninit-variables.c @@ -39,17 +39,18 @@ int test6() { 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}} } @@ -293,8 +294,8 @@ int test40(int x) { 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() {