[cfe-commits] r157458 - in /cfe/trunk: include/clang/Analysis/Analyses/UninitializedValues.h include/clang/Basic/DiagnosticGroups.td include/clang/Basic/DiagnosticSemaKinds.td lib/Analysis/UninitializedValues.cpp lib/Sema/AnalysisBasedWarnings.cpp test/Analysis/uninit-sometimes.cpp test/Sema/uninit-variables.c

Richard Smith richard-llvm at metafoo.co.uk
Thu May 24 19:17:09 PDT 2012


Author: rsmith
Date: Thu May 24 21:17:09 2012
New Revision: 157458

URL: http://llvm.org/viewvc/llvm-project?rev=157458&view=rev
Log:
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.

Added:
    cfe/trunk/test/Analysis/uninit-sometimes.cpp
Modified:
    cfe/trunk/include/clang/Analysis/Analyses/UninitializedValues.h
    cfe/trunk/include/clang/Basic/DiagnosticGroups.td
    cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td
    cfe/trunk/lib/Analysis/UninitializedValues.cpp
    cfe/trunk/lib/Sema/AnalysisBasedWarnings.cpp
    cfe/trunk/test/Sema/uninit-variables.c

Modified: cfe/trunk/include/clang/Analysis/Analyses/UninitializedValues.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Analysis/Analyses/UninitializedValues.h?rev=157458&r1=157457&r2=157458&view=diff
==============================================================================
--- cfe/trunk/include/clang/Analysis/Analyses/UninitializedValues.h (original)
+++ cfe/trunk/include/clang/Analysis/Analyses/UninitializedValues.h Thu May 24 21:17:09 2012
@@ -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 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

Modified: cfe/trunk/include/clang/Basic/DiagnosticGroups.td
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Basic/DiagnosticGroups.td?rev=157458&r1=157457&r2=157458&view=diff
==============================================================================
--- cfe/trunk/include/clang/Basic/DiagnosticGroups.td (original)
+++ cfe/trunk/include/clang/Basic/DiagnosticGroups.td Thu May 24 21:17:09 2012
@@ -214,8 +214,9 @@
 
 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">;

Modified: cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td?rev=157458&r1=157457&r2=157458&view=diff
==============================================================================
--- cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td (original)
+++ cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td Thu May 24 21:17:09 2012
@@ -1180,6 +1180,9 @@
 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;
@@ -1188,12 +1191,21 @@
 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<

Modified: cfe/trunk/lib/Analysis/UninitializedValues.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Analysis/UninitializedValues.cpp?rev=157458&r1=157457&r2=157458&view=diff
==============================================================================
--- cfe/trunk/lib/Analysis/UninitializedValues.cpp (original)
+++ cfe/trunk/lib/Analysis/UninitializedValues.cpp Thu May 24 21:17:09 2012
@@ -128,6 +128,13 @@
   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
 
@@ -338,6 +345,7 @@
 class TransferFunctions : public StmtVisitor<TransferFunctions> {
   CFGBlockValues &vals;
   const CFG &cfg;
+  const CFGBlock *block;
   AnalysisDeclContext ∾
   UninitVariablesHandler *handler;
   
@@ -357,9 +365,9 @@
   
 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 @@
   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);
@@ -403,7 +531,7 @@
     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 @@
     }
   }
   // 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)) {

Modified: cfe/trunk/lib/Sema/AnalysisBasedWarnings.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/AnalysisBasedWarnings.cpp?rev=157458&r1=157457&r2=157458&view=diff
==============================================================================
--- cfe/trunk/lib/Sema/AnalysisBasedWarnings.cpp (original)
+++ cfe/trunk/lib/Sema/AnalysisBasedWarnings.cpp Thu May 24 21:17:09 2012
@@ -456,16 +456,93 @@
   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
@@ -491,20 +568,37 @@
       }
     }
 
-    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
@@ -700,13 +794,14 @@
 
 }
 
-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();
   }
 };
@@ -735,9 +830,8 @@
     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 @@
       // 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 @@
         
         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 @@
 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;
     }
   }
@@ -1131,6 +1228,8 @@
 
   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) {
     if (CFG *cfg = AC.getCFG()) {

Added: cfe/trunk/test/Analysis/uninit-sometimes.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Analysis/uninit-sometimes.cpp?rev=157458&view=auto
==============================================================================
--- cfe/trunk/test/Analysis/uninit-sometimes.cpp (added)
+++ cfe/trunk/test/Analysis/uninit-sometimes.cpp Thu May 24 21:17:09 2012
@@ -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;
+}

Modified: cfe/trunk/test/Sema/uninit-variables.c
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Sema/uninit-variables.c?rev=157458&r1=157457&r2=157458&view=diff
==============================================================================
--- cfe/trunk/test/Sema/uninit-variables.c (original)
+++ cfe/trunk/test/Sema/uninit-variables.c Thu May 24 21:17:09 2012
@@ -39,17 +39,18 @@
 
 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 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() {





More information about the cfe-commits mailing list