r189594 - Consumed analysis: improve handling of conditionals.

DeLesley Hutchins delesley at google.com
Thu Aug 29 10:26:58 PDT 2013


Author: delesley
Date: Thu Aug 29 12:26:57 2013
New Revision: 189594

URL: http://llvm.org/viewvc/llvm-project?rev=189594&view=rev
Log:
Consumed analysis: improve handling of conditionals.
Patch by chris.wailes at gmail.com.

* The TestedVarsVisitor was folded into the ConsumedStmtVisitor.
* The VarTestResult class was updated to allow these changes.
* The PropagationInfo class was updated for the same reasons.
* Correctly handle short-circuiting of Boolean operations.
* Blocks are now marked as unreachable when we can statically prove we will
  never branch to them.
* Unreachable blocks are skipped by the analysis.

Modified:
    cfe/trunk/include/clang/Analysis/Analyses/Consumed.h
    cfe/trunk/lib/Analysis/Consumed.cpp
    cfe/trunk/test/SemaCXX/warn-consumed-analysis-strict.cpp
    cfe/trunk/test/SemaCXX/warn-consumed-analysis.cpp

Modified: cfe/trunk/include/clang/Analysis/Analyses/Consumed.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Analysis/Analyses/Consumed.h?rev=189594&r1=189593&r2=189594&view=diff
==============================================================================
--- cfe/trunk/include/clang/Analysis/Analyses/Consumed.h (original)
+++ cfe/trunk/include/clang/Analysis/Analyses/Consumed.h Thu Aug 29 12:26:57 2013
@@ -24,7 +24,9 @@
 
 namespace clang {
 namespace consumed {
-
+  
+  class ConsumedStmtVisitor;
+  
   typedef SmallVector<PartialDiagnosticAt, 1> OptionalNotes;
   typedef std::pair<PartialDiagnosticAt, OptionalNotes> DelayedDiag;
   typedef std::list<DelayedDiag> DiagList;
@@ -38,7 +40,7 @@ namespace consumed {
     /// \brief Emit the warnings and notes left by the analysis.
     virtual void emitDiagnostics() {}
 
-    /// Warn about unnecessary-test errors.
+    /// \brief Warn about unnecessary-test errors.
     /// \param VariableName -- The name of the variable that holds the unique
     /// value.
     ///
@@ -47,7 +49,7 @@ namespace consumed {
                                      StringRef VariableState,
                                      SourceLocation Loc) {}
 
-    /// Warn about use-while-consumed errors.
+    /// \brief Warn about use-while-consumed errors.
     /// \param MethodName -- The name of the method that was incorrectly
     /// invoked.
     ///
@@ -55,7 +57,7 @@ namespace consumed {
     virtual void warnUseOfTempWhileConsumed(StringRef MethodName,
                                             SourceLocation Loc) {}
 
-    /// Warn about use-in-unknown-state errors.
+    /// \brief Warn about use-in-unknown-state errors.
     /// \param MethodName -- The name of the method that was incorrectly
     /// invoked.
     ///
@@ -63,7 +65,7 @@ namespace consumed {
     virtual void warnUseOfTempInUnknownState(StringRef MethodName,
                                              SourceLocation Loc) {}
 
-    /// Warn about use-while-consumed errors.
+    /// \brief Warn about use-while-consumed errors.
     /// \param MethodName -- The name of the method that was incorrectly
     /// invoked.
     ///
@@ -75,7 +77,7 @@ namespace consumed {
                                       StringRef VariableName,
                                       SourceLocation Loc) {}
 
-    /// Warn about use-in-unknown-state errors.
+    /// \brief Warn about use-in-unknown-state errors.
     /// \param MethodName -- The name of the method that was incorrectly
     /// invoked.
     ///
@@ -104,18 +106,35 @@ namespace consumed {
     
   protected:
     
+    bool Reachable;
+    const Stmt *From;
     MapType Map;
     
   public:
+    ConsumedStateMap() : Reachable(true), From(NULL) {}
+    ConsumedStateMap(const ConsumedStateMap &Other)
+      : Reachable(Other.Reachable), From(Other.From), Map(Other.Map) {}
+    
     /// \brief Get the consumed state of a given variable.
     ConsumedState getState(const VarDecl *Var);
     
     /// \brief Merge this state map with another map.
     void intersect(const ConsumedStateMap *Other);
     
+    /// \brief Return true if this block is reachable.
+    bool isReachable() const { return Reachable; }
+    
     /// \brief Mark all variables as unknown.
     void makeUnknown();
     
+    /// \brief Mark the block as unreachable.
+    void markUnreachable();
+    
+    /// \brief Set the source for a decision about the branching of states.
+    /// \param Source -- The statement that was the origin of a branching
+    /// decision.
+    void setSource(const Stmt *Source) { this->From = Source; }
+    
     /// \brief Set the consumed state of a given variable.
     void setState(const VarDecl *Var, ConsumedState State);
     
@@ -144,18 +163,6 @@ namespace consumed {
     
     void markVisited(const CFGBlock *Block);
   };
-  
-  struct VarTestResult {
-    const VarDecl *Var;
-    SourceLocation Loc;
-    bool UnconsumedInTrueBranch;
-    
-    VarTestResult() : Var(NULL), Loc(), UnconsumedInTrueBranch(true) {}
-    
-    VarTestResult(const VarDecl *Var, SourceLocation Loc,
-                  bool UnconsumedInTrueBranch)
-      : Var(Var), Loc(Loc), UnconsumedInTrueBranch(UnconsumedInTrueBranch) {}
-  };
 
   /// A class that handles the analysis of uniqueness violations.
   class ConsumedAnalyzer {
@@ -169,7 +176,8 @@ namespace consumed {
     CacheMapType ConsumableTypeCache;
     
     bool hasConsumableAttributes(const CXXRecordDecl *RD);
-    void splitState(const CFGBlock *CurrBlock, const IfStmt *Terminator);
+    bool splitState(const CFGBlock *CurrBlock,
+                    const ConsumedStmtVisitor &Visitor);
     
   public:
     

Modified: cfe/trunk/lib/Analysis/Consumed.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Analysis/Consumed.cpp?rev=189594&r1=189593&r2=189594&view=diff
==============================================================================
--- cfe/trunk/lib/Analysis/Consumed.cpp (original)
+++ cfe/trunk/lib/Analysis/Consumed.cpp Thu Aug 29 12:26:57 2013
@@ -28,10 +28,16 @@
 #include "clang/Basic/SourceLocation.h"
 #include "llvm/ADT/DenseMap.h"
 #include "llvm/ADT/SmallVector.h"
+#include "llvm/Support/Compiler.h"
 #include "llvm/Support/raw_ostream.h"
 
+// TODO: Correctly identify unreachable blocks when chaining boolean operators.
+// TODO: Warn about unreachable code.
+// TODO: Switch to using a bitmap to track unreachable blocks.
 // TODO: Mark variables as Unknown going into while- or for-loops only if they
 //       are referenced inside that block. (Deferred)
+// TODO: Handle variable definitions, e.g. bool valid = x.isValid();
+//       if (valid) ...; (Deferred)
 // TODO: Add a method(s) to identify which method calls perform what state
 //       transitions. (Deferred)
 // TODO: Take notes on state transitions to provide better warning messages.
@@ -47,6 +53,31 @@ using namespace consumed;
 // Key method definition
 ConsumedWarningsHandlerBase::~ConsumedWarningsHandlerBase() {}
 
+static ConsumedState invertConsumedUnconsumed(ConsumedState State) {
+  switch (State) {
+  case CS_Unconsumed:
+    return CS_Consumed;
+  case CS_Consumed:
+    return CS_Unconsumed;
+  case CS_None:
+    return CS_None;
+  case CS_Unknown:
+    return CS_Unknown;
+  }
+  llvm_unreachable("invalid enum");
+}
+
+static bool isKnownState(ConsumedState State) {
+  switch (State) {
+  case CS_Unconsumed:
+  case CS_Consumed:
+    return true;
+  case CS_None:
+  case CS_Unknown:
+    return false;
+  }
+}
+
 static bool isTestingFunction(const FunctionDecl *FunDecl) {
   return FunDecl->hasAttr<TestsUnconsumedAttr>();
 }
@@ -69,40 +100,143 @@ static StringRef stateToString(ConsumedS
 }
 
 namespace {
-class ConsumedStmtVisitor : public ConstStmtVisitor<ConsumedStmtVisitor> {
+struct VarTestResult {
+  const VarDecl *Var;
+  ConsumedState TestsFor;
+};
+} // end anonymous::VarTestResult
+
+namespace clang {
+namespace consumed {
+
+enum EffectiveOp {
+  EO_And,
+  EO_Or
+};
+
+class PropagationInfo {
+  enum {
+    IT_None,
+    IT_State,
+    IT_Test,
+    IT_BinTest,
+    IT_Var
+  } InfoType;
   
-  union PropagationUnion {
+  union {
     ConsumedState State;
+    VarTestResult Test;
     const VarDecl *Var;
+    struct {
+      const BinaryOperator *Source;
+      EffectiveOp EOp;
+      VarTestResult LTest;
+      VarTestResult RTest;
+    } BinTest;
   };
   
-  class PropagationInfo {
-    PropagationUnion StateOrVar;
+public:
+  PropagationInfo() : InfoType(IT_None) {}
   
-  public:
-    bool IsVar;
+  PropagationInfo(const VarTestResult &Test) : InfoType(IT_Test), Test(Test) {}
+  PropagationInfo(const VarDecl *Var, ConsumedState TestsFor)
+    : InfoType(IT_Test) {
     
-    PropagationInfo() : IsVar(false) {
-      StateOrVar.State = consumed::CS_None;
-    }
+    Test.Var      = Var;
+    Test.TestsFor = TestsFor;
+  }
+  
+  PropagationInfo(const BinaryOperator *Source, EffectiveOp EOp,
+                  const VarTestResult &LTest, const VarTestResult &RTest)
+    : InfoType(IT_BinTest) {
     
-    PropagationInfo(ConsumedState State) : IsVar(false) {
-      StateOrVar.State = State;
-    }
+    BinTest.Source  = Source;
+    BinTest.EOp     = EOp;
+    BinTest.LTest   = LTest;
+    BinTest.RTest   = RTest;
+  }
+  
+  PropagationInfo(const BinaryOperator *Source, EffectiveOp EOp,
+                  const VarDecl *LVar, ConsumedState LTestsFor,
+                  const VarDecl *RVar, ConsumedState RTestsFor)
+    : InfoType(IT_BinTest) {
     
-    PropagationInfo(const VarDecl *Var) : IsVar(true) {
-      StateOrVar.Var = Var;
-    }
+    BinTest.Source         = Source;
+    BinTest.EOp            = EOp;
+    BinTest.LTest.Var      = LVar;
+    BinTest.LTest.TestsFor = LTestsFor;
+    BinTest.RTest.Var      = RVar;
+    BinTest.RTest.TestsFor = RTestsFor;
+  }
+  
+  PropagationInfo(ConsumedState State) : InfoType(IT_State), State(State) {}
+  PropagationInfo(const VarDecl *Var) : InfoType(IT_Var), Var(Var) {}
+  
+  const ConsumedState & getState() const {
+    assert(InfoType == IT_State);
+    return State;
+  }
+  
+  const VarTestResult & getTest() const {
+    assert(InfoType == IT_Test);
+    return Test;
+  }
+  
+  const VarTestResult & getLTest() const {
+    assert(InfoType == IT_BinTest);
+    return BinTest.LTest;
+  }
+  
+  const VarTestResult & getRTest() const {
+    assert(InfoType == IT_BinTest);
+    return BinTest.RTest;
+  }
+  
+  const VarDecl * getVar() const {
+    assert(InfoType == IT_Var);
+    return Var;
+  }
+  
+  EffectiveOp testEffectiveOp() const {
+    assert(InfoType == IT_BinTest);
+    return BinTest.EOp;
+  }
+  
+  const BinaryOperator * testSourceNode() const {
+    assert(InfoType == IT_BinTest);
+    return BinTest.Source;
+  }
+  
+  bool isValid()   const { return InfoType != IT_None;     }
+  bool isState()   const { return InfoType == IT_State;    }
+  bool isTest()    const { return InfoType == IT_Test;     }
+  bool isBinTest() const { return InfoType == IT_BinTest;  }
+  bool isVar()     const { return InfoType == IT_Var;      }
+  
+  PropagationInfo invertTest() const {
+    assert(InfoType == IT_Test || InfoType == IT_BinTest);
     
-    ConsumedState getState() const { return StateOrVar.State; }
+    if (InfoType == IT_Test) {
+      return PropagationInfo(Test.Var, invertConsumedUnconsumed(Test.TestsFor));
     
-    const VarDecl * getVar() const { return IsVar ? StateOrVar.Var : NULL; }
-  };
+    } else if (InfoType == IT_BinTest) {
+      return PropagationInfo(BinTest.Source,
+        BinTest.EOp == EO_And ? EO_Or : EO_And,
+        BinTest.LTest.Var, invertConsumedUnconsumed(BinTest.LTest.TestsFor),
+        BinTest.RTest.Var, invertConsumedUnconsumed(BinTest.RTest.TestsFor));
+    } else {
+      return PropagationInfo();
+    }
+  }
+};
+
+class ConsumedStmtVisitor : public ConstStmtVisitor<ConsumedStmtVisitor> {
   
   typedef llvm::DenseMap<const Stmt *, PropagationInfo> MapType;
   typedef std::pair<const Stmt *, PropagationInfo> PairType;
   typedef MapType::iterator InfoEntry;
-
+  typedef MapType::const_iterator ConstInfoEntry;
+  
   AnalysisDeclContext &AC;
   ConsumedAnalyzer &Analyzer;
   ConsumedStateMap *StateMap;
@@ -112,10 +246,11 @@ class ConsumedStmtVisitor : public Const
                         const FunctionDecl *FunDecl,
                         const CallExpr *Call);
   void forwardInfo(const Stmt *From, const Stmt *To);
+  void handleTestingFunctionCall(const CallExpr *Call, const VarDecl *Var);
   bool isLikeMoveAssignment(const CXXMethodDecl *MethodDecl);
   
 public:
-  
+
   void Visit(const Stmt *StmtNode);
   
   void VisitBinaryOperator(const BinaryOperator *BinOp);
@@ -131,12 +266,20 @@ public:
   void VisitUnaryOperator(const UnaryOperator *UOp);
   void VisitVarDecl(const VarDecl *Var);
 
-  ConsumedStmtVisitor(AnalysisDeclContext &AC, ConsumedAnalyzer &Analyzer,
-                      ConsumedStateMap *StateMap)
-      : AC(AC), Analyzer(Analyzer), StateMap(StateMap) {}
-
-  void reset() {
-    PropagationMap.clear();
+  ConsumedStmtVisitor(AnalysisDeclContext &AC, ConsumedAnalyzer &Analyzer)
+      : AC(AC), Analyzer(Analyzer), StateMap(NULL) {}
+  
+  PropagationInfo getInfo(const Stmt *StmtNode) const {
+    ConstInfoEntry Entry = PropagationMap.find(StmtNode);
+    
+    if (Entry != PropagationMap.end())
+      return Entry->second;
+    else
+      return PropagationInfo();
+  }
+  
+  void reset(ConsumedStateMap *NewStateMap) {
+    StateMap = NewStateMap;
   }
 };
 
@@ -148,7 +291,7 @@ void ConsumedStmtVisitor::checkCallabili
   
   if (!FunDecl->hasAttr<CallableWhenUnconsumedAttr>()) return;
   
-  if (PInfo.IsVar) {
+  if (PInfo.isVar()) {
     const VarDecl *Var = PInfo.getVar();
     
     switch (StateMap->getState(Var)) {
@@ -191,9 +334,24 @@ void ConsumedStmtVisitor::checkCallabili
 void ConsumedStmtVisitor::forwardInfo(const Stmt *From, const Stmt *To) {
   InfoEntry Entry = PropagationMap.find(From);
   
-  if (Entry != PropagationMap.end()) {
-    PropagationMap.insert(PairType(To, PropagationInfo(Entry->second)));
+  if (Entry != PropagationMap.end())
+    PropagationMap.insert(PairType(To, Entry->second));
+}
+
+void ConsumedStmtVisitor::handleTestingFunctionCall(const CallExpr *Call,
+                                                    const VarDecl  *Var) {
+  
+  ConsumedState VarState = StateMap->getState(Var);
+  
+  if (VarState != CS_Unknown) {
+    SourceLocation CallLoc = Call->getExprLoc();
+    
+    if (!CallLoc.isMacroID())
+      Analyzer.WarningsHandler.warnUnnecessaryTest(Var->getNameAsString(),
+        stateToString(VarState), CallLoc);
   }
+  
+  PropagationMap.insert(PairType(Call, PropagationInfo(Var, CS_Unconsumed)));
 }
 
 bool ConsumedStmtVisitor::isLikeMoveAssignment(
@@ -205,8 +363,49 @@ bool ConsumedStmtVisitor::isLikeMoveAssi
           MethodDecl->getParamDecl(0)->getType()->isRValueReferenceType());
 }
 
+void ConsumedStmtVisitor::Visit(const Stmt *StmtNode) {
+  
+  ConstStmtVisitor<ConsumedStmtVisitor>::Visit(StmtNode);
+  
+  for (Stmt::const_child_iterator CI = StmtNode->child_begin(),
+       CE = StmtNode->child_end(); CI != CE; ++CI) {
+    
+    PropagationMap.erase(*CI);
+  }
+}
+
 void ConsumedStmtVisitor::VisitBinaryOperator(const BinaryOperator *BinOp) {
   switch (BinOp->getOpcode()) {
+  case BO_LAnd:
+  case BO_LOr : {
+    InfoEntry LEntry = PropagationMap.find(BinOp->getLHS()),
+              REntry = PropagationMap.find(BinOp->getRHS());
+    
+    VarTestResult LTest, RTest;
+    
+    if (LEntry != PropagationMap.end() && LEntry->second.isTest()) {
+      LTest = LEntry->second.getTest();
+      
+    } else {
+      LTest.Var      = NULL;
+      LTest.TestsFor = CS_None;
+    }
+    
+    if (REntry != PropagationMap.end() && REntry->second.isTest()) {
+      RTest = REntry->second.getTest();
+      
+    } else {
+      RTest.Var      = NULL;
+      RTest.TestsFor = CS_None;
+    }
+    
+    if (!(LTest.Var == NULL && RTest.Var == NULL))
+      PropagationMap.insert(PairType(BinOp, PropagationInfo(BinOp,
+        static_cast<EffectiveOp>(BinOp->getOpcode() == BO_LOr), LTest, RTest)));
+    
+    break;
+  }
+    
   case BO_PtrMemD:
   case BO_PtrMemI:
     forwardInfo(BinOp->getLHS(), BinOp);
@@ -217,16 +416,6 @@ void ConsumedStmtVisitor::VisitBinaryOpe
   }
 }
 
-void ConsumedStmtVisitor::Visit(const Stmt *StmtNode) {
-  ConstStmtVisitor<ConsumedStmtVisitor>::Visit(StmtNode);
-  
-  for (Stmt::const_child_iterator CI = StmtNode->child_begin(),
-       CE = StmtNode->child_end(); CI != CE; ++CI) {
-    
-    PropagationMap.erase(*CI);
-  }
-}
-
 void ConsumedStmtVisitor::VisitCallExpr(const CallExpr *Call) {
   if (const FunctionDecl *FunDecl =
     dyn_cast_or_null<FunctionDecl>(Call->getDirectCallee())) {
@@ -250,7 +439,7 @@ void ConsumedStmtVisitor::VisitCallExpr(
       
       InfoEntry Entry = PropagationMap.find(Call->getArg(Index));
       
-      if (Entry == PropagationMap.end() || !Entry->second.IsVar) {
+      if (Entry == PropagationMap.end() || !Entry->second.isVar()) {
         continue;
       }
       
@@ -274,10 +463,7 @@ void ConsumedStmtVisitor::VisitCallExpr(
 }
 
 void ConsumedStmtVisitor::VisitCastExpr(const CastExpr *Cast) {
-  InfoEntry Entry = PropagationMap.find(Cast->getSubExpr());
-  
-  if (Entry != PropagationMap.end())
-    PropagationMap.insert(PairType(Cast, Entry->second));
+  forwardInfo(Cast->getSubExpr(), Cast);
 }
 
 void ConsumedStmtVisitor::VisitCXXConstructExpr(const CXXConstructExpr *Call) {
@@ -298,7 +484,7 @@ void ConsumedStmtVisitor::VisitCXXConstr
       PropagationInfo PInfo =
         PropagationMap.find(Call->getArg(0))->second;
       
-      if (PInfo.IsVar) {
+      if (PInfo.isVar()) {
         const VarDecl* Var = PInfo.getVar();
         
         PropagationMap.insert(PairType(Call,
@@ -336,8 +522,10 @@ void ConsumedStmtVisitor::VisitCXXMember
     
     checkCallability(PInfo, MethodDecl, Call);
     
-    if (PInfo.IsVar) {
-      if (MethodDecl->hasAttr<ConsumesAttr>())
+    if (PInfo.isVar()) {
+      if (isTestingFunction(MethodDecl))
+        handleTestingFunctionCall(Call, PInfo.getVar());
+      else if (MethodDecl->hasAttr<ConsumesAttr>())
         StateMap->setState(PInfo.getVar(), consumed::CS_Consumed);
       else if (!MethodDecl->isConst())
         StateMap->setState(PInfo.getVar(), consumed::CS_Unknown);
@@ -367,7 +555,7 @@ void ConsumedStmtVisitor::VisitCXXOperat
       LPInfo = LEntry->second;
       RPInfo = REntry->second;
       
-      if (LPInfo.IsVar && RPInfo.IsVar) {
+      if (LPInfo.isVar() && RPInfo.isVar()) {
         StateMap->setState(LPInfo.getVar(),
           StateMap->getState(RPInfo.getVar()));
         
@@ -375,12 +563,12 @@ void ConsumedStmtVisitor::VisitCXXOperat
         
         PropagationMap.insert(PairType(Call, LPInfo));
         
-      } else if (LPInfo.IsVar && !RPInfo.IsVar) {
+      } else if (LPInfo.isVar() && !RPInfo.isVar()) {
         StateMap->setState(LPInfo.getVar(), RPInfo.getState());
         
         PropagationMap.insert(PairType(Call, LPInfo));
         
-      } else if (!LPInfo.IsVar && RPInfo.IsVar) {
+      } else if (!LPInfo.isVar() && RPInfo.isVar()) {
         PropagationMap.insert(PairType(Call,
           PropagationInfo(StateMap->getState(RPInfo.getVar()))));
         
@@ -395,7 +583,7 @@ void ConsumedStmtVisitor::VisitCXXOperat
       
       LPInfo = LEntry->second;
       
-      if (LPInfo.IsVar) {
+      if (LPInfo.isVar()) {
         StateMap->setState(LPInfo.getVar(), consumed::CS_Unknown);
         
         PropagationMap.insert(PairType(Call, LPInfo));
@@ -410,7 +598,7 @@ void ConsumedStmtVisitor::VisitCXXOperat
       
       RPInfo = REntry->second;
       
-      if (RPInfo.IsVar) {
+      if (RPInfo.isVar()) {
         const VarDecl *Var = RPInfo.getVar();
         
         PropagationMap.insert(PairType(Call,
@@ -434,14 +622,16 @@ void ConsumedStmtVisitor::VisitCXXOperat
       
       checkCallability(PInfo, FunDecl, Call);
       
-      if (PInfo.IsVar) {
-        if (FunDecl->hasAttr<ConsumesAttr>()) {
-          // Handle consuming operators.
+      if (PInfo.isVar()) {
+        if (isTestingFunction(FunDecl)) {
+          handleTestingFunctionCall(Call, PInfo.getVar());
+          
+        } else if (FunDecl->hasAttr<ConsumesAttr>()) {
           StateMap->setState(PInfo.getVar(), consumed::CS_Consumed);
+          
         } else if (const CXXMethodDecl *MethodDecl =
-          dyn_cast_or_null<CXXMethodDecl>(FunDecl)) {
+                     dyn_cast_or_null<CXXMethodDecl>(FunDecl)) {
           
-          // Handle non-constant member operators.
           if (!MethodDecl->isConst())
             StateMap->setState(PInfo.getVar(), consumed::CS_Unknown);
         }
@@ -482,11 +672,21 @@ void ConsumedStmtVisitor::VisitMemberExp
 }
 
 void ConsumedStmtVisitor::VisitUnaryOperator(const UnaryOperator *UOp) {
-  if (UOp->getOpcode() == UO_AddrOf) {
-    InfoEntry Entry = PropagationMap.find(UOp->getSubExpr());
-    
-    if (Entry != PropagationMap.end())
-      PropagationMap.insert(PairType(UOp, Entry->second));
+  InfoEntry Entry = PropagationMap.find(UOp->getSubExpr()->IgnoreParens());
+  if (Entry == PropagationMap.end()) return;
+  
+  switch (UOp->getOpcode()) {
+  case UO_AddrOf:
+    PropagationMap.insert(PairType(UOp, Entry->second));
+    break;
+  
+  case UO_LNot:
+    if (Entry->second.isTest() || Entry->second.isBinTest())
+      PropagationMap.insert(PairType(UOp, Entry->second.invertTest()));
+    break;
+  
+  default:
+    break;
   }
 }
 
@@ -495,74 +695,97 @@ void ConsumedStmtVisitor::VisitVarDecl(c
     PropagationInfo PInfo =
       PropagationMap.find(Var->getInit())->second;
     
-    StateMap->setState(Var, PInfo.IsVar ?
+    StateMap->setState(Var, PInfo.isVar() ?
       StateMap->getState(PInfo.getVar()) : PInfo.getState());
   }
 }
-} // end anonymous::ConsumedStmtVisitor
+}} // end clang::consumed::ConsumedStmtVisitor
 
-namespace {
+namespace clang {
+namespace consumed {
 
-// TODO: Handle variable definitions, e.g. bool valid = x.isValid();
-//       if (valid) ...; (Deferred)
-class TestedVarsVisitor : public RecursiveASTVisitor<TestedVarsVisitor> {
-  
-  bool Invert;
-  SourceLocation CurrTestLoc;
-  
-  ConsumedStateMap *StateMap;
-  
-public:
-  bool IsUsefulConditional;
-  VarTestResult Test;
+void splitVarStateForIf(const IfStmt * IfNode, const VarTestResult &Test,
+                        ConsumedStateMap *ThenStates,
+                        ConsumedStateMap *ElseStates) {
+
+  ConsumedState VarState = ThenStates->getState(Test.Var);
   
-  TestedVarsVisitor(ConsumedStateMap *StateMap) : Invert(false),
-    StateMap(StateMap), IsUsefulConditional(false) {}
+  if (VarState == CS_Unknown) {
+    ThenStates->setState(Test.Var, Test.TestsFor);
+    if (ElseStates)
+      ElseStates->setState(Test.Var, invertConsumedUnconsumed(Test.TestsFor));
   
-  bool VisitCallExpr(CallExpr *Call);
-  bool VisitDeclRefExpr(DeclRefExpr *DeclRef);
-  bool VisitUnaryOperator(UnaryOperator *UnaryOp);
-};
-
-bool TestedVarsVisitor::VisitCallExpr(CallExpr *Call) {
-  if (const FunctionDecl *FunDecl =
-    dyn_cast_or_null<FunctionDecl>(Call->getDirectCallee())) {
-    
-    if (isTestingFunction(FunDecl)) {
-      CurrTestLoc = Call->getExprLoc();
-      IsUsefulConditional = true;
-      return true;
-    }
+  } else if (VarState == invertConsumedUnconsumed(Test.TestsFor)) {
+    ThenStates->markUnreachable();
     
-    IsUsefulConditional = false;
+  } else if (VarState == Test.TestsFor && ElseStates) {
+    ElseStates->markUnreachable();
   }
-  
-  return false;
 }
 
-bool TestedVarsVisitor::VisitDeclRefExpr(DeclRefExpr *DeclRef) {
-  if (const VarDecl *Var = dyn_cast_or_null<VarDecl>(DeclRef->getDecl()))
-    if (StateMap->getState(Var) != consumed::CS_None)
-      Test = VarTestResult(Var, CurrTestLoc, !Invert);
+void splitVarStateForIfBinOp(const PropagationInfo &PInfo,
+  ConsumedStateMap *ThenStates, ConsumedStateMap *ElseStates) {
   
-  return true;
-}
-
-bool TestedVarsVisitor::VisitUnaryOperator(UnaryOperator *UnaryOp) {
-  if (UnaryOp->getOpcode() == UO_LNot) {
-    Invert = true;
-    TraverseStmt(UnaryOp->getSubExpr());
-    
-  } else {
-    IsUsefulConditional = false;
+  const VarTestResult &LTest = PInfo.getLTest(),
+                      &RTest = PInfo.getRTest();
+  
+  ConsumedState LState = LTest.Var ? ThenStates->getState(LTest.Var) : CS_None,
+                RState = RTest.Var ? ThenStates->getState(RTest.Var) : CS_None;
+  
+  if (LTest.Var) {
+    if (PInfo.testEffectiveOp() == EO_And) {
+      if (LState == CS_Unknown) {
+        ThenStates->setState(LTest.Var, LTest.TestsFor);
+        
+      } else if (LState == invertConsumedUnconsumed(LTest.TestsFor)) {
+        ThenStates->markUnreachable();
+        
+      } else if (LState == LTest.TestsFor && isKnownState(RState)) {
+        if (RState == RTest.TestsFor) {
+          if (ElseStates)
+            ElseStates->markUnreachable();
+        } else {
+          ThenStates->markUnreachable();
+        }
+      }
+      
+    } else {
+      if (LState == CS_Unknown && ElseStates) {
+        ElseStates->setState(LTest.Var,
+                             invertConsumedUnconsumed(LTest.TestsFor));
+      
+      } else if (LState == LTest.TestsFor && ElseStates) {
+        ElseStates->markUnreachable();
+        
+      } else if (LState == invertConsumedUnconsumed(LTest.TestsFor) &&
+                 isKnownState(RState)) {
+        
+        if (RState == RTest.TestsFor) {
+          if (ElseStates)
+            ElseStates->markUnreachable();
+        } else {
+          ThenStates->markUnreachable();
+        }
+      }
+    }
   }
   
-  return false;
+  if (RTest.Var) {
+    if (PInfo.testEffectiveOp() == EO_And) {
+      if (RState == CS_Unknown)
+        ThenStates->setState(RTest.Var, RTest.TestsFor);
+      else if (RState == invertConsumedUnconsumed(RTest.TestsFor))
+        ThenStates->markUnreachable();
+      
+    } else if (ElseStates) {
+      if (RState == CS_Unknown)
+        ElseStates->setState(RTest.Var,
+                             invertConsumedUnconsumed(RTest.TestsFor));
+      else if (RState == RTest.TestsFor)
+        ElseStates->markUnreachable();
+    }
+  }
 }
-} // end anonymouse::TestedVarsVisitor
-
-namespace clang {
-namespace consumed {
 
 void ConsumedBlockInfo::addInfo(const CFGBlock *Block,
                                 ConsumedStateMap *StateMap,
@@ -625,26 +848,34 @@ ConsumedState ConsumedStateMap::getState
 void ConsumedStateMap::intersect(const ConsumedStateMap *Other) {
   ConsumedState LocalState;
   
+  if (this->From && this->From == Other->From && !Other->Reachable) {
+    this->markUnreachable();
+    return;
+  }
+  
   for (MapType::const_iterator DMI = Other->Map.begin(),
        DME = Other->Map.end(); DMI != DME; ++DMI) {
     
     LocalState = this->getState(DMI->first);
     
-    if (LocalState != CS_None && LocalState != DMI->second)
-      setState(DMI->first, CS_Unknown);
+    if (LocalState == CS_None)
+      continue;
+    
+    if (LocalState != DMI->second)
+       Map[DMI->first] = CS_Unknown;
   }
 }
 
+void ConsumedStateMap::markUnreachable() {
+  this->Reachable = false;
+  Map.clear();
+}
+
 void ConsumedStateMap::makeUnknown() {
-  PairType Pair;
-  
   for (MapType::const_iterator DMI = Map.begin(), DME = Map.end(); DMI != DME;
        ++DMI) {
     
-    Pair = *DMI;
-    
-    Map.erase(Pair.first);
-    Map.insert(PairType(Pair.first, CS_Unknown));
+    Map[DMI->first] = CS_Unknown;
   }
 }
 
@@ -694,42 +925,98 @@ bool ConsumedAnalyzer::hasConsumableAttr
   return false;
 }
 
-// TODO: Handle other forms of branching with precision, including while- and
-//       for-loops. (Deferred)
-void ConsumedAnalyzer::splitState(const CFGBlock *CurrBlock,
-                                  const IfStmt *Terminator) {
-  
-  TestedVarsVisitor Visitor(CurrStates);
-  Visitor.TraverseStmt(const_cast<Expr*>(Terminator->getCond()));
-  
-  bool HasElse = Terminator->getElse() != NULL;
+bool ConsumedAnalyzer::splitState(const CFGBlock *CurrBlock,
+                                  const ConsumedStmtVisitor &Visitor) {
   
-  ConsumedStateMap *ElseOrMergeStates = new ConsumedStateMap(*CurrStates);
+  ConsumedStateMap *FalseStates = new ConsumedStateMap(*CurrStates);
+  PropagationInfo PInfo;
   
-  if (Visitor.IsUsefulConditional) {
-    ConsumedState VarState = CurrStates->getState(Visitor.Test.Var);
+  if (const IfStmt *IfNode =
+    dyn_cast_or_null<IfStmt>(CurrBlock->getTerminator().getStmt())) {
     
-    if (VarState != CS_Unknown) {
-      // FIXME: Make this not warn if the test is from a macro expansion.
-      //        (Deferred)
-      WarningsHandler.warnUnnecessaryTest(Visitor.Test.Var->getNameAsString(),
-        stateToString(VarState), Visitor.Test.Loc);
-    }
+    bool HasElse = IfNode->getElse() != NULL;
+    const Stmt *Cond = IfNode->getCond();
+    
+    PInfo = Visitor.getInfo(Cond);
+    if (!PInfo.isValid() && isa<BinaryOperator>(Cond))
+      PInfo = Visitor.getInfo(cast<BinaryOperator>(Cond)->getRHS());
     
-    if (Visitor.Test.UnconsumedInTrueBranch) {
-      CurrStates->setState(Visitor.Test.Var, CS_Unconsumed);
-      if (HasElse) ElseOrMergeStates->setState(Visitor.Test.Var, CS_Consumed);
+    if (PInfo.isTest()) {
+      CurrStates->setSource(Cond);
+      FalseStates->setSource(Cond);
+      
+      splitVarStateForIf(IfNode, PInfo.getTest(), CurrStates,
+                         HasElse ? FalseStates : NULL);
+      
+    } else if (PInfo.isBinTest()) {
+      CurrStates->setSource(PInfo.testSourceNode());
+      FalseStates->setSource(PInfo.testSourceNode());
+      
+      splitVarStateForIfBinOp(PInfo, CurrStates, HasElse ? FalseStates : NULL);
       
     } else {
-      CurrStates->setState(Visitor.Test.Var, CS_Consumed);
-      if (HasElse) ElseOrMergeStates->setState(Visitor.Test.Var, CS_Unconsumed);
+      delete FalseStates;
+      return false;
     }
-  }
     
+  } else if (const BinaryOperator *BinOp =
+    dyn_cast_or_null<BinaryOperator>(CurrBlock->getTerminator().getStmt())) {
+    
+    PInfo = Visitor.getInfo(BinOp->getLHS());
+    if (!PInfo.isTest()) {
+      if ((BinOp = dyn_cast_or_null<BinaryOperator>(BinOp->getLHS()))) {
+        PInfo = Visitor.getInfo(BinOp->getRHS());
+        
+        if (!PInfo.isTest()) {
+          delete FalseStates;
+          return false;
+        }
+        
+      } else {
+        delete FalseStates;
+        return false;
+      }
+    }
+    
+    CurrStates->setSource(BinOp);
+    FalseStates->setSource(BinOp);
+    
+    const VarTestResult &Test = PInfo.getTest();
+    ConsumedState VarState = CurrStates->getState(Test.Var);
+    
+    if (BinOp->getOpcode() == BO_LAnd) {
+      if (VarState == CS_Unknown)
+        CurrStates->setState(Test.Var, Test.TestsFor);
+      else if (VarState == invertConsumedUnconsumed(Test.TestsFor))
+        CurrStates->markUnreachable();
+      
+    } else if (BinOp->getOpcode() == BO_LOr) {
+      if (VarState == CS_Unknown)
+        FalseStates->setState(Test.Var,
+                              invertConsumedUnconsumed(Test.TestsFor));
+      else if (VarState == Test.TestsFor)
+        FalseStates->markUnreachable();
+    }
+    
+  } else {
+    delete FalseStates;
+    return false;
+  }
+  
   CFGBlock::const_succ_iterator SI = CurrBlock->succ_begin();
   
-  if (*SI)   BlockInfo.addInfo(*SI,        CurrStates);
-  if (*++SI) BlockInfo.addInfo(*SI, ElseOrMergeStates);
+  if (*SI)
+    BlockInfo.addInfo(*SI, CurrStates);
+  else
+    delete CurrStates;
+    
+  if (*++SI)
+    BlockInfo.addInfo(*SI, FalseStates);
+  else
+    delete FalseStates;
+  
+  CurrStates = NULL;
+  return true;
 }
 
 void ConsumedAnalyzer::run(AnalysisDeclContext &AC) {
@@ -742,6 +1029,7 @@ void ConsumedAnalyzer::run(AnalysisDeclC
   PostOrderCFGView *SortedGraph = AC.getAnalysis<PostOrderCFGView>();
   
   CurrStates = new ConsumedStateMap();
+  ConsumedStmtVisitor Visitor(AC, *this);
   
   // Visit all of the function's basic blocks.
   for (PostOrderCFGView::iterator I = SortedGraph->begin(),
@@ -752,9 +1040,18 @@ void ConsumedAnalyzer::run(AnalysisDeclC
     
     if (CurrStates == NULL)
       CurrStates = BlockInfo.getInfo(CurrBlock);
-
-    ConsumedStmtVisitor Visitor(AC, *this, CurrStates);
-
+    
+    if (!CurrStates) {
+      continue;
+      
+    } else if (!CurrStates->isReachable()) {
+      delete CurrStates;
+      CurrStates = NULL;
+      continue;
+    }
+    
+    Visitor.reset(CurrStates);
+    
     // Visit all of the basic block's statements.
     for (CFGBlock::const_iterator BI = CurrBlock->begin(),
          BE = CurrBlock->end(); BI != BE; ++BI) {
@@ -770,36 +1067,34 @@ void ConsumedAnalyzer::run(AnalysisDeclC
       }
     }
     
-    if (const IfStmt *Terminator =
-      dyn_cast_or_null<IfStmt>(CurrBlock->getTerminator().getStmt())) {
-      
-      splitState(CurrBlock, Terminator);
-      CurrStates = NULL;
-    
-    } else if (CurrBlock->succ_size() > 1) {
-      CurrStates->makeUnknown();
-      
-      bool OwnershipTaken = false;
+    // TODO: Handle other forms of branching with precision, including while-
+    //       and for-loops. (Deferred)
+    if (!splitState(CurrBlock, Visitor)) {
+      CurrStates->setSource(NULL);
       
-      for (CFGBlock::const_succ_iterator SI = CurrBlock->succ_begin(),
-           SE = CurrBlock->succ_end(); SI != SE; ++SI) {
+      if (CurrBlock->succ_size() > 1) {
+        CurrStates->makeUnknown();
         
-        if (*SI) BlockInfo.addInfo(*SI, CurrStates, OwnershipTaken);
+        bool OwnershipTaken = false;
+        
+        for (CFGBlock::const_succ_iterator SI = CurrBlock->succ_begin(),
+             SE = CurrBlock->succ_end(); SI != SE; ++SI) {
+          
+          if (*SI) BlockInfo.addInfo(*SI, CurrStates, OwnershipTaken);
+        }
+        
+        if (!OwnershipTaken)
+          delete CurrStates;
+        
+        CurrStates = NULL;
+        
+      } else if (CurrBlock->succ_size() == 1 &&
+                 (*CurrBlock->succ_begin())->pred_size() > 1) {
+        
+        BlockInfo.addInfo(*CurrBlock->succ_begin(), CurrStates);
+        CurrStates = NULL;
       }
-      
-      if (!OwnershipTaken)
-        delete CurrStates;
-      
-      CurrStates = NULL;
-      
-    } else if (CurrBlock->succ_size() == 1 &&
-               (*CurrBlock->succ_begin())->pred_size() > 1) {
-      
-      BlockInfo.addInfo(*CurrBlock->succ_begin(), CurrStates);
-      CurrStates = NULL;
     }
-    
-    Visitor.reset();
   } // End of block iterator.
   
   // Delete the last existing state map.

Modified: cfe/trunk/test/SemaCXX/warn-consumed-analysis-strict.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/SemaCXX/warn-consumed-analysis-strict.cpp?rev=189594&r1=189593&r2=189594&view=diff
==============================================================================
--- cfe/trunk/test/SemaCXX/warn-consumed-analysis-strict.cpp (original)
+++ cfe/trunk/test/SemaCXX/warn-consumed-analysis-strict.cpp Thu Aug 29 12:26:57 2013
@@ -4,6 +4,8 @@
 #define CONSUMES __attribute__ ((consumes))
 #define TESTS_UNCONSUMED __attribute__ ((tests_unconsumed))
 
+#define TEST_VAR(Var) Var.isValid()
+
 typedef decltype(nullptr) nullptr_t;
 
 template <typename T>
@@ -11,7 +13,7 @@ class ConsumableClass {
   T var;
   
   public:
-  ConsumableClass(void);
+  ConsumableClass();
   ConsumableClass(T val);
   ConsumableClass(ConsumableClass<T> &other);
   ConsumableClass(ConsumableClass<T> &&other);
@@ -26,20 +28,24 @@ class ConsumableClass {
   template <typename U>
   ConsumableClass<T>& operator=(ConsumableClass<U> &&other);
   
-  void operator*(void) const CALLABLE_WHEN_UNCONSUMED;
-  
-  bool isValid(void) const TESTS_UNCONSUMED;
+  void operator()(int a) CONSUMES;
+  void operator*() const CALLABLE_WHEN_UNCONSUMED;
+  void unconsumedCall() const CALLABLE_WHEN_UNCONSUMED;
+  
+  bool isValid() const TESTS_UNCONSUMED;
+  operator bool() const TESTS_UNCONSUMED;
+  bool operator!=(nullptr_t) const TESTS_UNCONSUMED;
   
-  void constCall(void) const;
-  void nonconstCall(void);
+  void constCall() const;
+  void nonconstCall();
   
-  void consume(void) CONSUMES;
+  void consume() CONSUMES;
 };
 
 void baf0(ConsumableClass<int>  &var);
 void baf1(ConsumableClass<int>  *var);
 
-void testIfStmt(void) {
+void testIfStmt() {
   ConsumableClass<int> var;
   
   if (var.isValid()) { // expected-warning {{unnecessary test. Variable 'var' is known to be in the 'consumed' state}}
@@ -51,27 +57,113 @@ void testIfStmt(void) {
   }
 }
 
-void testConditionalMerge(void) {
-  ConsumableClass<int> var;
+void testComplexConditionals() {
+  ConsumableClass<int> var0, var1, var2;
   
-  if (var.isValid()) {// expected-warning {{unnecessary test. Variable 'var' is known to be in the 'consumed' state}}
+  // Coerce all variables into the unknown state.
+  baf0(var0);
+  baf0(var1);
+  baf0(var2);
+  
+  if (var0 && var1) {
+    *var0;
+    *var1;
     
-    // Empty
+  } else {
+    *var0; // expected-warning {{invocation of method 'operator*' on object 'var0' while it is in an unknown state}}
+    *var1; // expected-warning {{invocation of method 'operator*' on object 'var1' while it is in an unknown state}}
   }
   
-  *var; // expected-warning {{invocation of method 'operator*' on object 'var' while it is in an unknown state}}
+  if (var0 || var1) {
+    *var0; // expected-warning {{invocation of method 'operator*' on object 'var0' while it is in an unknown state}}
+    *var1; // expected-warning {{invocation of method 'operator*' on object 'var1' while it is in an unknown state}}
+    
+  } else {
+    *var0; // expected-warning {{invocation of method 'operator*' on object 'var0' while it is in the 'consumed' state}}
+    *var1; // expected-warning {{invocation of method 'operator*' on object 'var1' while it is in the 'consumed' state}}
+  }
   
-  if (var.isValid()) {
-    // Empty
+  if (var0 && !var1) {
+    *var0;
+    *var1; // expected-warning {{invocation of method 'operator*' on object 'var1' while it is in the 'consumed' state}}
     
   } else {
-    // Empty
+    *var0; // expected-warning {{invocation of method 'operator*' on object 'var0' while it is in an unknown state}}
+    *var1; // expected-warning {{invocation of method 'operator*' on object 'var1' while it is in an unknown state}}
   }
   
-  *var; // expected-warning {{invocation of method 'operator*' on object 'var' while it is in an unknown state}}
+  if (var0 || !var1) {
+    *var0; // expected-warning {{invocation of method 'operator*' on object 'var0' while it is in an unknown state}}
+    *var1; // expected-warning {{invocation of method 'operator*' on object 'var1' while it is in an unknown state}}
+    
+  } else {
+    *var0; // expected-warning {{invocation of method 'operator*' on object 'var0' while it is in the 'consumed' state}}
+    *var1;
+  }
+  
+  if (!var0 && !var1) {
+    *var0; // expected-warning {{invocation of method 'operator*' on object 'var0' while it is in the 'consumed' state}}
+    *var1; // expected-warning {{invocation of method 'operator*' on object 'var1' while it is in the 'consumed' state}}
+    
+  } else {
+    *var0; // expected-warning {{invocation of method 'operator*' on object 'var0' while it is in an unknown state}}
+    *var1; // expected-warning {{invocation of method 'operator*' on object 'var1' while it is in an unknown state}}
+  }
+  
+  if (!(var0 || var1)) {
+    *var0; // expected-warning {{invocation of method 'operator*' on object 'var0' while it is in the 'consumed' state}}
+    *var1; // expected-warning {{invocation of method 'operator*' on object 'var1' while it is in the 'consumed' state}}
+    
+  } else {
+    *var0; // expected-warning {{invocation of method 'operator*' on object 'var0' while it is in an unknown state}}
+    *var1; // expected-warning {{invocation of method 'operator*' on object 'var1' while it is in an unknown state}}
+  }
+  
+  if (!var0 || !var1) {
+    *var0; // expected-warning {{invocation of method 'operator*' on object 'var0' while it is in an unknown state}}
+    *var1; // expected-warning {{invocation of method 'operator*' on object 'var1' while it is in an unknown state}}
+    
+  } else {
+    *var0;
+    *var1;
+  }
+  
+  if (!(var0 && var1)) {
+    *var0; // expected-warning {{invocation of method 'operator*' on object 'var0' while it is in an unknown state}}
+    *var1; // expected-warning {{invocation of method 'operator*' on object 'var1' while it is in an unknown state}}
+    
+  } else {
+    *var0;
+    *var1;
+  }
+  
+  if (var0 && var1 && var2) {
+    *var0;
+    *var1;
+    *var2;
+    
+  } else {
+    *var0; // expected-warning {{invocation of method 'operator*' on object 'var0' while it is in an unknown state}}
+    *var1; // expected-warning {{invocation of method 'operator*' on object 'var1' while it is in an unknown state}}
+    *var2; // expected-warning {{invocation of method 'operator*' on object 'var2' while it is in an unknown state}}
+  }
+  
+#if 0
+  // FIXME: Get this test to pass.
+  if (var0 || var1 || var2) {
+    *var0; // expected-warning {{invocation of method 'operator*' on object 'var0' while it is in an unknown state}}
+    *var1; // expected-warning {{invocation of method 'operator*' on object 'var1' while it is in an unknown state}}
+    *var2; // expected-warning {{invocation of method 'operator*' on object 'var2' while it is in an unknown state}}
+    
+  } else {
+    *var0; // expected-warning {{invocation of method 'operator*' on object 'var0' while it is in the 'consumed' state}}
+    *var1; // expected-warning {{invocation of method 'operator*' on object 'var1' while it is in the 'consumed' state}}
+    *var2; // expected-warning {{invocation of method 'operator*' on object 'var2' while it is in the 'consumed' state}}
+  }
+#endif
 }
 
-void testCallingConventions(void) {
+void testCallingConventions() {
   ConsumableClass<int> var(42);
   
   baf0(var);  
@@ -82,14 +174,14 @@ void testCallingConventions(void) {
   *var; // expected-warning {{invocation of method 'operator*' on object 'var' while it is in an unknown state}}
 }
 
-void testMoveAsignmentish(void) {
+void testMoveAsignmentish() {
   ConsumableClass<int> var;
   
   var = nullptr;
   *var; // expected-warning {{invocation of method 'operator*' on object 'var' while it is in an unknown state}}
 }
 
-void testConstAndNonConstMemberFunctions(void) {
+void testConstAndNonConstMemberFunctions() {
   ConsumableClass<int> var(42);
   
   var.constCall();
@@ -99,7 +191,15 @@ void testConstAndNonConstMemberFunctions
   *var; // expected-warning {{invocation of method 'operator*' on object 'var' while it is in an unknown state}}
 }
 
-void testSimpleForLoop(void) {
+void testNoWarnTestFromMacroExpansion() {
+  ConsumableClass<int> var(42);
+  
+  if (TEST_VAR(var)) {
+    *var;
+  }
+}
+
+void testSimpleForLoop() {
   ConsumableClass<int> var;
   
   for (int i = 0; i < 10; ++i) {
@@ -109,7 +209,7 @@ void testSimpleForLoop(void) {
   *var; // expected-warning {{invocation of method 'operator*' on object 'var' while it is in an unknown state}}
 }
 
-void testSimpleWhileLoop(void) {
+void testSimpleWhileLoop() {
   int i = 0;
   
   ConsumableClass<int> var;

Modified: cfe/trunk/test/SemaCXX/warn-consumed-analysis.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/SemaCXX/warn-consumed-analysis.cpp?rev=189594&r1=189593&r2=189594&view=diff
==============================================================================
--- cfe/trunk/test/SemaCXX/warn-consumed-analysis.cpp (original)
+++ cfe/trunk/test/SemaCXX/warn-consumed-analysis.cpp Thu Aug 29 12:26:57 2013
@@ -11,7 +11,7 @@ class ConsumableClass {
   T var;
   
   public:
-  ConsumableClass(void);
+  ConsumableClass();
   ConsumableClass(nullptr_t p) CONSUMES;
   ConsumableClass(T val);
   ConsumableClass(ConsumableClass<T> &other);
@@ -28,17 +28,17 @@ class ConsumableClass {
   ConsumableClass<T>& operator=(ConsumableClass<U> &&other);
   
   void operator()(int a) CONSUMES;
-  void operator*(void) const CALLABLE_WHEN_UNCONSUMED;
-  void unconsumedCall(void) const CALLABLE_WHEN_UNCONSUMED;
+  void operator*() const CALLABLE_WHEN_UNCONSUMED;
+  void unconsumedCall() const CALLABLE_WHEN_UNCONSUMED;
   
-  bool isValid(void) const TESTS_UNCONSUMED;
+  bool isValid() const TESTS_UNCONSUMED;
   operator bool() const TESTS_UNCONSUMED;
   bool operator!=(nullptr_t) const TESTS_UNCONSUMED;
   
-  void constCall(void) const;
-  void nonconstCall(void);
+  void constCall() const;
+  void nonconstCall();
   
-  void consume(void) CONSUMES;
+  void consume() CONSUMES;
 };
 
 void baf0(const ConsumableClass<int>  var);
@@ -47,7 +47,7 @@ void baf2(const ConsumableClass<int> *va
 
 void baf3(ConsumableClass<int> &&var);
 
-void testInitialization(void) {
+void testInitialization() {
   ConsumableClass<int> var0;
   ConsumableClass<int> var1 = ConsumableClass<int>();
   
@@ -58,10 +58,10 @@ void testInitialization(void) {
   
   if (var0.isValid()) {
     *var0;
-    *var1;  // expected-warning {{invocation of method 'operator*' on object 'var1' while it is in the 'consumed' state}}
+    *var1;
     
   } else {
-    *var0;  // expected-warning {{invocation of method 'operator*' on object 'var0' while it is in the 'consumed' state}}
+    *var0; // expected-warning {{invocation of method 'operator*' on object 'var0' while it is in the 'consumed' state}}
   }
 }
 
@@ -69,7 +69,7 @@ void testTempValue() {
   *ConsumableClass<int>(); // expected-warning {{invocation of method 'operator*' on a temporary object while it is in the 'consumed' state}}
 }
 
-void testSimpleRValueRefs(void) {
+void testSimpleRValueRefs() {
   ConsumableClass<int> var0;
   ConsumableClass<int> var1(42);
   
@@ -82,39 +82,149 @@ void testSimpleRValueRefs(void) {
   *var1; // expected-warning {{invocation of method 'operator*' on object 'var1' while it is in the 'consumed' state}}
 }
 
-void testIfStmt(void) {
+void testIfStmt() {
   ConsumableClass<int> var;
   
   if (var.isValid()) {
-    // Empty
-    
+    *var;
   } else {
     *var; // expected-warning {{invocation of method 'operator*' on object 'var' while it is in the 'consumed' state}}
   }
   
   if (!var.isValid()) {
     *var; // expected-warning {{invocation of method 'operator*' on object 'var' while it is in the 'consumed' state}}
-    
   } else {
     *var;
   }
   
   if (var) {
     // Empty
-    
   } else {
     *var; // expected-warning {{invocation of method 'operator*' on object 'var' while it is in the 'consumed' state}}
   }
   
   if (var != nullptr) {
     // Empty
-    
   } else {
     *var; // expected-warning {{invocation of method 'operator*' on object 'var' while it is in the 'consumed' state}}
   }
 }
 
-void testCallingConventions(void) {
+void testComplexConditionals() {
+  ConsumableClass<int> var0, var1, var2;
+  
+  if (var0 && var1) {
+    *var0;
+    *var1;
+    
+  } else {
+    *var0; // expected-warning {{invocation of method 'operator*' on object 'var0' while it is in the 'consumed' state}}
+    *var1; // expected-warning {{invocation of method 'operator*' on object 'var1' while it is in the 'consumed' state}}
+  }
+  
+  if (var0 || var1) {
+    *var0;
+    *var1;
+    
+  } else {
+    *var0; // expected-warning {{invocation of method 'operator*' on object 'var0' while it is in the 'consumed' state}}
+    *var1; // expected-warning {{invocation of method 'operator*' on object 'var1' while it is in the 'consumed' state}}
+  }
+  
+  if (var0 && !var1) {
+    *var0;
+    *var1;
+    
+  } else {
+    *var0; // expected-warning {{invocation of method 'operator*' on object 'var0' while it is in the 'consumed' state}}
+    *var1; // expected-warning {{invocation of method 'operator*' on object 'var1' while it is in the 'consumed' state}}
+  }
+  
+  if (var0 || !var1) {
+    *var0; // expected-warning {{invocation of method 'operator*' on object 'var0' while it is in the 'consumed' state}}
+    *var1; // expected-warning {{invocation of method 'operator*' on object 'var1' while it is in the 'consumed' state}}
+    
+  } else {
+    *var0;
+    *var1;
+  }
+  
+  if (!var0 && !var1) {
+    *var0; // expected-warning {{invocation of method 'operator*' on object 'var0' while it is in the 'consumed' state}}
+    *var1; // expected-warning {{invocation of method 'operator*' on object 'var1' while it is in the 'consumed' state}}
+    
+  } else {
+    *var0;
+    *var1;
+  }
+  
+  if (!var0 || !var1) {
+    *var0; // expected-warning {{invocation of method 'operator*' on object 'var0' while it is in the 'consumed' state}}
+    *var1; // expected-warning {{invocation of method 'operator*' on object 'var1' while it is in the 'consumed' state}}
+    
+  } else {
+    *var0;
+    *var1;
+  }
+  
+  if (!(var0 && var1)) {
+    *var0; // expected-warning {{invocation of method 'operator*' on object 'var0' while it is in the 'consumed' state}}
+    *var1; // expected-warning {{invocation of method 'operator*' on object 'var1' while it is in the 'consumed' state}}
+    
+  } else {
+    *var0;
+    *var1;
+  }
+  
+  if (!(var0 || var1)) {
+    *var0; // expected-warning {{invocation of method 'operator*' on object 'var0' while it is in the 'consumed' state}}
+    *var1; // expected-warning {{invocation of method 'operator*' on object 'var1' while it is in the 'consumed' state}}
+    
+  } else {
+    *var0;
+    *var1;
+  }
+  
+  if (var0 && var1 && var2) {
+    *var0;
+    *var1;
+    *var2;
+    
+  } else {
+    *var0; // expected-warning {{invocation of method 'operator*' on object 'var0' while it is in the 'consumed' state}}
+    *var1; // expected-warning {{invocation of method 'operator*' on object 'var1' while it is in the 'consumed' state}}
+    *var2; // expected-warning {{invocation of method 'operator*' on object 'var2' while it is in the 'consumed' state}}
+  }
+  
+#if 0
+  // FIXME: Get this test to pass.
+  if (var0 || var1 || var2) {
+    *var0;
+    *var1;
+    *var2;
+    
+  } else {
+    *var0; // expected-warning {{invocation of method 'operator*' on object 'var0' while it is in the 'consumed' state}}
+    *var1; // expected-warning {{invocation of method 'operator*' on object 'var1' while it is in the 'consumed' state}}
+    *var2; // expected-warning {{invocation of method 'operator*' on object 'var2' while it is in the 'consumed' state}}
+  }
+#endif
+}
+
+void testStateChangeInBranch() {
+  ConsumableClass<int> var;
+  
+  // Make var enter the 'unknown' state.
+  baf1(var);
+  
+  if (!var) {
+    var = ConsumableClass<int>(42);
+  }
+  
+  *var;
+}
+
+void testCallingConventions() {
   ConsumableClass<int> var(42);
   
   baf0(var);  
@@ -130,7 +240,7 @@ void testCallingConventions(void) {
   *var; // expected-warning {{invocation of method 'operator*' on object 'var' while it is in the 'consumed' state}}
 }
 
-void testMoveAsignmentish(void) {
+void testMoveAsignmentish() {
   ConsumableClass<int>  var0;
   ConsumableClass<long> var1(42);
   
@@ -143,26 +253,25 @@ void testMoveAsignmentish(void) {
   *var1; // expected-warning {{invocation of method 'operator*' on object 'var1' while it is in the 'consumed' state}}
 }
 
-void testConditionalMerge(void) {
+void testConditionalMerge() {
   ConsumableClass<int> var;
   
   if (var.isValid()) {
     // Empty
   }
   
-  *var;
+  *var; // expected-warning {{invocation of method 'operator*' on object 'var' while it is in the 'consumed' state}}
   
   if (var.isValid()) {
     // Empty
-    
   } else {
     // Empty
   }
   
-  *var;
+  *var; // expected-warning {{invocation of method 'operator*' on object 'var' while it is in the 'consumed' state}}
 }
 
-void testConsumes0(void) {
+void testConsumes0() {
   ConsumableClass<int> var(42);
   
   *var;
@@ -172,13 +281,13 @@ void testConsumes0(void) {
   *var; // expected-warning {{invocation of method 'operator*' on object 'var' while it is in the 'consumed' state}}
 }
 
-void testConsumes1(void) {
+void testConsumes1() {
   ConsumableClass<int> var(nullptr);
   
   *var; // expected-warning {{invocation of method 'operator*' on object 'var' while it is in the 'consumed' state}}
 }
 
-void testConsumes2(void) {
+void testConsumes2() {
   ConsumableClass<int> var(42);
   
   var.unconsumedCall();
@@ -187,7 +296,19 @@ void testConsumes2(void) {
   var.unconsumedCall(); // expected-warning {{invocation of method 'unconsumedCall' on object 'var' while it is in the 'consumed' state}}
 }
 
-void testSimpleForLoop(void) {
+void testNonsenseState() {
+  ConsumableClass<int> var(42);
+  
+  if (var) {
+    *var;
+  } else {
+    *var;
+  }
+  
+  *var;
+}
+
+void testSimpleForLoop() {
   ConsumableClass<int> var;
   
   for (int i = 0; i < 10; ++i) {
@@ -197,7 +318,7 @@ void testSimpleForLoop(void) {
   *var;
 }
 
-void testSimpleWhileLoop(void) {
+void testSimpleWhileLoop() {
   int i = 0;
   
   ConsumableClass<int> var;





More information about the cfe-commits mailing list