r194900 - Consumed analysis: track state of temporary objects.

DeLesley Hutchins delesley at google.com
Fri Nov 15 16:22:44 PST 2013


Author: delesley
Date: Fri Nov 15 18:22:43 2013
New Revision: 194900

URL: http://llvm.org/viewvc/llvm-project?rev=194900&view=rev
Log:
Consumed analysis: track state of temporary objects.
Earlier versions discarded the state too soon, and did not track state changes,
e.g. when passing a temporary to a move constructor.  Patch by
chris.wailes at gmail.com; review and minor fixes by delesley.

Modified:
    cfe/trunk/include/clang/Analysis/Analyses/Consumed.h
    cfe/trunk/lib/Analysis/Consumed.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=194900&r1=194899&r2=194900&view=diff
==============================================================================
--- cfe/trunk/include/clang/Analysis/Analyses/Consumed.h (original)
+++ cfe/trunk/include/clang/Analysis/Analyses/Consumed.h Fri Nov 15 18:22:43 2013
@@ -130,28 +130,37 @@ namespace consumed {
 
   class ConsumedStateMap {
     
-    typedef llvm::DenseMap<const VarDecl *, ConsumedState> MapType;
-    typedef std::pair<const VarDecl *, ConsumedState> PairType;
+    typedef llvm::DenseMap<const VarDecl *, ConsumedState> VarMapType;
+    typedef llvm::DenseMap<const CXXBindTemporaryExpr *, ConsumedState>
+            TmpMapType;
     
   protected:
     
     bool Reachable;
     const Stmt *From;
-    MapType Map;
+    VarMapType VarMap;
+    TmpMapType TmpMap;
     
   public:
     ConsumedStateMap() : Reachable(true), From(NULL) {}
     ConsumedStateMap(const ConsumedStateMap &Other)
-      : Reachable(Other.Reachable), From(Other.From), Map(Other.Map) {}
+      : Reachable(Other.Reachable), From(Other.From), VarMap(Other.VarMap),
+        TmpMap() {}
     
     /// \brief Warn if any of the parameters being tracked are not in the state
     /// they were declared to be in upon return from a function.
     void checkParamsForReturnTypestate(SourceLocation BlameLoc,
       ConsumedWarningsHandlerBase &WarningsHandler) const;
     
+    /// \brief Clear the TmpMap.
+    void clearTemporaries();
+    
     /// \brief Get the consumed state of a given variable.
     ConsumedState getState(const VarDecl *Var) const;
     
+    /// \brief Get the consumed state of a given temporary value.
+    ConsumedState getState(const CXXBindTemporaryExpr *Tmp) const;
+    
     /// \brief Merge this state map with another map.
     void intersect(const ConsumedStateMap *Other);
     
@@ -173,6 +182,9 @@ namespace consumed {
     /// \brief Set the consumed state of a given variable.
     void setState(const VarDecl *Var, ConsumedState State);
     
+    /// \brief Set the consumed state of a given temporary value.
+    void setState(const CXXBindTemporaryExpr *Tmp, ConsumedState State);
+    
     /// \brief Remove the variable from our state map.
     void remove(const VarDecl *Var);
     

Modified: cfe/trunk/lib/Analysis/Consumed.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Analysis/Consumed.cpp?rev=194900&r1=194899&r2=194900&view=diff
==============================================================================
--- cfe/trunk/lib/Analysis/Consumed.cpp (original)
+++ cfe/trunk/lib/Analysis/Consumed.cpp Fri Nov 15 18:22:43 2013
@@ -32,7 +32,9 @@
 #include "llvm/Support/Compiler.h"
 #include "llvm/Support/raw_ostream.h"
 
-// TODO: Use information from tests in while-loop conditional.
+// TODO: Adjust states of args to constructors in the same way that arguments to
+//       function calls are handled.
+// TODO: Use information from tests in for- and while-loop conditional.
 // TODO: Add notes about the actual and expected state for 
 // TODO: Correctly identify unreachable blocks when chaining boolean operators.
 // TODO: Adjust the parser and AttributesList class to support lists of
@@ -280,9 +282,10 @@ class PropagationInfo {
   enum {
     IT_None,
     IT_State,
-    IT_Test,
+    IT_VarTest,
     IT_BinTest,
-    IT_Var
+    IT_Var,
+    IT_Tmp
   } InfoType;
 
   struct BinTestTy {
@@ -294,22 +297,23 @@ class PropagationInfo {
   
   union {
     ConsumedState State;
-    VarTestResult Test;
+    VarTestResult VarTest;
     const VarDecl *Var;
+    const CXXBindTemporaryExpr *Tmp;
     BinTestTy BinTest;
   };
   
-  QualType TempType;
-  
 public:
   PropagationInfo() : InfoType(IT_None) {}
   
-  PropagationInfo(const VarTestResult &Test) : InfoType(IT_Test), Test(Test) {}
+  PropagationInfo(const VarTestResult &VarTest)
+    : InfoType(IT_VarTest), VarTest(VarTest) {}
+  
   PropagationInfo(const VarDecl *Var, ConsumedState TestsFor)
-    : InfoType(IT_Test) {
+    : InfoType(IT_VarTest) {
     
-    Test.Var      = Var;
-    Test.TestsFor = TestsFor;
+    VarTest.Var      = Var;
+    VarTest.TestsFor = TestsFor;
   }
   
   PropagationInfo(const BinaryOperator *Source, EffectiveOp EOp,
@@ -335,24 +339,21 @@ public:
     BinTest.RTest.TestsFor = RTestsFor;
   }
   
-  PropagationInfo(ConsumedState State, QualType TempType)
-    : InfoType(IT_State), State(State), TempType(TempType) {}
+  PropagationInfo(ConsumedState State)
+    : InfoType(IT_State), State(State) {}
   
   PropagationInfo(const VarDecl *Var) : InfoType(IT_Var), Var(Var) {}
+  PropagationInfo(const CXXBindTemporaryExpr *Tmp)
+    : InfoType(IT_Tmp), Tmp(Tmp) {}
   
   const ConsumedState & getState() const {
     assert(InfoType == IT_State);
     return State;
   }
   
-  const QualType & getTempType() const {
-    assert(InfoType == IT_State);
-    return TempType;
-  }
-  
-  const VarTestResult & getTest() const {
-    assert(InfoType == IT_Test);
-    return Test;
+  const VarTestResult & getVarTest() const {
+    assert(InfoType == IT_VarTest);
+    return VarTest;
   }
   
   const VarTestResult & getLTest() const {
@@ -370,6 +371,24 @@ public:
     return Var;
   }
   
+  const CXXBindTemporaryExpr * getTmp() const {
+    assert(InfoType == IT_Tmp);
+    return Tmp;
+  }
+  
+  ConsumedState getAsState(const ConsumedStateMap *StateMap) const {
+    assert(isVar() || isTmp() || isState());
+    
+    if (isVar())
+      return StateMap->getState(Var);
+    else if (isTmp())
+      return StateMap->getState(Tmp);
+    else if (isState())
+      return State;
+    else
+      return CS_None;
+  }
+  
   EffectiveOp testEffectiveOp() const {
     assert(InfoType == IT_BinTest);
     return BinTest.EOp;
@@ -380,17 +399,27 @@ public:
     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;      }
+  inline bool isValid()   const { return InfoType != IT_None;    }
+  inline bool isState()   const { return InfoType == IT_State;   }
+  inline bool isVarTest() const { return InfoType == IT_VarTest; }
+  inline bool isBinTest() const { return InfoType == IT_BinTest; }
+  inline bool isVar()     const { return InfoType == IT_Var;     }
+  inline bool isTmp()     const { return InfoType == IT_Tmp;     }
+  
+  bool isTest() const {
+    return InfoType == IT_VarTest || InfoType == IT_BinTest;
+  }
+  
+  bool isPointerToValue() const {
+    return InfoType == IT_Var || InfoType == IT_Tmp;
+  }
   
   PropagationInfo invertTest() const {
-    assert(InfoType == IT_Test || InfoType == IT_BinTest);
+    assert(InfoType == IT_VarTest || InfoType == IT_BinTest);
     
-    if (InfoType == IT_Test) {
-      return PropagationInfo(Test.Var, invertConsumedUnconsumed(Test.TestsFor));
+    if (InfoType == IT_VarTest) {
+      return PropagationInfo(VarTest.Var,
+                             invertConsumedUnconsumed(VarTest.TestsFor));
     
     } else if (InfoType == IT_BinTest) {
       return PropagationInfo(BinTest.Source,
@@ -403,6 +432,18 @@ public:
   }
 };
 
+static inline void
+setStateForVarOrTmp(ConsumedStateMap *StateMap, const PropagationInfo &PInfo,
+                    ConsumedState State) {
+
+  assert(PInfo.isVar() || PInfo.isTmp());
+  
+  if (PInfo.isVar())
+    StateMap->setState(PInfo.getVar(), State);
+  else
+    StateMap->setState(PInfo.getTmp(), State);
+}
+
 class ConsumedStmtVisitor : public ConstStmtVisitor<ConsumedStmtVisitor> {
   
   typedef llvm::DenseMap<const Stmt *, PropagationInfo> MapType;
@@ -418,22 +459,11 @@ class ConsumedStmtVisitor : public Const
   bool isLikeMoveAssignment(const CXXMethodDecl *MethodDecl);
   void propagateReturnType(const Stmt *Call, const FunctionDecl *Fun,
                            QualType ReturnType);
-  
-  inline ConsumedState getPInfoState(const PropagationInfo& PInfo) {
-    if (PInfo.isVar())
-      return StateMap->getState(PInfo.getVar());
-    else if (PInfo.isState())
-      return PInfo.getState();
-    else
-      return CS_None;
-  }
 
 public:
   void checkCallability(const PropagationInfo &PInfo,
                         const FunctionDecl *FunDecl,
                         SourceLocation BlameLoc);
-
-  void Visit(const Stmt *StmtNode);
   
   void VisitBinaryOperator(const BinaryOperator *BinOp);
   void VisitCallExpr(const CallExpr *Call);
@@ -472,6 +502,7 @@ public:
 void ConsumedStmtVisitor::checkCallability(const PropagationInfo &PInfo,
                                            const FunctionDecl *FunDecl,
                                            SourceLocation BlameLoc) {
+  assert(!PInfo.isTest());
   
   if (!FunDecl->hasAttr<CallableWhenAttr>())
     return;
@@ -479,27 +510,23 @@ void ConsumedStmtVisitor::checkCallabili
   const CallableWhenAttr *CWAttr = FunDecl->getAttr<CallableWhenAttr>();
   
   if (PInfo.isVar()) {
-    const VarDecl *Var = PInfo.getVar();
-    ConsumedState VarState = StateMap->getState(Var);
+    ConsumedState VarState = StateMap->getState(PInfo.getVar());
     
-    assert(VarState != CS_None && "Invalid state");
-    
-    if (isCallableInState(CWAttr, VarState))
+    if (VarState == CS_None || isCallableInState(CWAttr, VarState))
       return;
     
     Analyzer.WarningsHandler.warnUseInInvalidState(
-      FunDecl->getNameAsString(), Var->getNameAsString(),
+      FunDecl->getNameAsString(), PInfo.getVar()->getNameAsString(),
       stateToString(VarState), BlameLoc);
     
-  } else if (PInfo.isState()) {
-    
-    assert(PInfo.getState() != CS_None && "Invalid state");
+  } else {
+    ConsumedState TmpState = PInfo.getAsState(StateMap);
     
-    if (isCallableInState(CWAttr, PInfo.getState()))
+    if (TmpState == CS_None || isCallableInState(CWAttr, TmpState))
       return;
     
     Analyzer.WarningsHandler.warnUseOfTempInInvalidState(
-      FunDecl->getNameAsString(), stateToString(PInfo.getState()), BlameLoc);
+      FunDecl->getNameAsString(), stateToString(TmpState), BlameLoc);
   }
 }
 
@@ -532,19 +559,7 @@ void ConsumedStmtVisitor::propagateRetur
     else
       ReturnState = mapConsumableAttrState(ReturnType);
     
-    PropagationMap.insert(PairType(Call,
-      PropagationInfo(ReturnState, ReturnType)));
-  }
-}
-
-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);
+    PropagationMap.insert(PairType(Call, PropagationInfo(ReturnState)));
   }
 }
 
@@ -557,16 +572,16 @@ void ConsumedStmtVisitor::VisitBinaryOpe
     
     VarTestResult LTest, RTest;
     
-    if (LEntry != PropagationMap.end() && LEntry->second.isTest()) {
-      LTest = LEntry->second.getTest();
+    if (LEntry != PropagationMap.end() && LEntry->second.isVarTest()) {
+      LTest = LEntry->second.getVarTest();
       
     } else {
       LTest.Var      = NULL;
       LTest.TestsFor = CS_None;
     }
     
-    if (REntry != PropagationMap.end() && REntry->second.isTest()) {
-      RTest = REntry->second.getTest();
+    if (REntry != PropagationMap.end() && REntry->second.isVarTest()) {
+      RTest = REntry->second.getVarTest();
       
     } else {
       RTest.Var      = NULL;
@@ -609,8 +624,7 @@ void ConsumedStmtVisitor::VisitCallExpr(
       
       InfoEntry Entry = PropagationMap.find(Call->getArg(Index));
       
-      if (Entry == PropagationMap.end() ||
-          !(Entry->second.isState() || Entry->second.isVar()))
+      if (Entry == PropagationMap.end() || Entry->second.isTest())
         continue;
       
       PropagationInfo PInfo = Entry->second;
@@ -618,9 +632,7 @@ void ConsumedStmtVisitor::VisitCallExpr(
       // Check that the parameter is in the correct state.
       
       if (Param->hasAttr<ParamTypestateAttr>()) {
-        ConsumedState ParamState =
-          PInfo.isState() ? PInfo.getState() :
-                            StateMap->getState(PInfo.getVar());
+        ConsumedState ParamState = PInfo.getAsState(StateMap);
         
         ConsumedState ExpectedState =
           mapParamTypestateAttrState(Param->getAttr<ParamTypestateAttr>());
@@ -631,22 +643,22 @@ void ConsumedStmtVisitor::VisitCallExpr(
             stateToString(ExpectedState), stateToString(ParamState));
       }
       
-      if (!Entry->second.isVar())
+      if (!(Entry->second.isVar() || Entry->second.isTmp()))
         continue;
       
       // Adjust state on the caller side.
       
       if (isRValueRefish(ParamType)) {
-        StateMap->setState(PInfo.getVar(), consumed::CS_Consumed);
+        setStateForVarOrTmp(StateMap, PInfo, consumed::CS_Consumed);
         
       } else if (Param->hasAttr<ReturnTypestateAttr>()) {
-        StateMap->setState(PInfo.getVar(),
+        setStateForVarOrTmp(StateMap, PInfo,
           mapReturnTypestateAttrState(Param->getAttr<ReturnTypestateAttr>()));
         
       } else if (!isValueType(ParamType) &&
                  !ParamType->getPointeeType().isConstQualified()) {
         
-        StateMap->setState(PInfo.getVar(), consumed::CS_Unknown);
+        setStateForVarOrTmp(StateMap, PInfo, consumed::CS_Unknown);
       }
     }
     
@@ -665,7 +677,12 @@ void ConsumedStmtVisitor::VisitCastExpr(
 void ConsumedStmtVisitor::VisitCXXBindTemporaryExpr(
   const CXXBindTemporaryExpr *Temp) {
   
-  forwardInfo(Temp->getSubExpr(), Temp);
+  InfoEntry Entry = PropagationMap.find(Temp->getSubExpr());
+  
+  if (Entry != PropagationMap.end() && !Entry->second.isTest()) {
+    StateMap->setState(Temp, Entry->second.getAsState(StateMap));
+    PropagationMap.insert(PairType(Temp, PropagationInfo(Temp)));
+  }
 }
 
 void ConsumedStmtVisitor::VisitCXXConstructExpr(const CXXConstructExpr *Call) {
@@ -679,14 +696,16 @@ void ConsumedStmtVisitor::VisitCXXConstr
   
   // FIXME: What should happen if someone annotates the move constructor?
   if (Constructor->hasAttr<ReturnTypestateAttr>()) {
+    // TODO: Adjust state of args appropriately.
+    
     ReturnTypestateAttr *RTAttr = Constructor->getAttr<ReturnTypestateAttr>();
     ConsumedState RetState = mapReturnTypestateAttrState(RTAttr);
-    PropagationMap.insert(PairType(Call, PropagationInfo(RetState, ThisType)));
+    PropagationMap.insert(PairType(Call, PropagationInfo(RetState)));
     
   } else if (Constructor->isDefaultConstructor()) {
     
     PropagationMap.insert(PairType(Call,
-      PropagationInfo(consumed::CS_Consumed, ThisType)));
+      PropagationInfo(consumed::CS_Consumed)));
     
   } else if (Constructor->isMoveConstructor()) {
     
@@ -699,10 +718,18 @@ void ConsumedStmtVisitor::VisitCXXConstr
         const VarDecl* Var = PInfo.getVar();
         
         PropagationMap.insert(PairType(Call,
-          PropagationInfo(StateMap->getState(Var), ThisType)));
+          PropagationInfo(StateMap->getState(Var))));
         
         StateMap->setState(Var, consumed::CS_Consumed);
         
+      } else if (PInfo.isTmp()) {
+        const CXXBindTemporaryExpr *Tmp = PInfo.getTmp();
+        
+        PropagationMap.insert(PairType(Call,
+          PropagationInfo(StateMap->getState(Tmp))));
+        
+        StateMap->setState(Tmp, consumed::CS_Consumed);
+        
       } else {
         PropagationMap.insert(PairType(Call, PInfo));
       }
@@ -711,8 +738,10 @@ void ConsumedStmtVisitor::VisitCXXConstr
     forwardInfo(Call->getArg(0), Call);
     
   } else {
+    // TODO: Adjust state of args appropriately.
+    
     ConsumedState RetState = mapConsumableAttrState(ThisType);
-    PropagationMap.insert(PairType(Call, PropagationInfo(RetState, ThisType)));
+    PropagationMap.insert(PairType(Call, PropagationInfo(RetState)));
   }
 }
 
@@ -736,6 +765,9 @@ void ConsumedStmtVisitor::VisitCXXMember
       else if (MethodDecl->hasAttr<SetTypestateAttr>())
         StateMap->setState(PInfo.getVar(),
           mapSetTypestateAttrState(MethodDecl->getAttr<SetTypestateAttr>()));
+    } else if (PInfo.isTmp() && MethodDecl->hasAttr<SetTypestateAttr>()) {
+      StateMap->setState(PInfo.getTmp(),
+        mapSetTypestateAttrState(MethodDecl->getAttr<SetTypestateAttr>()));
     }
   }
 }
@@ -762,28 +794,17 @@ void ConsumedStmtVisitor::VisitCXXOperat
       LPInfo = LEntry->second;
       RPInfo = REntry->second;
       
-      if (LPInfo.isVar() && RPInfo.isVar()) {
-        StateMap->setState(LPInfo.getVar(),
-          StateMap->getState(RPInfo.getVar()));
-        
-        StateMap->setState(RPInfo.getVar(), consumed::CS_Consumed);
-        
+      if (LPInfo.isPointerToValue() && RPInfo.isPointerToValue()) {
+        setStateForVarOrTmp(StateMap, LPInfo, RPInfo.getAsState(StateMap));
         PropagationMap.insert(PairType(Call, LPInfo));
+        setStateForVarOrTmp(StateMap, RPInfo, consumed::CS_Consumed);
         
-      } else if (LPInfo.isVar() && !RPInfo.isVar()) {
-        StateMap->setState(LPInfo.getVar(), RPInfo.getState());
-        
+      } else if (RPInfo.isState()) {
+        setStateForVarOrTmp(StateMap, LPInfo, RPInfo.getState());
         PropagationMap.insert(PairType(Call, LPInfo));
         
-      } else if (!LPInfo.isVar() && RPInfo.isVar()) {
-        PropagationMap.insert(PairType(Call,
-          PropagationInfo(StateMap->getState(RPInfo.getVar()),
-                          LPInfo.getTempType())));
-        
-        StateMap->setState(RPInfo.getVar(), consumed::CS_Consumed);
-        
       } else {
-        PropagationMap.insert(PairType(Call, RPInfo));
+        setStateForVarOrTmp(StateMap, RPInfo, consumed::CS_Consumed);
       }
       
     } else if (LEntry != PropagationMap.end() &&
@@ -791,21 +812,24 @@ void ConsumedStmtVisitor::VisitCXXOperat
       
       LPInfo = LEntry->second;
       
-      if (LPInfo.isVar()) {
-        StateMap->setState(LPInfo.getVar(), consumed::CS_Unknown);
-        
+      assert(!LPInfo.isTest());
+      
+      if (LPInfo.isPointerToValue()) {
+        setStateForVarOrTmp(StateMap, LPInfo, consumed::CS_Unknown);
         PropagationMap.insert(PairType(Call, LPInfo));
         
-      } else if (LPInfo.isState()) {
+      } else {
         PropagationMap.insert(PairType(Call,
-          PropagationInfo(consumed::CS_Unknown, LPInfo.getTempType())));
+          PropagationInfo(consumed::CS_Unknown)));
       }
       
     } else if (LEntry == PropagationMap.end() &&
                REntry != PropagationMap.end()) {
       
-      if (REntry->second.isVar())
-        StateMap->setState(REntry->second.getVar(), consumed::CS_Consumed);
+      RPInfo = REntry->second;
+      
+      if (RPInfo.isPointerToValue())
+        setStateForVarOrTmp(StateMap, RPInfo, consumed::CS_Consumed);
     }
     
   } else {
@@ -826,7 +850,11 @@ void ConsumedStmtVisitor::VisitCXXOperat
         else if (FunDecl->hasAttr<SetTypestateAttr>())
           StateMap->setState(PInfo.getVar(),
             mapSetTypestateAttrState(FunDecl->getAttr<SetTypestateAttr>()));
-      }
+        
+      } else if (PInfo.isTmp() && FunDecl->hasAttr<SetTypestateAttr>()) {
+        StateMap->setState(PInfo.getTmp(),
+          mapSetTypestateAttrState(FunDecl->getAttr<SetTypestateAttr>()));
+    }
     }
   }
 }
@@ -892,10 +920,7 @@ void ConsumedStmtVisitor::VisitReturnStm
     InfoEntry Entry = PropagationMap.find(Ret->getRetValue());
     
     if (Entry != PropagationMap.end()) {
-      assert(Entry->second.isState() || Entry->second.isVar());
-       
-      ConsumedState RetState = Entry->second.isState() ?
-        Entry->second.getState() : StateMap->getState(Entry->second.getVar());
+      ConsumedState RetState = Entry->second.getAsState(StateMap);
         
       if (RetState != ExpectedState)
         Analyzer.WarningsHandler.warnReturnTypestateMismatch(
@@ -918,7 +943,7 @@ void ConsumedStmtVisitor::VisitUnaryOper
     break;
   
   case UO_LNot:
-    if (Entry->second.isTest() || Entry->second.isBinTest())
+    if (Entry->second.isTest())
       PropagationMap.insert(PairType(UOp, Entry->second.invertTest()));
     break;
   
@@ -931,10 +956,12 @@ void ConsumedStmtVisitor::VisitUnaryOper
 void ConsumedStmtVisitor::VisitVarDecl(const VarDecl *Var) {
   if (isConsumableType(Var->getType())) {
     if (Var->hasInit()) {
-      MapType::iterator VIT = PropagationMap.find(Var->getInit());
+      MapType::iterator VIT = PropagationMap.find(
+        Var->getInit()->IgnoreImplicit());
       if (VIT != PropagationMap.end()) {
         PropagationInfo PInfo = VIT->second;
-        ConsumedState St = getPInfoState(PInfo);
+        ConsumedState St = PInfo.getAsState(StateMap);
+        
         if (St != consumed::CS_None) {
           StateMap->setState(Var, St);
           return;
@@ -1133,8 +1160,8 @@ void ConsumedStateMap::checkParamsForRet
   
   ConsumedState ExpectedState;
   
-  for (MapType::const_iterator DMI = Map.begin(), DME = Map.end(); DMI != DME;
-       ++DMI) {
+  for (VarMapType::const_iterator DMI = VarMap.begin(), DME = VarMap.end();
+       DMI != DME; ++DMI) {
     
     if (isa<ParmVarDecl>(DMI->first)) {
       const ParmVarDecl *Param = cast<ParmVarDecl>(DMI->first);
@@ -1153,15 +1180,27 @@ void ConsumedStateMap::checkParamsForRet
   }
 }
 
+void ConsumedStateMap::clearTemporaries() {
+  TmpMap.clear();
+}
+
 ConsumedState ConsumedStateMap::getState(const VarDecl *Var) const {
-  MapType::const_iterator Entry = Map.find(Var);
+  VarMapType::const_iterator Entry = VarMap.find(Var);
   
-  if (Entry != Map.end()) {
+  if (Entry != VarMap.end())
     return Entry->second;
     
-  } else {
-    return CS_None;
-  }
+  return CS_None;
+}
+
+ConsumedState
+ConsumedStateMap::getState(const CXXBindTemporaryExpr *Tmp) const {
+  TmpMapType::const_iterator Entry = TmpMap.find(Tmp);
+  
+  if (Entry != TmpMap.end())
+    return Entry->second;
+  
+  return CS_None;
 }
 
 void ConsumedStateMap::intersect(const ConsumedStateMap *Other) {
@@ -1172,8 +1211,8 @@ void ConsumedStateMap::intersect(const C
     return;
   }
   
-  for (MapType::const_iterator DMI = Other->Map.begin(), DME = Other->Map.end();
-       DMI != DME; ++DMI) {
+  for (VarMapType::const_iterator DMI = Other->VarMap.begin(),
+       DME = Other->VarMap.end(); DMI != DME; ++DMI) {
     
     LocalState = this->getState(DMI->first);
     
@@ -1181,7 +1220,7 @@ void ConsumedStateMap::intersect(const C
       continue;
     
     if (LocalState != DMI->second)
-       Map[DMI->first] = CS_Unknown;
+       VarMap[DMI->first] = CS_Unknown;
   }
 }
 
@@ -1192,8 +1231,8 @@ void ConsumedStateMap::intersectAtLoopHe
   ConsumedState LocalState;
   SourceLocation BlameLoc = getLastStmtLoc(LoopBack);
   
-  for (MapType::const_iterator DMI = LoopBackStates->Map.begin(),
-       DME = LoopBackStates->Map.end(); DMI != DME; ++DMI) {
+  for (VarMapType::const_iterator DMI = LoopBackStates->VarMap.begin(),
+       DME = LoopBackStates->VarMap.end(); DMI != DME; ++DMI) {
     
     LocalState = this->getState(DMI->first);
     
@@ -1201,7 +1240,7 @@ void ConsumedStateMap::intersectAtLoopHe
       continue;
     
     if (LocalState != DMI->second) {
-      Map[DMI->first] = CS_Unknown;
+      VarMap[DMI->first] = CS_Unknown;
       WarningsHandler.warnLoopStateMismatch(
         BlameLoc, DMI->first->getNameAsString());
     }
@@ -1210,20 +1249,26 @@ void ConsumedStateMap::intersectAtLoopHe
 
 void ConsumedStateMap::markUnreachable() {
   this->Reachable = false;
-  Map.clear();
+  VarMap.clear();
+  TmpMap.clear();
 }
 
 void ConsumedStateMap::setState(const VarDecl *Var, ConsumedState State) {
-  Map[Var] = State;
+  VarMap[Var] = State;
+}
+
+void ConsumedStateMap::setState(const CXXBindTemporaryExpr *Tmp,
+                                ConsumedState State) {
+  TmpMap[Tmp] = State;
 }
 
 void ConsumedStateMap::remove(const VarDecl *Var) {
-  Map.erase(Var);
+  VarMap.erase(Var);
 }
 
 bool ConsumedStateMap::operator!=(const ConsumedStateMap *Other) const {
-  for (MapType::const_iterator DMI = Other->Map.begin(), DME = Other->Map.end();
-       DMI != DME; ++DMI) {
+  for (VarMapType::const_iterator DMI = Other->VarMap.begin(),
+       DME = Other->VarMap.end(); DMI != DME; ++DMI) {
     
     if (this->getState(DMI->first) != DMI->second)
       return true;
@@ -1276,10 +1321,10 @@ bool ConsumedAnalyzer::splitState(const
     if (!PInfo.isValid() && isa<BinaryOperator>(Cond))
       PInfo = Visitor.getInfo(cast<BinaryOperator>(Cond)->getRHS());
     
-    if (PInfo.isTest()) {
+    if (PInfo.isVarTest()) {
       CurrStates->setSource(Cond);
       FalseStates->setSource(Cond);
-      splitVarStateForIf(IfNode, PInfo.getTest(), CurrStates,
+      splitVarStateForIf(IfNode, PInfo.getVarTest(), CurrStates,
                          FalseStates.get());
       
     } else if (PInfo.isBinTest()) {
@@ -1295,11 +1340,11 @@ bool ConsumedAnalyzer::splitState(const
     dyn_cast_or_null<BinaryOperator>(CurrBlock->getTerminator().getStmt())) {
     
     PInfo = Visitor.getInfo(BinOp->getLHS());
-    if (!PInfo.isTest()) {
+    if (!PInfo.isVarTest()) {
       if ((BinOp = dyn_cast_or_null<BinaryOperator>(BinOp->getLHS()))) {
         PInfo = Visitor.getInfo(BinOp->getRHS());
         
-        if (!PInfo.isTest())
+        if (!PInfo.isVarTest())
           return false;
         
       } else {
@@ -1310,7 +1355,7 @@ bool ConsumedAnalyzer::splitState(const
     CurrStates->setSource(BinOp);
     FalseStates->setSource(BinOp);
     
-    const VarTestResult &Test = PInfo.getTest();
+    const VarTestResult &Test = PInfo.getVarTest();
     ConsumedState VarState = CurrStates->getState(Test.Var);
     
     if (BinOp->getOpcode() == BO_LAnd) {
@@ -1402,30 +1447,21 @@ void ConsumedAnalyzer::run(AnalysisDeclC
       case CFGElement::TemporaryDtor: {
         const CFGTemporaryDtor DTor = BI->castAs<CFGTemporaryDtor>();
         const CXXBindTemporaryExpr *BTE = DTor.getBindTemporaryExpr();
-        PropagationInfo PInfo = Visitor.getInfo(BTE);
         
-        if (PInfo.isValid())
-          Visitor.checkCallability(PInfo,
-                                   DTor.getDestructorDecl(AC.getASTContext()),
-                                   BTE->getExprLoc());
+        Visitor.checkCallability(PropagationInfo(BTE),
+                                 DTor.getDestructorDecl(AC.getASTContext()),
+                                 BTE->getExprLoc());
         break;
       }
       
       case CFGElement::AutomaticObjectDtor: {
         const CFGAutomaticObjDtor DTor = BI->castAs<CFGAutomaticObjDtor>();
-        
+        SourceLocation Loc = DTor.getTriggerStmt()->getLocEnd();
         const VarDecl *Var = DTor.getVarDecl();
-        ConsumedState VarState = CurrStates->getState(Var);
         
-        if (VarState != CS_None) {
-          PropagationInfo PInfo(Var);
-          
-          Visitor.checkCallability(PInfo,
-                                   DTor.getDestructorDecl(AC.getASTContext()),
-                                   getLastStmtLoc(CurrBlock));
-          
-          CurrStates->remove(Var);
-        }
+        Visitor.checkCallability(PropagationInfo(Var),
+                                 DTor.getDestructorDecl(AC.getASTContext()),
+                                 Loc);
         break;
       }
       
@@ -1434,6 +1470,8 @@ void ConsumedAnalyzer::run(AnalysisDeclC
       }
     }
     
+    CurrStates->clearTemporaries();
+    
     // TODO: Handle other forms of branching with precision, including while-
     //       and for-loops. (Deferred)
     if (!splitState(CurrBlock, Visitor)) {

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=194900&r1=194899&r2=194900&view=diff
==============================================================================
--- cfe/trunk/test/SemaCXX/warn-consumed-analysis.cpp (original)
+++ cfe/trunk/test/SemaCXX/warn-consumed-analysis.cpp Fri Nov 15 18:22:43 2013
@@ -655,10 +655,10 @@ public:
   Status(int c) RETURN_TYPESTATE(unconsumed);
 
   Status(const Status &other);
-  //Status(Status &&other);
+  Status(Status &&other);
 
   Status& operator=(const Status &other) CALLABLE_WHEN("unknown", "consumed");
-  //Status& operator=(Status &&other) CALLABLE_WHEN("unknown", "consumed");
+  Status& operator=(Status &&other) CALLABLE_WHEN("unknown", "consumed");
 
   bool check()  const SET_TYPESTATE(consumed);
   void ignore() const SET_TYPESTATE(consumed);
@@ -672,18 +672,124 @@ public:
 
 bool   cond();
 Status doSomething();
-void   handleStatus(const Status& s);
+void   handleStatus(const Status& s RETURN_TYPESTATE(consumed));
 void   handleStatusPtr(const Status* s);
 
-int a;
+void testSimpleTemporaries0() {
+  doSomething(); // expected-warning {{invalid invocation of method '~Status' on a temporary object while it is in the 'unconsumed' state}}
+}
+
+void testSimpleTemporaries1() {
+  doSomething().ignore();
+}
+
+void testSimpleTemporaries2() {
+  handleStatus(doSomething());
+}
+
+void testSimpleTemporaries3() {
+  Status s = doSomething();
+}  // expected-warning {{invalid invocation of method '~Status' on object 's' while it is in the 'unconsumed' state}}
+
+void testSimpleTemporaries4() {
+  Status s = doSomething();
+  s.check();
+}
+
+void testSimpleTemporaries5() {
+  Status s = doSomething();
+  s.clear(); // expected-warning {{invalid invocation of method 'clear' on object 's' while it is in the 'unconsumed' state}}
+}
+
+void testSimpleTemporaries6() {
+  Status s = doSomething();
+  handleStatus(s);
+}
 
+void testSimpleTemporaries7() {
+  Status s;
+  s = doSomething();
+}  // expected-warning {{invalid invocation of method '~Status' on object 's' while it is in the 'unconsumed' state}}
+
+void testTemporariesWithConditionals0() {
+  int a;
+
+  Status s = doSomething();
+  if (cond()) a = 0;
+  else        a = 1;
+} // expected-warning {{invalid invocation of method '~Status' on object 's' while it is in the 'unconsumed' state}}
+
+void testTemporariesWithConditionals1() {
+  int a;
+  
+  Status s = doSomething();
+  if (cond()) a = 0;
+  else        a = 1;
+  s.ignore();
+}
+
+void testTemporariesWithConditionals2() {
+  int a;
+  
+  Status s = doSomething();
+  s.ignore();
+  if (cond()) a = 0;
+  else        a = 1;
+}
 
-void test() {
+void testTemporariesWithConditionals3() {
+  Status s = doSomething();
   if (cond()) {
-    Status s = doSomething();
-    return;                     // Warning: Store it, but don't check.
+    s.check();
   }
 }
 
+void testTemporariesAndConstructors0() {
+  Status s(doSomething());
+  s.check();
+}
+
+void testTemporariesAndConstructors1() {
+  // Test the copy constructor.
+  
+  Status s1 = doSomething();
+  Status s2(s1);
+  s2.check();
+}  // expected-warning {{invalid invocation of method '~Status' on object 's1' while it is in the 'unconsumed' state}}
+
+void testTemporariesAndConstructors2() {
+  // Test the move constructor.
+  
+  Status s1 = doSomething();
+  Status s2(static_cast<Status&&>(s1));
+  s2.check();
+}
+
+void testTemporariesAndOperators0() {
+  // Test the assignment operator.
+  
+  Status s1 = doSomething();
+  Status s2;
+  s2 = s1;
+  s2.check();
+} // expected-warning {{invalid invocation of method '~Status' on object 's1' while it is in the 'unconsumed' state}}
+
+void testTemporariesAndOperators1() {
+  // Test the move assignment operator.
+  
+  Status s1 = doSomething();
+  Status s2;
+  s2 = static_cast<Status&&>(s1);
+  s2.check();
+}
+
+void testTemporariesAndOperators2() {
+  Status s1 = doSomething();
+  Status s2 = doSomething();
+  s1 = s2; // expected-warning {{invalid invocation of method 'operator=' on object 's1' while it is in the 'unconsumed' state}}
+  s1.check();
+  s2.check();
+}
+
 } // end namespace InitializerAssertionFailTest
 





More information about the cfe-commits mailing list