r191983 - Consumed Analysis: Change callable_when so that it can take a list of states

DeLesley Hutchins delesley at google.com
Fri Oct 4 14:28:06 PDT 2013


Author: delesley
Date: Fri Oct  4 16:28:06 2013
New Revision: 191983

URL: http://llvm.org/viewvc/llvm-project?rev=191983&view=rev
Log:
Consumed Analysis:  Change callable_when so that it can take a list of states
that a function can be called in.  This reduced the total number of annotations
needed and makes writing more complicated behaviour less burdensome.
Patch by chriswails at gmail.com.

Modified:
    cfe/trunk/include/clang/Analysis/Analyses/Consumed.h
    cfe/trunk/include/clang/Basic/Attr.td
    cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td
    cfe/trunk/lib/Analysis/Consumed.cpp
    cfe/trunk/lib/Sema/AnalysisBasedWarnings.cpp
    cfe/trunk/lib/Sema/SemaDeclAttr.cpp
    cfe/trunk/test/SemaCXX/warn-consumed-analysis-strict.cpp
    cfe/trunk/test/SemaCXX/warn-consumed-analysis.cpp
    cfe/trunk/test/SemaCXX/warn-consumed-parsing.cpp
    cfe/trunk/utils/TableGen/ClangAttrEmitter.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=191983&r1=191982&r2=191983&view=diff
==============================================================================
--- cfe/trunk/include/clang/Analysis/Analyses/Consumed.h (original)
+++ cfe/trunk/include/clang/Analysis/Analyses/Consumed.h Fri Oct  4 16:28:06 2013
@@ -25,6 +25,15 @@
 namespace clang {
 namespace consumed {
   
+  enum ConsumedState {
+    // No state information for the given variable.
+    CS_None,
+    
+    CS_Unknown,
+    CS_Unconsumed,
+    CS_Consumed
+  };
+  
   class ConsumedStmtVisitor;
   
   typedef SmallVector<PartialDiagnosticAt, 1> OptionalNotes;
@@ -71,52 +80,29 @@ namespace consumed {
     /// \param MethodName -- The name of the method that was incorrectly
     /// invoked.
     ///
-    /// \param Loc -- The SourceLocation of the method invocation.
-    virtual void warnUseOfTempWhileConsumed(StringRef MethodName,
-                                            SourceLocation Loc) {}
-
-    /// \brief Warn about use-in-unknown-state errors.
-    /// \param MethodName -- The name of the method that was incorrectly
-    /// invoked.
+    /// \param State -- The state the object was used in.
     ///
     /// \param Loc -- The SourceLocation of the method invocation.
-    virtual void warnUseOfTempInUnknownState(StringRef MethodName,
+    virtual void warnUseOfTempInInvalidState(StringRef MethodName,
+                                             StringRef State,
                                              SourceLocation Loc) {}
 
     /// \brief Warn about use-while-consumed errors.
     /// \param MethodName -- The name of the method that was incorrectly
     /// invoked.
     ///
-    /// \param VariableName -- The name of the variable that holds the unique
-    /// value.
-    ///
-    /// \param Loc -- The SourceLocation of the method invocation.
-    virtual void warnUseWhileConsumed(StringRef MethodName,
-                                      StringRef VariableName,
-                                      SourceLocation Loc) {}
-
-    /// \brief Warn about use-in-unknown-state errors.
-    /// \param MethodName -- The name of the method that was incorrectly
-    /// invoked.
+    /// \param State -- The state the object was used in.
     ///
     /// \param VariableName -- The name of the variable that holds the unique
     /// value.
     ///
     /// \param Loc -- The SourceLocation of the method invocation.
-    virtual void warnUseInUnknownState(StringRef MethodName,
+    virtual void warnUseInInvalidState(StringRef MethodName,
                                        StringRef VariableName,
+                                       StringRef State,
                                        SourceLocation Loc) {}
   };
 
-  enum ConsumedState {
-    // No state information for the given variable.
-    CS_None,
-    
-    CS_Unknown,
-    CS_Unconsumed,
-    CS_Consumed
-  };
-
   class ConsumedStateMap {
     
     typedef llvm::DenseMap<const VarDecl *, ConsumedState> MapType;

Modified: cfe/trunk/include/clang/Basic/Attr.td
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Basic/Attr.td?rev=191983&r1=191982&r2=191983&view=diff
==============================================================================
--- cfe/trunk/include/clang/Basic/Attr.td (original)
+++ cfe/trunk/include/clang/Basic/Attr.td Fri Oct  4 16:28:06 2013
@@ -81,6 +81,15 @@ class EnumArgument<string name, string t
   list<string> Enums = enums;
 }
 
+// FIXME: There should be a VariadicArgument type that takes any other type
+//        of argument and generates the appropriate type.
+class VariadicEnumArgument<string name, string type, list<string> values,
+                           list<string> enums> : Argument<name, 1>  {
+  string Type = type;
+  list<string> Values = values;
+  list<string> Enums = enums;
+}
+
 // This handles one spelling of an attribute.
 class Spelling<string name, string variety> {
   string Name = name;
@@ -950,9 +959,12 @@ def Consumable : InheritableAttr {
                            ["Unknown", "Consumed", "Unconsumed"]>];
 }
 
-def CallableWhenUnconsumed : InheritableAttr {
-  let Spellings = [GNU<"callable_when_unconsumed">];
+def CallableWhen : InheritableAttr {
+  let Spellings = [GNU<"callable_when">];
   let Subjects = [CXXMethod];
+  let Args = [VariadicEnumArgument<"CallableState", "ConsumedState",
+                                   ["unknown", "consumed", "unconsumed"],
+                                   ["Unknown", "Consumed", "Unconsumed"]>];
 }
 
 def TestsUnconsumed : InheritableAttr {

Modified: cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td?rev=191983&r1=191982&r2=191983&view=diff
==============================================================================
--- cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td (original)
+++ cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td Fri Oct  4 16:28:06 2013
@@ -2195,12 +2195,12 @@ def warn_thread_safety_beta : Warning<
   "Thread safety beta warning.">, InGroup<ThreadSafetyBeta>, DefaultIgnore;
 
 // Consumed warnings
-def warn_use_while_consumed : Warning<
-  "invocation of method '%0' on object '%1' while it is in the 'consumed' "
+def warn_use_in_invalid_state : Warning<
+  "invalid invocation of method '%0' on object '%1' while it is in the '%2' "
   "state">, InGroup<Consumed>, DefaultIgnore;
-def warn_use_of_temp_while_consumed : Warning<
-  "invocation of method '%0' on a temporary object while it is in the "
-  "'consumed' state">, InGroup<Consumed>, DefaultIgnore;
+def warn_use_of_temp_in_invalid_state : Warning<
+  "invalid invocation of method '%0' on a temporary object while it is in the "
+  "'%1' state">, InGroup<Consumed>, DefaultIgnore;
 def warn_attr_on_unconsumable_class : Warning<
   "consumed analysis attribute is attached to member of class '%0' which isn't "
   "marked as consumable">, InGroup<Consumed>, DefaultIgnore;
@@ -2212,12 +2212,6 @@ def warn_return_typestate_mismatch : War
   InGroup<Consumed>, DefaultIgnore;
 
 // ConsumedStrict warnings
-def warn_use_in_unknown_state : Warning<
-  "invocation of method '%0' on object '%1' while it is in an unknown state">,
-  InGroup<ConsumedStrict>, DefaultIgnore;
-def warn_use_of_temp_in_unknown_state : Warning<
-  "invocation of method '%0' on a temporary object while it is in an unknown "
-  "state">, InGroup<ConsumedStrict>, DefaultIgnore;
 def warn_unnecessary_test : Warning<
   "unnecessary test. Variable '%0' is known to be in the '%1' state">,
   InGroup<ConsumedStrict>, DefaultIgnore;

Modified: cfe/trunk/lib/Analysis/Consumed.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Analysis/Consumed.cpp?rev=191983&r1=191982&r2=191983&view=diff
==============================================================================
--- cfe/trunk/lib/Analysis/Consumed.cpp (original)
+++ cfe/trunk/lib/Analysis/Consumed.cpp Fri Oct  4 16:28:06 2013
@@ -33,6 +33,8 @@
 
 // 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
+//       identifiers.
 // 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
@@ -66,6 +68,37 @@ static ConsumedState invertConsumedUncon
   llvm_unreachable("invalid enum");
 }
 
+static bool isCallableInState(const CallableWhenAttr *CWAttr,
+                              ConsumedState State) {
+  
+  CallableWhenAttr::callableState_iterator I = CWAttr->callableState_begin(),
+                                           E = CWAttr->callableState_end();
+  
+  for (; I != E; ++I) {
+    
+    ConsumedState MappedAttrState = CS_None;
+    
+    switch (*I) {
+    case CallableWhenAttr::Unknown:
+      MappedAttrState = CS_Unknown;
+      break;
+      
+    case CallableWhenAttr::Unconsumed:
+      MappedAttrState = CS_Unconsumed;
+      break;
+      
+    case CallableWhenAttr::Consumed:
+      MappedAttrState = CS_Consumed;
+      break;
+    }
+    
+    if (MappedAttrState == State)
+      return true;
+  }
+  
+  return false;
+}
+
 static bool isConsumableType(const QualType &QT) {
   if (const CXXRecordDecl *RD = QT->getAsCXXRecordDecl())
     return RD->hasAttr<ConsumableAttr>();
@@ -174,6 +207,8 @@ class PropagationInfo {
     BinTestTy BinTest;
   };
   
+  QualType TempType;
+  
 public:
   PropagationInfo() : InfoType(IT_None) {}
   
@@ -208,7 +243,9 @@ public:
     BinTest.RTest.TestsFor = RTestsFor;
   }
   
-  PropagationInfo(ConsumedState State) : InfoType(IT_State), State(State) {}
+  PropagationInfo(ConsumedState State, QualType TempType)
+    : InfoType(IT_State), State(State), TempType(TempType) {}
+  
   PropagationInfo(const VarDecl *Var) : InfoType(IT_Var), Var(Var) {}
   
   const ConsumedState & getState() const {
@@ -216,6 +253,11 @@ public:
     return State;
   }
   
+  const QualType & getTempType() const {
+    assert(InfoType == IT_State);
+    return TempType;
+  }
+  
   const VarTestResult & getTest() const {
     assert(InfoType == IT_Test);
     return Test;
@@ -327,51 +369,38 @@ public:
   }
 };
 
-// TODO: When we support CallableWhenConsumed this will have to check for
-//       the different attributes and change the behavior bellow. (Deferred)
 void ConsumedStmtVisitor::checkCallability(const PropagationInfo &PInfo,
                                            const FunctionDecl *FunDecl,
                                            const CallExpr *Call) {
   
-  if (!FunDecl->hasAttr<CallableWhenUnconsumedAttr>()) return;
+  if (!FunDecl->hasAttr<CallableWhenAttr>())
+    return;
+  
+  const CallableWhenAttr *CWAttr = FunDecl->getAttr<CallableWhenAttr>();
   
   if (PInfo.isVar()) {
     const VarDecl *Var = PInfo.getVar();
+    ConsumedState VarState = StateMap->getState(Var);
     
-    switch (StateMap->getState(Var)) {
-    case CS_Consumed:
-      Analyzer.WarningsHandler.warnUseWhileConsumed(
-        FunDecl->getNameAsString(), Var->getNameAsString(),
-        Call->getExprLoc());
-      break;
+    assert(VarState != CS_None && "Invalid state");
     
-    case CS_Unknown:
-      Analyzer.WarningsHandler.warnUseInUnknownState(
-        FunDecl->getNameAsString(), Var->getNameAsString(),
-        Call->getExprLoc());
-      break;
-      
-    case CS_None:
-    case CS_Unconsumed:
-      break;
-    }
+    if (isCallableInState(CWAttr, VarState))
+      return;
     
-  } else {
-    switch (PInfo.getState()) {
-    case CS_Consumed:
-      Analyzer.WarningsHandler.warnUseOfTempWhileConsumed(
-        FunDecl->getNameAsString(), Call->getExprLoc());
-      break;
+    Analyzer.WarningsHandler.warnUseInInvalidState(
+      FunDecl->getNameAsString(), Var->getNameAsString(),
+      stateToString(VarState), Call->getExprLoc());
     
-    case CS_Unknown:
-      Analyzer.WarningsHandler.warnUseOfTempInUnknownState(
-        FunDecl->getNameAsString(), Call->getExprLoc());
-      break;
-      
-    case CS_None:
-    case CS_Unconsumed:
-      break;
-    }
+  } else if (PInfo.isState()) {
+    
+    assert(PInfo.getState() != CS_None && "Invalid state");
+    
+    if (isCallableInState(CWAttr, PInfo.getState()))
+      return;
+    
+    Analyzer.WarningsHandler.warnUseOfTempInInvalidState(
+      FunDecl->getNameAsString(), stateToString(PInfo.getState()),
+      Call->getExprLoc());
   }
 }
 
@@ -421,7 +450,7 @@ void ConsumedStmtVisitor::propagateRetur
       ReturnState = mapConsumableAttrState(ReturnType);
     
     PropagationMap.insert(PairType(Call,
-      PropagationInfo(ReturnState)));
+      PropagationInfo(ReturnState, ReturnType)));
   }
 }
 
@@ -522,7 +551,11 @@ void ConsumedStmtVisitor::VisitCallExpr(
       }
     }
     
-    propagateReturnType(Call, FunDecl, FunDecl->getCallResultType());
+    QualType RetType = FunDecl->getCallResultType();
+    if (RetType->isReferenceType())
+      RetType = RetType->getPointeeType();
+    
+    propagateReturnType(Call, FunDecl, RetType);
   }
 }
 
@@ -540,7 +573,7 @@ void ConsumedStmtVisitor::VisitCXXConstr
     if (Constructor->isDefaultConstructor()) {
       
       PropagationMap.insert(PairType(Call,
-        PropagationInfo(consumed::CS_Consumed)));
+        PropagationInfo(consumed::CS_Consumed, ThisType)));
       
     } else if (Constructor->isMoveConstructor()) {
       
@@ -551,7 +584,7 @@ void ConsumedStmtVisitor::VisitCXXConstr
         const VarDecl* Var = PInfo.getVar();
         
         PropagationMap.insert(PairType(Call,
-          PropagationInfo(StateMap->getState(Var))));
+          PropagationInfo(StateMap->getState(Var), ThisType)));
         
         StateMap->setState(Var, consumed::CS_Consumed);
         
@@ -630,7 +663,8 @@ void ConsumedStmtVisitor::VisitCXXOperat
         
       } else if (!LPInfo.isVar() && RPInfo.isVar()) {
         PropagationMap.insert(PairType(Call,
-          PropagationInfo(StateMap->getState(RPInfo.getVar()))));
+          PropagationInfo(StateMap->getState(RPInfo.getVar()),
+                          LPInfo.getTempType())));
         
         StateMap->setState(RPInfo.getVar(), consumed::CS_Consumed);
         
@@ -648,27 +682,16 @@ void ConsumedStmtVisitor::VisitCXXOperat
         
         PropagationMap.insert(PairType(Call, LPInfo));
         
-      } else {
+      } else if (LPInfo.isState()) {
         PropagationMap.insert(PairType(Call,
-          PropagationInfo(consumed::CS_Unknown)));
+          PropagationInfo(consumed::CS_Unknown, LPInfo.getTempType())));
       }
       
     } else if (LEntry == PropagationMap.end() &&
                REntry != PropagationMap.end()) {
       
-      RPInfo = REntry->second;
-      
-      if (RPInfo.isVar()) {
-        const VarDecl *Var = RPInfo.getVar();
-        
-        PropagationMap.insert(PairType(Call,
-          PropagationInfo(StateMap->getState(Var))));
-        
-        StateMap->setState(Var, consumed::CS_Consumed);
-        
-      } else {
-        PropagationMap.insert(PairType(Call, RPInfo));
-      }
+      if (REntry->second.isVar())
+        StateMap->setState(REntry->second.getVar(), consumed::CS_Consumed);
     }
     
   } else {
@@ -776,6 +799,7 @@ void ConsumedStmtVisitor::VisitUnaryOper
   }
 }
 
+// TODO: See if I need to check for reference types here.
 void ConsumedStmtVisitor::VisitVarDecl(const VarDecl *Var) {
   if (isConsumableType(Var->getType())) {
     if (Var->hasInit()) {
@@ -803,13 +827,12 @@ void splitVarStateForIf(const IfStmt * I
   
   if (VarState == CS_Unknown) {
     ThenStates->setState(Test.Var, Test.TestsFor);
-    if (ElseStates)
-      ElseStates->setState(Test.Var, invertConsumedUnconsumed(Test.TestsFor));
+    ElseStates->setState(Test.Var, invertConsumedUnconsumed(Test.TestsFor));
   
   } else if (VarState == invertConsumedUnconsumed(Test.TestsFor)) {
     ThenStates->markUnreachable();
     
-  } else if (VarState == Test.TestsFor && ElseStates) {
+  } else if (VarState == Test.TestsFor) {
     ElseStates->markUnreachable();
   }
 }
@@ -832,31 +855,27 @@ void splitVarStateForIfBinOp(const Propa
         ThenStates->markUnreachable();
         
       } else if (LState == LTest.TestsFor && isKnownState(RState)) {
-        if (RState == RTest.TestsFor) {
-          if (ElseStates)
-            ElseStates->markUnreachable();
-        } else {
+        if (RState == RTest.TestsFor)
+          ElseStates->markUnreachable();
+        else
           ThenStates->markUnreachable();
-        }
       }
       
     } else {
-      if (LState == CS_Unknown && ElseStates) {
+      if (LState == CS_Unknown) {
         ElseStates->setState(LTest.Var,
                              invertConsumedUnconsumed(LTest.TestsFor));
       
-      } else if (LState == LTest.TestsFor && ElseStates) {
+      } else if (LState == LTest.TestsFor) {
         ElseStates->markUnreachable();
         
       } else if (LState == invertConsumedUnconsumed(LTest.TestsFor) &&
                  isKnownState(RState)) {
         
-        if (RState == RTest.TestsFor) {
-          if (ElseStates)
-            ElseStates->markUnreachable();
-        } else {
+        if (RState == RTest.TestsFor)
+          ElseStates->markUnreachable();
+        else
           ThenStates->markUnreachable();
-        }
       }
     }
   }
@@ -868,7 +887,7 @@ void splitVarStateForIfBinOp(const Propa
       else if (RState == invertConsumedUnconsumed(RTest.TestsFor))
         ThenStates->markUnreachable();
       
-    } else if (ElseStates) {
+    } else {
       if (RState == CS_Unknown)
         ElseStates->setState(RTest.Var,
                              invertConsumedUnconsumed(RTest.TestsFor));
@@ -1016,7 +1035,6 @@ bool ConsumedAnalyzer::splitState(const
   if (const IfStmt *IfNode =
     dyn_cast_or_null<IfStmt>(CurrBlock->getTerminator().getStmt())) {
     
-    bool HasElse = IfNode->getElse() != NULL;
     const Stmt *Cond = IfNode->getCond();
     
     PInfo = Visitor.getInfo(Cond);
@@ -1026,15 +1044,12 @@ bool ConsumedAnalyzer::splitState(const
     if (PInfo.isTest()) {
       CurrStates->setSource(Cond);
       FalseStates->setSource(Cond);
-      
-      splitVarStateForIf(IfNode, PInfo.getTest(), CurrStates,
-                         HasElse ? FalseStates : NULL);
+      splitVarStateForIf(IfNode, PInfo.getTest(), CurrStates, FalseStates);
       
     } else if (PInfo.isBinTest()) {
       CurrStates->setSource(PInfo.testSourceNode());
       FalseStates->setSource(PInfo.testSourceNode());
-      
-      splitVarStateForIfBinOp(PInfo, CurrStates, HasElse ? FalseStates : NULL);
+      splitVarStateForIfBinOp(PInfo, CurrStates, FalseStates);
       
     } else {
       delete FalseStates;

Modified: cfe/trunk/lib/Sema/AnalysisBasedWarnings.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/AnalysisBasedWarnings.cpp?rev=191983&r1=191982&r2=191983&view=diff
==============================================================================
--- cfe/trunk/lib/Sema/AnalysisBasedWarnings.cpp (original)
+++ cfe/trunk/lib/Sema/AnalysisBasedWarnings.cpp Fri Oct  4 16:28:06 2013
@@ -1503,36 +1503,20 @@ public:
     Warnings.push_back(DelayedDiag(Warning, OptionalNotes()));
   }
   
-  void warnUseOfTempWhileConsumed(StringRef MethodName, SourceLocation Loc) {
+  void warnUseOfTempInInvalidState(StringRef MethodName, StringRef State,
+                                   SourceLocation Loc) {
                                                     
     PartialDiagnosticAt Warning(Loc, S.PDiag(
-      diag::warn_use_of_temp_while_consumed) << MethodName);
+      diag::warn_use_of_temp_in_invalid_state) << MethodName << State);
     
     Warnings.push_back(DelayedDiag(Warning, OptionalNotes()));
   }
   
-  void warnUseOfTempInUnknownState(StringRef MethodName, SourceLocation Loc) {
+  void warnUseInInvalidState(StringRef MethodName, StringRef VariableName,
+                                  StringRef State, SourceLocation Loc) {
   
-    PartialDiagnosticAt Warning(Loc, S.PDiag(
-      diag::warn_use_of_temp_in_unknown_state) << MethodName);
-    
-    Warnings.push_back(DelayedDiag(Warning, OptionalNotes()));
-  }
-  
-  void warnUseWhileConsumed(StringRef MethodName, StringRef VariableName,
-                            SourceLocation Loc) {
-  
-    PartialDiagnosticAt Warning(Loc, S.PDiag(diag::warn_use_while_consumed) <<
-                                MethodName << VariableName);
-    
-    Warnings.push_back(DelayedDiag(Warning, OptionalNotes()));
-  }
-  
-  void warnUseInUnknownState(StringRef MethodName, StringRef VariableName,
-                             SourceLocation Loc) {
-
-    PartialDiagnosticAt Warning(Loc, S.PDiag(diag::warn_use_in_unknown_state) <<
-                                MethodName << VariableName);
+    PartialDiagnosticAt Warning(Loc, S.PDiag(diag::warn_use_in_invalid_state) <<
+                                MethodName << VariableName << State);
     
     Warnings.push_back(DelayedDiag(Warning, OptionalNotes()));
   }
@@ -1570,7 +1554,7 @@ clang::sema::AnalysisBasedWarnings::Anal
     (D.getDiagnosticLevel(diag::warn_double_lock, SourceLocation()) !=
      DiagnosticsEngine::Ignored);
   DefaultPolicy.enableConsumedAnalysis = (unsigned)
-    (D.getDiagnosticLevel(diag::warn_use_while_consumed, SourceLocation()) !=
+    (D.getDiagnosticLevel(diag::warn_use_in_invalid_state, SourceLocation()) !=
      DiagnosticsEngine::Ignored);
 }
 

Modified: cfe/trunk/lib/Sema/SemaDeclAttr.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaDeclAttr.cpp?rev=191983&r1=191982&r2=191983&view=diff
==============================================================================
--- cfe/trunk/lib/Sema/SemaDeclAttr.cpp (original)
+++ cfe/trunk/lib/Sema/SemaDeclAttr.cpp Fri Oct  4 16:28:06 2013
@@ -1041,8 +1041,11 @@ static void handleConsumesAttr(Sema &S,
                           Attr.getAttributeSpellingListIndex()));
 }
 
-static void handleCallableWhenUnconsumedAttr(Sema &S, Decl *D,
-                                             const AttributeList &Attr) {
+static void handleCallableWhenAttr(Sema &S, Decl *D,
+                                   const AttributeList &Attr) {
+  
+  if (!checkAttributeAtLeastNumArgs(S, Attr, 1)) return;
+
   if (!isa<CXXMethodDecl>(D)) {
     S.Diag(Attr.getLoc(), diag::warn_attribute_wrong_decl_type) <<
       Attr.getName() << ExpectedMethod;
@@ -1052,9 +1055,34 @@ static void handleCallableWhenUnconsumed
   if (!checkForConsumableClass(S, cast<CXXMethodDecl>(D), Attr))
     return;
   
+  SmallVector<CallableWhenAttr::ConsumedState, 3> States;
+  for (unsigned ArgIndex = 0; ArgIndex < Attr.getNumArgs(); ++ArgIndex) {
+    CallableWhenAttr::ConsumedState CallableState;
+    
+    if (Attr.isArgExpr(ArgIndex) &&
+        isa<StringLiteral>(Attr.getArgAsExpr(ArgIndex))) {
+      
+      Expr *Arg = Attr.getArgAsExpr(ArgIndex);
+      StringRef StateString = cast<StringLiteral>(Arg)->getString();
+      
+      if (!CallableWhenAttr::ConvertStrToConsumedState(StateString,
+                                                       CallableState)) {
+        S.Diag(Arg->getExprLoc(), diag::warn_attribute_type_not_supported)
+          << Attr.getName() << StateString;
+        return;
+      }
+      
+      States.push_back(CallableState);
+    } else {
+      S.Diag(Attr.getLoc(), diag::err_attribute_argument_type) << Attr.getName()
+        << AANT_ArgumentString;
+      return;
+    }
+  }
+  
   D->addAttr(::new (S.Context)
-             CallableWhenUnconsumedAttr(Attr.getRange(), S.Context,
-                                        Attr.getAttributeSpellingListIndex()));
+             CallableWhenAttr(Attr.getRange(), S.Context, States.data(),
+               States.size(), Attr.getAttributeSpellingListIndex()));
 }
 
 static void handleTestsConsumedAttr(Sema &S, Decl *D,
@@ -4767,8 +4795,8 @@ static void ProcessDeclAttribute(Sema &S
   case AttributeList::AT_Consumes:
     handleConsumesAttr(S, D, Attr);
     break;
-  case AttributeList::AT_CallableWhenUnconsumed:
-    handleCallableWhenUnconsumedAttr(S, D, Attr);
+  case AttributeList::AT_CallableWhen:
+    handleCallableWhenAttr(S, D, Attr);
     break;
   case AttributeList::AT_TestsConsumed:
     handleTestsConsumedAttr(S, D, Attr);

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=191983&r1=191982&r2=191983&view=diff
==============================================================================
--- cfe/trunk/test/SemaCXX/warn-consumed-analysis-strict.cpp (original)
+++ cfe/trunk/test/SemaCXX/warn-consumed-analysis-strict.cpp Fri Oct  4 16:28:06 2013
@@ -1,10 +1,10 @@
 // RUN: %clang_cc1 -fsyntax-only -verify -Wconsumed-strict -std=c++11 %s
 
-#define CALLABLE_WHEN_UNCONSUMED __attribute__ ((callable_when_unconsumed))
-#define CONSUMABLE(state)        __attribute__ ((consumable(state)))
-#define CONSUMES                 __attribute__ ((consumes))
-#define RETURN_TYPESTATE(state)  __attribute__ ((return_typestate(state)))
-#define TESTS_UNCONSUMED         __attribute__ ((tests_unconsumed))
+#define CALLABLE_WHEN(...)      __attribute__ ((callable_when(__VA_ARGS__)))
+#define CONSUMABLE(state)       __attribute__ ((consumable(state)))
+#define CONSUMES                __attribute__ ((consumes))
+#define RETURN_TYPESTATE(state) __attribute__ ((return_typestate(state)))
+#define TESTS_UNCONSUMED        __attribute__ ((tests_unconsumed))
 
 #define TEST_VAR(Var) Var.isValid()
 
@@ -32,8 +32,8 @@ class CONSUMABLE(unconsumed) ConsumableC
   ConsumableClass<T>& operator=(ConsumableClass<U> &&other);
   
   void operator()(int a) CONSUMES;
-  void operator*() const CALLABLE_WHEN_UNCONSUMED;
-  void unconsumedCall() const CALLABLE_WHEN_UNCONSUMED;
+  void operator*() const CALLABLE_WHEN("unconsumed");
+  void unconsumedCall() const CALLABLE_WHEN("unconsumed");
   
   bool isValid() const TESTS_UNCONSUMED;
   operator bool() const TESTS_UNCONSUMED;
@@ -45,9 +45,6 @@ class CONSUMABLE(unconsumed) ConsumableC
   void consume() CONSUMES;
 };
 
-void baf0(ConsumableClass<int>  &var);
-void baf1(ConsumableClass<int>  *var);
-
 void testIfStmt() {
   ConsumableClass<int> var;
   
@@ -60,137 +57,6 @@ void testIfStmt() {
   }
 }
 
-void testComplexConditionals() {
-  ConsumableClass<int> var0, var1, var2;
-  
-  // Coerce all variables into the unknown state.
-  baf0(var0);
-  baf0(var1);
-  baf0(var2);
-  
-  if (var0 && var1) {
-    *var0;
-    *var1;
-    
-  } 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; // 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; // 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; // 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() {
-  ConsumableClass<int> var(42);
-  
-  baf0(var);  
-  *var; // expected-warning {{invocation of method 'operator*' on object 'var' while it is in an unknown state}}
-  
-  var = ConsumableClass<int>(42);
-  baf1(&var);  
-  *var; // expected-warning {{invocation of method 'operator*' on object 'var' while it is in an unknown state}}
-}
-
-void testConstAndNonConstMemberFunctions() {
-  ConsumableClass<int> var(42);
-  
-  var.constCall();
-  *var;
-  
-  var.nonconstCall();
-  *var;
-}
-
-void testFunctionParam0(ConsumableClass<int> &param) {
-  *param; // expected-warning {{invocation of method 'operator*' on object 'param' while it is in an unknown state}}
-}
-
 void testNoWarnTestFromMacroExpansion() {
   ConsumableClass<int> var(42);
   
@@ -198,26 +64,3 @@ void testNoWarnTestFromMacroExpansion()
     *var;
   }
 }
-
-void testSimpleForLoop() {
-  ConsumableClass<int> var;
-  
-  for (int i = 0; i < 10; ++i) {
-    *var; // expected-warning {{invocation of method 'operator*' on object 'var' while it is in an unknown state}}
-  }
-  
-  *var; // expected-warning {{invocation of method 'operator*' on object 'var' while it is in an unknown state}}
-}
-
-void testSimpleWhileLoop() {
-  int i = 0;
-  
-  ConsumableClass<int> var;
-  
-  while (i < 10) {
-    *var; // expected-warning {{invocation of method 'operator*' on object 'var' while it is in an unknown state}}
-    ++i;
-  }
-  
-  *var; // expected-warning {{invocation of method 'operator*' on object 'var' while it is in an unknown state}}
-}

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=191983&r1=191982&r2=191983&view=diff
==============================================================================
--- cfe/trunk/test/SemaCXX/warn-consumed-analysis.cpp (original)
+++ cfe/trunk/test/SemaCXX/warn-consumed-analysis.cpp Fri Oct  4 16:28:06 2013
@@ -1,10 +1,12 @@
 // RUN: %clang_cc1 -fsyntax-only -verify -Wconsumed -std=c++11 %s
 
-#define CALLABLE_WHEN_UNCONSUMED __attribute__ ((callable_when_unconsumed))
-#define CONSUMABLE(state)        __attribute__ ((consumable(state)))
-#define CONSUMES                 __attribute__ ((consumes))
-#define RETURN_TYPESTATE(state)  __attribute__ ((return_typestate(state)))
-#define TESTS_UNCONSUMED         __attribute__ ((tests_unconsumed))
+// TODO: Switch to using macros for the expected warnings.
+
+#define CALLABLE_WHEN(...)      __attribute__ ((callable_when(__VA_ARGS__)))
+#define CONSUMABLE(state)       __attribute__ ((consumable(state)))
+#define CONSUMES                __attribute__ ((consumes))
+#define RETURN_TYPESTATE(state) __attribute__ ((return_typestate(state)))
+#define TESTS_UNCONSUMED        __attribute__ ((tests_unconsumed))
 
 typedef decltype(nullptr) nullptr_t;
 
@@ -30,8 +32,9 @@ class CONSUMABLE(unconsumed) ConsumableC
   ConsumableClass<T>& operator=(ConsumableClass<U> &&other);
   
   void operator()(int a) CONSUMES;
-  void operator*() const CALLABLE_WHEN_UNCONSUMED;
-  void unconsumedCall() const CALLABLE_WHEN_UNCONSUMED;
+  void operator*() const CALLABLE_WHEN("unconsumed");
+  void unconsumedCall() const CALLABLE_WHEN("unconsumed");
+  void callableWhenUnknown() const CALLABLE_WHEN("unconsumed", "unknown");
   
   bool isValid() const TESTS_UNCONSUMED;
   operator bool() const TESTS_UNCONSUMED;
@@ -47,7 +50,9 @@ void baf0(const ConsumableClass<int>  va
 void baf1(const ConsumableClass<int> &var);
 void baf2(const ConsumableClass<int> *var);
 
-void baf3(ConsumableClass<int> &&var);
+void baf3(ConsumableClass<int>  &var);
+void baf4(ConsumableClass<int>  *var);
+void baf5(ConsumableClass<int> &&var);
 
 ConsumableClass<int> returnsUnconsumed() {
   return ConsumableClass<int>(); // expected-warning {{return value not in expected state; expected 'unconsumed', observed 'consumed'}}
@@ -58,39 +63,41 @@ ConsumableClass<int> returnsConsumed() {
   return ConsumableClass<int>();
 }
 
+ConsumableClass<int> returnsUnknown() RETURN_TYPESTATE(unknown);
+
 void testInitialization() {
   ConsumableClass<int> var0;
   ConsumableClass<int> var1 = ConsumableClass<int>();
   
   var0 = ConsumableClass<int>();
   
-  *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}}
+  *var0; // expected-warning {{invalid invocation of method 'operator*' on object 'var0' while it is in the 'consumed' state}}
+  *var1; // expected-warning {{invalid invocation of method 'operator*' on object 'var1' while it is in the 'consumed' state}}
   
   if (var0.isValid()) {
     *var0;
     *var1;
     
   } else {
-    *var0; // expected-warning {{invocation of method 'operator*' on object 'var0' while it is in the 'consumed' state}}
+    *var0; // expected-warning {{invalid invocation of method 'operator*' on object 'var0' while it is in the 'consumed' state}}
   }
 }
 
 void testTempValue() {
-  *ConsumableClass<int>(); // expected-warning {{invocation of method 'operator*' on a temporary object while it is in the 'consumed' state}}
+  *ConsumableClass<int>(); // expected-warning {{invalid invocation of method 'operator*' on a temporary object while it is in the 'consumed' state}}
 }
 
 void testSimpleRValueRefs() {
   ConsumableClass<int> var0;
   ConsumableClass<int> var1(42);
   
-  *var0; // expected-warning {{invocation of method 'operator*' on object 'var0' while it is in the 'consumed' state}}
+  *var0; // expected-warning {{invalid invocation of method 'operator*' on object 'var0' while it is in the 'consumed' state}}
   *var1;
   
   var0 = static_cast<ConsumableClass<int>&&>(var1);
   
   *var0;
-  *var1; // expected-warning {{invocation of method 'operator*' on object 'var1' while it is in the 'consumed' state}}
+  *var1; // expected-warning {{invalid invocation of method 'operator*' on object 'var1' while it is in the 'consumed' state}}
 }
 
 void testIfStmt() {
@@ -99,11 +106,11 @@ void testIfStmt() {
   if (var.isValid()) {
     *var;
   } else {
-    *var; // expected-warning {{invocation of method 'operator*' on object 'var' while it is in the 'consumed' state}}
+    *var; // expected-warning {{invalid 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}}
+    *var; // expected-warning {{invalid invocation of method 'operator*' on object 'var' while it is in the 'consumed' state}}
   } else {
     *var;
   }
@@ -111,17 +118,17 @@ void testIfStmt() {
   if (var) {
     // Empty
   } else {
-    *var; // expected-warning {{invocation of method 'operator*' on object 'var' while it is in the 'consumed' state}}
+    *var; // expected-warning {{invalid 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}}
+    *var; // expected-warning {{invalid invocation of method 'operator*' on object 'var' while it is in the 'consumed' state}}
   }
 }
 
-void testComplexConditionals() {
+void testComplexConditionals0() {
   ConsumableClass<int> var0, var1, var2;
   
   if (var0 && var1) {
@@ -129,8 +136,8 @@ void testComplexConditionals() {
     *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}}
+    *var0; // expected-warning {{invalid invocation of method 'operator*' on object 'var0' while it is in the 'consumed' state}}
+    *var1; // expected-warning {{invalid invocation of method 'operator*' on object 'var1' while it is in the 'consumed' state}}
   }
   
   if (var0 || var1) {
@@ -138,8 +145,8 @@ void testComplexConditionals() {
     *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}}
+    *var0; // expected-warning {{invalid invocation of method 'operator*' on object 'var0' while it is in the 'consumed' state}}
+    *var1; // expected-warning {{invalid invocation of method 'operator*' on object 'var1' while it is in the 'consumed' state}}
   }
   
   if (var0 && !var1) {
@@ -147,13 +154,13 @@ void testComplexConditionals() {
     *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}}
+    *var0; // expected-warning {{invalid invocation of method 'operator*' on object 'var0' while it is in the 'consumed' state}}
+    *var1; // expected-warning {{invalid 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}}
+    *var0; // expected-warning {{invalid invocation of method 'operator*' on object 'var0' while it is in the 'consumed' state}}
+    *var1; // expected-warning {{invalid invocation of method 'operator*' on object 'var1' while it is in the 'consumed' state}}
     
   } else {
     *var0;
@@ -161,8 +168,8 @@ void testComplexConditionals() {
   }
   
   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}}
+    *var0; // expected-warning {{invalid invocation of method 'operator*' on object 'var0' while it is in the 'consumed' state}}
+    *var1; // expected-warning {{invalid invocation of method 'operator*' on object 'var1' while it is in the 'consumed' state}}
     
   } else {
     *var0;
@@ -170,8 +177,8 @@ void testComplexConditionals() {
   }
   
   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}}
+    *var0; // expected-warning {{invalid invocation of method 'operator*' on object 'var0' while it is in the 'consumed' state}}
+    *var1; // expected-warning {{invalid invocation of method 'operator*' on object 'var1' while it is in the 'consumed' state}}
     
   } else {
     *var0;
@@ -179,8 +186,8 @@ void testComplexConditionals() {
   }
   
   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}}
+    *var0; // expected-warning {{invalid invocation of method 'operator*' on object 'var0' while it is in the 'consumed' state}}
+    *var1; // expected-warning {{invalid invocation of method 'operator*' on object 'var1' while it is in the 'consumed' state}}
     
   } else {
     *var0;
@@ -188,8 +195,8 @@ void testComplexConditionals() {
   }
   
   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}}
+    *var0; // expected-warning {{invalid invocation of method 'operator*' on object 'var0' while it is in the 'consumed' state}}
+    *var1; // expected-warning {{invalid invocation of method 'operator*' on object 'var1' while it is in the 'consumed' state}}
     
   } else {
     *var0;
@@ -202,9 +209,9 @@ void testComplexConditionals() {
     *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}}
+    *var0; // expected-warning {{invalid invocation of method 'operator*' on object 'var0' while it is in the 'consumed' state}}
+    *var1; // expected-warning {{invalid invocation of method 'operator*' on object 'var1' while it is in the 'consumed' state}}
+    *var2; // expected-warning {{invalid invocation of method 'operator*' on object 'var2' while it is in the 'consumed' state}}
   }
   
 #if 0
@@ -215,9 +222,115 @@ void testComplexConditionals() {
     *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}}
+    *var0; // expected-warning {{invalid invocation of method 'operator*' on object 'var0' while it is in the 'consumed' state}}
+    *var1; // expected-warning {{invalid invocation of method 'operator*' on object 'var1' while it is in the 'consumed' state}}
+    *var2; // expected-warning {{invalid invocation of method 'operator*' on object 'var2' while it is in the 'consumed' state}}
+  }
+#endif
+}
+
+void testComplexConditionals1() {
+  ConsumableClass<int> var0, var1, var2;
+  
+  // Coerce all variables into the unknown state.
+  baf3(var0);
+  baf3(var1);
+  baf3(var2);
+  
+  if (var0 && var1) {
+    *var0;
+    *var1;
+    
+  } else {
+    *var0; // expected-warning {{invalid invocation of method 'operator*' on object 'var0' while it is in the 'unknown' state}}
+    *var1; // expected-warning {{invalid invocation of method 'operator*' on object 'var1' while it is in the 'unknown' state}}
+  }
+  
+  if (var0 || var1) {
+    *var0; // expected-warning {{invalid invocation of method 'operator*' on object 'var0' while it is in the 'unknown' state}}
+    *var1; // expected-warning {{invalid invocation of method 'operator*' on object 'var1' while it is in the 'unknown' state}}
+    
+  } else {
+    *var0; // expected-warning {{invalid invocation of method 'operator*' on object 'var0' while it is in the 'consumed' state}}
+    *var1; // expected-warning {{invalid invocation of method 'operator*' on object 'var1' while it is in the 'consumed' state}}
+  }
+  
+  if (var0 && !var1) {
+    *var0;
+    *var1; // expected-warning {{invalid invocation of method 'operator*' on object 'var1' while it is in the 'consumed' state}}
+    
+  } else {
+    *var0; // expected-warning {{invalid invocation of method 'operator*' on object 'var0' while it is in the 'unknown' state}}
+    *var1; // expected-warning {{invalid invocation of method 'operator*' on object 'var1' while it is in the 'unknown' state}}
+  }
+  
+  if (var0 || !var1) {
+    *var0; // expected-warning {{invalid invocation of method 'operator*' on object 'var0' while it is in the 'unknown' state}}
+    *var1; // expected-warning {{invalid invocation of method 'operator*' on object 'var1' while it is in the 'unknown' state}}
+    
+  } else {
+    *var0; // expected-warning {{invalid invocation of method 'operator*' on object 'var0' while it is in the 'consumed' state}}
+    *var1;
+  }
+  
+  if (!var0 && !var1) {
+    *var0; // expected-warning {{invalid invocation of method 'operator*' on object 'var0' while it is in the 'consumed' state}}
+    *var1; // expected-warning {{invalid invocation of method 'operator*' on object 'var1' while it is in the 'consumed' state}}
+    
+  } else {
+    *var0; // expected-warning {{invalid invocation of method 'operator*' on object 'var0' while it is in the 'unknown' state}}
+    *var1; // expected-warning {{invalid invocation of method 'operator*' on object 'var1' while it is in the 'unknown' state}}
+  }
+  
+  if (!(var0 || var1)) {
+    *var0; // expected-warning {{invalid invocation of method 'operator*' on object 'var0' while it is in the 'consumed' state}}
+    *var1; // expected-warning {{invalid invocation of method 'operator*' on object 'var1' while it is in the 'consumed' state}}
+    
+  } else {
+    *var0; // expected-warning {{invalid invocation of method 'operator*' on object 'var0' while it is in the 'unknown' state}}
+    *var1; // expected-warning {{invalid invocation of method 'operator*' on object 'var1' while it is in the 'unknown' state}}
+  }
+  
+  if (!var0 || !var1) {
+    *var0; // expected-warning {{invalid invocation of method 'operator*' on object 'var0' while it is in the 'unknown' state}}
+    *var1; // expected-warning {{invalid invocation of method 'operator*' on object 'var1' while it is in the 'unknown' state}}
+    
+  } else {
+    *var0;
+    *var1;
+  }
+  
+  if (!(var0 && var1)) {
+    *var0; // expected-warning {{invalid invocation of method 'operator*' on object 'var0' while it is in the 'unknown' state}}
+    *var1; // expected-warning {{invalid invocation of method 'operator*' on object 'var1' while it is in the 'unknown' state}}
+    
+  } else {
+    *var0;
+    *var1;
+  }
+  
+  if (var0 && var1 && var2) {
+    *var0;
+    *var1;
+    *var2;
+    
+  } else {
+    *var0; // expected-warning {{invalid invocation of method 'operator*' on object 'var0' while it is in the 'unknown' state}}
+    *var1; // expected-warning {{invalid invocation of method 'operator*' on object 'var1' while it is in the 'unknown' state}}
+    *var2; // expected-warning {{invalid invocation of method 'operator*' on object 'var2' while it is in the 'unknown' state}}
+  }
+  
+#if 0
+  // FIXME: Get this test to pass.
+  if (var0 || var1 || var2) {
+    *var0; // expected-warning {{invalid invocation of method 'operator*' on object 'var0' while it is in the 'unknown' state}}
+    *var1; // expected-warning {{invalid invocation of method 'operator*' on object 'var1' while it is in the 'unknown' state}}
+    *var2; // expected-warning {{invalid invocation of method 'operator*' on object 'var2' while it is in the 'unknown' state}}
+    
+  } else {
+    *var0; // expected-warning {{invalid invocation of method 'operator*' on object 'var0' while it is in the 'consumed' state}}
+    *var1; // expected-warning {{invalid invocation of method 'operator*' on object 'var1' while it is in the 'consumed' state}}
+    *var2; // expected-warning {{invalid invocation of method 'operator*' on object 'var2' while it is in the 'consumed' state}}
   }
 #endif
 }
@@ -226,7 +339,7 @@ void testStateChangeInBranch() {
   ConsumableClass<int> var;
   
   // Make var enter the 'unknown' state.
-  baf1(var);
+  baf3(var);
   
   if (!var) {
     var = ConsumableClass<int>(42);
@@ -259,8 +372,34 @@ void testCallingConventions() {
   baf2(&var);  
   *var;
   
-  baf3(static_cast<ConsumableClass<int>&&>(var));  
-  *var; // expected-warning {{invocation of method 'operator*' on object 'var' while it is in the 'consumed' state}}
+  baf3(var);  
+  *var; // expected-warning {{invalid invocation of method 'operator*' on object 'var' while it is in the 'unknown' state}}
+  
+  var = ConsumableClass<int>(42);
+  baf4(&var);  
+  *var; // expected-warning {{invalid invocation of method 'operator*' on object 'var' while it is in the 'unknown' state}}
+  
+  var = ConsumableClass<int>(42);
+  baf5(static_cast<ConsumableClass<int>&&>(var));  
+  *var; // expected-warning {{invalid invocation of method 'operator*' on object 'var' while it is in the 'consumed' state}}
+}
+
+void testConstAndNonConstMemberFunctions() {
+  ConsumableClass<int> var(42);
+  
+  var.constCall();
+  *var;
+  
+  var.nonconstCall();
+  *var;
+}
+
+void testFunctionParam0(ConsumableClass<int> param) {
+  *param;
+}
+
+void testFunctionParam1(ConsumableClass<int> &param) {
+  *param; // expected-warning {{invalid invocation of method 'operator*' on object 'param' while it is in the 'unknown' state}}
 }
 
 void testReturnStates() {
@@ -270,24 +409,34 @@ void testReturnStates() {
   *var;
   
   var = returnsConsumed();
-  *var; // expected-warning {{invocation of method 'operator*' on object 'var' while it is in the 'consumed' state}}
+  *var; // expected-warning {{invalid invocation of method 'operator*' on object 'var' while it is in the 'consumed' state}}
+}
+
+void testCallableWhen() {
+  ConsumableClass<int> var(42);
+  
+  *var;
+  
+  baf3(var);
+  
+  var.callableWhenUnknown();
 }
 
 void testMoveAsignmentish() {
   ConsumableClass<int>  var0;
   ConsumableClass<long> var1(42);
   
-  *var0; // expected-warning {{invocation of method 'operator*' on object 'var0' while it is in the 'consumed' state}}
+  *var0; // expected-warning {{invalid invocation of method 'operator*' on object 'var0' while it is in the 'consumed' state}}
   *var1;
   
   var0 = static_cast<ConsumableClass<long>&&>(var1);
   
   *var0;
-  *var1; // expected-warning {{invocation of method 'operator*' on object 'var1' while it is in the 'consumed' state}}
+  *var1; // expected-warning {{invalid invocation of method 'operator*' on object 'var1' while it is in the 'consumed' state}}
   
   var1 = ConsumableClass<long>(42);
   var1 = nullptr;
-  *var1; // expected-warning {{invocation of method 'operator*' on object 'var1' while it is in the 'consumed' state}}
+  *var1; // expected-warning {{invalid invocation of method 'operator*' on object 'var1' while it is in the 'consumed' state}}
 }
 
 void testConditionalMerge() {
@@ -297,7 +446,7 @@ void testConditionalMerge() {
     // Empty
   }
   
-  *var; // expected-warning {{invocation of method 'operator*' on object 'var' while it is in the 'consumed' state}}
+  *var; // expected-warning {{invalid invocation of method 'operator*' on object 'var' while it is in the 'consumed' state}}
   
   if (var.isValid()) {
     // Empty
@@ -305,7 +454,7 @@ void testConditionalMerge() {
     // Empty
   }
   
-  *var; // expected-warning {{invocation of method 'operator*' on object 'var' while it is in the 'consumed' state}}
+  *var; // expected-warning {{invalid invocation of method 'operator*' on object 'var' while it is in the 'consumed' state}}
 }
 
 void testConsumes0() {
@@ -315,13 +464,13 @@ void testConsumes0() {
   
   var.consume();
   
-  *var; // expected-warning {{invocation of method 'operator*' on object 'var' while it is in the 'consumed' state}}
+  *var; // expected-warning {{invalid invocation of method 'operator*' on object 'var' while it is in the 'consumed' state}}
 }
 
 void testConsumes1() {
   ConsumableClass<int> var(nullptr);
   
-  *var; // expected-warning {{invocation of method 'operator*' on object 'var' while it is in the 'consumed' state}}
+  *var; // expected-warning {{invalid invocation of method 'operator*' on object 'var' while it is in the 'consumed' state}}
 }
 
 void testConsumes2() {
@@ -330,10 +479,10 @@ void testConsumes2() {
   var.unconsumedCall();
   var(6);
   
-  var.unconsumedCall(); // expected-warning {{invocation of method 'unconsumedCall' on object 'var' while it is in the 'consumed' state}}
+  var.unconsumedCall(); // expected-warning {{invalid invocation of method 'unconsumedCall' on object 'var' while it is in the 'consumed' state}}
 }
 
-void testNonsenseState() {
+void testUnreachableBlock() {
   ConsumableClass<int> var(42);
   
   if (var) {
@@ -349,10 +498,10 @@ void testSimpleForLoop() {
   ConsumableClass<int> var;
   
   for (int i = 0; i < 10; ++i) {
-    *var;
+    *var; // expected-warning {{invalid invocation of method 'operator*' on object 'var' while it is in the 'unknown' state}}
   }
   
-  *var;
+  *var; // expected-warning {{invalid invocation of method 'operator*' on object 'var' while it is in the 'unknown' state}}
 }
 
 void testSimpleWhileLoop() {
@@ -361,9 +510,9 @@ void testSimpleWhileLoop() {
   ConsumableClass<int> var;
   
   while (i < 10) {
-    *var;
+    *var; // expected-warning {{invalid invocation of method 'operator*' on object 'var' while it is in the 'unknown' state}}
     ++i;
   }
   
-  *var;
+  *var; // expected-warning {{invalid invocation of method 'operator*' on object 'var' while it is in the 'unknown' state}}
 }

Modified: cfe/trunk/test/SemaCXX/warn-consumed-parsing.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/SemaCXX/warn-consumed-parsing.cpp?rev=191983&r1=191982&r2=191983&view=diff
==============================================================================
--- cfe/trunk/test/SemaCXX/warn-consumed-parsing.cpp (original)
+++ cfe/trunk/test/SemaCXX/warn-consumed-parsing.cpp Fri Oct  4 16:28:06 2013
@@ -1,6 +1,6 @@
 // RUN: %clang_cc1 -fsyntax-only -verify -Wconsumed -std=c++11 %s
 
-#define CALLABLE_WHEN_UNCONSUMED __attribute__ ((callable_when_unconsumed))
+#define CALLABLE_WHEN(...)      __attribute__ ((callable_when(__VA_ARGS__)))
 #define CONSUMABLE(state)        __attribute__ ((consumable(state)))
 #define CONSUMES                 __attribute__ ((consumes))
 #define RETURN_TYPESTATE(state)  __attribute__ ((return_typestate(state)))
@@ -19,33 +19,35 @@ int returnTypestateForUnconsumable() {
 class AttrTester0 {
   void consumes()        __attribute__ ((consumes(42))); // expected-error {{attribute takes no arguments}}
   bool testsUnconsumed() __attribute__ ((tests_unconsumed(42))); // expected-error {{attribute takes no arguments}}
-  void callableWhenUnconsumed() __attribute__ ((callable_when_unconsumed(42))); // expected-error {{attribute takes no arguments}}
+  void callableWhen()    __attribute__ ((callable_when())); // expected-error {{attribute takes at least 1 argument}}
 };
 
 int var0 CONSUMES; // expected-warning {{'consumes' attribute only applies to methods}}
 int var1 TESTS_UNCONSUMED; // expected-warning {{'tests_unconsumed' attribute only applies to methods}}
-int var2 CALLABLE_WHEN_UNCONSUMED; // expected-warning {{'callable_when_unconsumed' attribute only applies to methods}}
+int var2 CALLABLE_WHEN(42); // expected-warning {{'callable_when' attribute only applies to methods}}
 int var3 CONSUMABLE(consumed); // expected-warning {{'consumable' attribute only applies to classes}}
 int var4 RETURN_TYPESTATE(consumed); // expected-warning {{'return_typestate' attribute only applies to functions}}
 
 void function0() CONSUMES; // expected-warning {{'consumes' attribute only applies to methods}}
 void function1() TESTS_UNCONSUMED; // expected-warning {{'tests_unconsumed' attribute only applies to methods}}
-void function2() CALLABLE_WHEN_UNCONSUMED; // expected-warning {{'callable_when_unconsumed' attribute only applies to methods}}
+void function2() CALLABLE_WHEN(42); // expected-warning {{'callable_when' attribute only applies to methods}}
 void function3() CONSUMABLE(consumed); // expected-warning {{'consumable' attribute only applies to classes}}
 
 class CONSUMABLE(unknown) AttrTester1 {
-  void callableWhenUnconsumed() CALLABLE_WHEN_UNCONSUMED;
-  void consumes()               CONSUMES;
-  bool testsUnconsumed()        TESTS_UNCONSUMED;
+  void callableWhen0()   CALLABLE_WHEN("unconsumed");
+  void callableWhen1()   CALLABLE_WHEN(42); // expected-error {{'callable_when' attribute requires a string}}
+  void callableWhen2()   CALLABLE_WHEN("foo"); // expected-warning {{'callable_when' attribute argument not supported: foo}}
+  void consumes()        CONSUMES;
+  bool testsUnconsumed() TESTS_UNCONSUMED;
 };
 
 AttrTester1 returnTypestateTester0() RETURN_TYPESTATE(not_a_state); // expected-warning {{'return_typestate' attribute argument not supported: 'not_a_state'}}
 AttrTester1 returnTypestateTester1() RETURN_TYPESTATE(42); // expected-error {{'return_typestate' attribute requires an identifier}}
 
 class AttrTester2 {
-  void callableWhenUnconsumed() CALLABLE_WHEN_UNCONSUMED; // expected-warning {{consumed analysis attribute is attached to member of class 'AttrTester2' which isn't marked as consumable}}
-  void consumes()               CONSUMES; // expected-warning {{consumed analysis attribute is attached to member of class 'AttrTester2' which isn't marked as consumable}}
-  bool testsUnconsumed()        TESTS_UNCONSUMED; // expected-warning {{consumed analysis attribute is attached to member of class 'AttrTester2' which isn't marked as consumable}}
+  void callableWhen()    CALLABLE_WHEN("unconsumed"); // expected-warning {{consumed analysis attribute is attached to member of class 'AttrTester2' which isn't marked as consumable}}
+  void consumes()        CONSUMES; // expected-warning {{consumed analysis attribute is attached to member of class 'AttrTester2' which isn't marked as consumable}}
+  bool testsUnconsumed() TESTS_UNCONSUMED; // expected-warning {{consumed analysis attribute is attached to member of class 'AttrTester2' which isn't marked as consumable}}
 };
 
 class CONSUMABLE(42) AttrTester3; // expected-error {{'consumable' attribute requires an identifier}}

Modified: cfe/trunk/utils/TableGen/ClangAttrEmitter.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/utils/TableGen/ClangAttrEmitter.cpp?rev=191983&r1=191982&r2=191983&view=diff
==============================================================================
--- cfe/trunk/utils/TableGen/ClangAttrEmitter.cpp (original)
+++ cfe/trunk/utils/TableGen/ClangAttrEmitter.cpp Fri Oct  4 16:28:06 2013
@@ -136,6 +136,7 @@ namespace {
     virtual void writeHasChildren(raw_ostream &OS) const { OS << "false"; }
 
     virtual bool isEnumArg() const { return false; }
+    virtual bool isVariadicEnumArg() const { return false; }
   };
 
   class SimpleArgument : public Argument {
@@ -474,7 +475,7 @@ namespace {
          << ";\n";
       OS << "  " << getLowerName() << ".reserve(" << getLowerName()
          << "Size);\n";
-      OS << "  for (unsigned i = " << getLowerName() << "Size; i; --i)\n";
+      OS << "    for (unsigned i = " << getLowerName() << "Size; i; --i)\n";
       
       std::string read = ReadPCHRecord(type);
       OS << "    " << getLowerName() << ".push_back(" << read << ");\n";
@@ -604,6 +605,93 @@ namespace {
       OS << "  }\n";
     }
   };
+  
+  class VariadicEnumArgument: public VariadicArgument {
+    std::string type, QualifiedTypeName;
+    std::vector<StringRef> values, enums, uniques;
+  public:
+    VariadicEnumArgument(Record &Arg, StringRef Attr)
+      : VariadicArgument(Arg, Attr, Arg.getValueAsString("Type")),
+        type(Arg.getValueAsString("Type")),
+        values(getValueAsListOfStrings(Arg, "Values")),
+        enums(getValueAsListOfStrings(Arg, "Enums")),
+        uniques(enums)
+    {
+      // Calculate the various enum values
+      std::sort(uniques.begin(), uniques.end());
+      uniques.erase(std::unique(uniques.begin(), uniques.end()), uniques.end());
+      
+      QualifiedTypeName = getAttrName().str() + "Attr::" + type;
+      
+      // FIXME: Emit a proper error
+      assert(!uniques.empty());
+    }
+
+    bool isVariadicEnumArg() const { return true; }
+    
+    void writeDeclarations(raw_ostream &OS) const {
+      std::vector<StringRef>::const_iterator i = uniques.begin(),
+                                             e = uniques.end();
+      // The last one needs to not have a comma.
+      --e;
+
+      OS << "public:\n";
+      OS << "  enum " << type << " {\n";
+      for (; i != e; ++i)
+        OS << "    " << *i << ",\n";
+      OS << "    " << *e << "\n";
+      OS << "  };\n";
+      OS << "private:\n";
+      
+      VariadicArgument::writeDeclarations(OS);
+    }
+    void writeDump(raw_ostream &OS) const {
+      OS << "    for (" << getAttrName() << "Attr::" << getLowerName()
+         << "_iterator I = SA->" << getLowerName() << "_begin(), E = SA->"
+         << getLowerName() << "_end(); I != E; ++I) {\n";
+      OS << "      switch(*I) {\n";
+      for (std::vector<StringRef>::const_iterator UI = uniques.begin(),
+           UE = uniques.end(); UI != UE; ++UI) {
+        OS << "    case " << getAttrName() << "Attr::" << *UI << ":\n";
+        OS << "      OS << \" " << *UI << "\";\n";
+        OS << "      break;\n";
+      }
+      OS << "      }\n";
+      OS << "    }\n";
+    }
+    void writePCHReadDecls(raw_ostream &OS) const {
+      OS << "    unsigned " << getLowerName() << "Size = Record[Idx++];\n";
+      OS << "    SmallVector<" << QualifiedTypeName << ", 4> " << getLowerName()
+         << ";\n";
+      OS << "    " << getLowerName() << ".reserve(" << getLowerName()
+         << "Size);\n";
+      OS << "    for (unsigned i = " << getLowerName() << "Size; i; --i)\n";
+      OS << "      " << getLowerName() << ".push_back(" << "static_cast<"
+         << QualifiedTypeName << ">(Record[Idx++]));\n";
+    }
+    void writePCHWrite(raw_ostream &OS) const{
+      OS << "    Record.push_back(SA->" << getLowerName() << "_size());\n";
+      OS << "    for (" << getAttrName() << "Attr::" << getLowerName()
+         << "_iterator i = SA->" << getLowerName() << "_begin(), e = SA->"
+         << getLowerName() << "_end(); i != e; ++i)\n";
+      OS << "      " << WritePCHRecord(QualifiedTypeName, "(*i)");
+    }
+    void writeConversion(raw_ostream &OS) const {
+      OS << "  static bool ConvertStrTo" << type << "(StringRef Val, ";
+      OS << type << " &Out) {\n";
+      OS << "    Optional<" << type << "> R = llvm::StringSwitch<Optional<";
+      OS << type << "> >(Val)\n";
+      for (size_t I = 0; I < enums.size(); ++I) {
+        OS << "      .Case(\"" << values[I] << "\", ";
+        OS << getAttrName() << "Attr::" << enums[I] << ")\n";
+      }
+      OS << "      .Default(Optional<" << type << ">());\n";
+      OS << "    if (R) {\n";
+      OS << "      Out = *R;\n      return true;\n    }\n";
+      OS << "    return false;\n";
+      OS << "  }\n";
+    }
+  };
 
   class VersionArgument : public Argument {
   public:
@@ -768,6 +856,8 @@ static Argument *createArgument(Record &
     Ptr = new SimpleArgument(Arg, Attr, "SourceLocation");
   else if (ArgName == "VariadicUnsignedArgument")
     Ptr = new VariadicArgument(Arg, Attr, "unsigned");
+  else if (ArgName == "VariadicEnumArgument")
+    Ptr = new VariadicEnumArgument(Arg, Attr);
   else if (ArgName == "VariadicExprArgument")
     Ptr = new VariadicExprArgument(Arg, Attr);
   else if (ArgName == "VersionArgument")
@@ -1051,6 +1141,9 @@ void EmitClangAttrClass(RecordKeeper &Re
       if ((*ai)->isEnumArg()) {
         EnumArgument *EA = (EnumArgument *)*ai;
         EA->writeConversion(OS);
+      } else if ((*ai)->isVariadicEnumArg()) {
+        VariadicEnumArgument *VEA = (VariadicEnumArgument *)*ai;
+        VEA->writeConversion(OS);
       }
     }
 





More information about the cfe-commits mailing list