r192932 - Consumed Analysis: Allow parameters that are passed by non-const reference

DeLesley Hutchins delesley at google.com
Thu Oct 17 15:53:05 PDT 2013


Author: delesley
Date: Thu Oct 17 17:53:04 2013
New Revision: 192932

URL: http://llvm.org/viewvc/llvm-project?rev=192932&view=rev
Log:
Consumed Analysis: Allow parameters that are passed by non-const reference
to be treated as return values, and marked with the "returned_typestate"
attribute.  Patch by chris.wailes at gmail.com; reviewed by delesley at google.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.cpp
    cfe/trunk/test/SemaCXX/warn-consumed-parsing.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=192932&r1=192931&r2=192932&view=diff
==============================================================================
--- cfe/trunk/include/clang/Analysis/Analyses/Consumed.h (original)
+++ cfe/trunk/include/clang/Analysis/Analyses/Consumed.h Thu Oct 17 17:53:04 2013
@@ -59,6 +59,20 @@ namespace consumed {
     virtual void warnLoopStateMismatch(SourceLocation Loc,
                                        StringRef VariableName) {}
     
+    /// \brief Warn about parameter typestate mismatches upon return.
+    ///
+    /// \param Loc -- The SourceLocation of the return statement.
+    ///
+    /// \param ExpectedState -- The state the return value was expected to be
+    /// in.
+    ///
+    /// \param ObservedState -- The state the return value was observed to be
+    /// in.
+    virtual void warnParamReturnTypestateMismatch(SourceLocation Loc,
+                                                  StringRef VariableName,
+                                                  StringRef ExpectedState,
+                                                  StringRef ObservedState) {};
+    
     // FIXME: This can be removed when the attr propagation fix for templated
     //        classes lands.
     /// \brief Warn about return typestates set for unconsumable types.
@@ -70,7 +84,14 @@ namespace consumed {
                                                         StringRef TypeName) {}
     
     /// \brief Warn about return typestate mismatches.
+    ///
     /// \param Loc -- The SourceLocation of the return statement.
+    ///
+    /// \param ExpectedState -- The state the return value was expected to be
+    /// in.
+    ///
+    /// \param ObservedState -- The state the return value was observed to be
+    /// in.
     virtual void warnReturnTypestateMismatch(SourceLocation Loc,
                                              StringRef ExpectedState,
                                              StringRef ObservedState) {}
@@ -118,6 +139,11 @@ namespace consumed {
     ConsumedStateMap(const ConsumedStateMap &Other)
       : Reachable(Other.Reachable), From(Other.From), Map(Other.Map) {}
     
+    /// \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 Get the consumed state of a given variable.
     ConsumedState getState(const VarDecl *Var) const;
     

Modified: cfe/trunk/include/clang/Basic/Attr.td
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Basic/Attr.td?rev=192932&r1=192931&r2=192932&view=diff
==============================================================================
--- cfe/trunk/include/clang/Basic/Attr.td (original)
+++ cfe/trunk/include/clang/Basic/Attr.td Thu Oct 17 17:53:04 2013
@@ -969,7 +969,7 @@ def CallableWhen : InheritableAttr {
 
 def ReturnTypestate : InheritableAttr {
   let Spellings = [GNU<"return_typestate">];
-  let Subjects = [Function];
+  let Subjects = [Function, ParmVar];
   let Args = [EnumArgument<"State", "ConsumedState",
                            ["unknown", "consumed", "unconsumed"],
                            ["Unknown", "Consumed", "Unconsumed"]>];

Modified: cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td?rev=192932&r1=192931&r2=192932&view=diff
==============================================================================
--- cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td (original)
+++ cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td Thu Oct 17 17:53:04 2013
@@ -2222,6 +2222,9 @@ def warn_return_typestate_mismatch : War
 def warn_loop_state_mismatch : Warning<
   "state of variable '%0' must match at the entry and exit of loop">,
   InGroup<Consumed>, DefaultIgnore;
+def warn_param_return_typestate_mismatch : Warning<
+  "parameter '%0' not in expected state when the function returns: expected "
+  "'%1', observed '%2'">, InGroup<Consumed>, DefaultIgnore;
 
 def warn_impcast_vector_scalar : Warning<
   "implicit conversion turns vector to scalar: %0 to %1">,

Modified: cfe/trunk/lib/Analysis/Consumed.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Analysis/Consumed.cpp?rev=192932&r1=192931&r2=192932&view=diff
==============================================================================
--- cfe/trunk/lib/Analysis/Consumed.cpp (original)
+++ cfe/trunk/lib/Analysis/Consumed.cpp Thu Oct 17 17:53:04 2013
@@ -87,7 +87,7 @@ static SourceLocation getLastStmtLoc(con
     Loc = getFirstStmtLoc(*Block->succ_begin());
   if (Loc.isValid())
     return Loc;
-  
+
   // If we have one predecessor, return the last statement in that block
   if (Block->pred_size() == 1 && *Block->pred_begin())
     return getLastStmtLoc(*Block->pred_begin());
@@ -572,7 +572,8 @@ void ConsumedStmtVisitor::VisitCallExpr(
     unsigned Offset = Call->getNumArgs() - FunDecl->getNumParams();
     
     for (unsigned Index = Offset; Index < Call->getNumArgs(); ++Index) {
-      QualType ParamType = FunDecl->getParamDecl(Index - Offset)->getType();
+      const ParmVarDecl *Param = FunDecl->getParamDecl(Index - Offset);
+      QualType ParamType = Param->getType();
       
       InfoEntry Entry = PropagationMap.find(Call->getArg(Index));
       
@@ -588,6 +589,10 @@ void ConsumedStmtVisitor::VisitCallExpr(
         
         StateMap->setState(PInfo.getVar(), consumed::CS_Consumed);
         
+      } else if (Param->hasAttr<ReturnTypestateAttr>()) {
+        StateMap->setState(PInfo.getVar(),
+          mapReturnTypestateAttrState(Param->getAttr<ReturnTypestateAttr>()));
+        
       } else if (!(ParamType.isConstQualified() ||
                    ((ParamType->isReferenceType() ||
                      ParamType->isPointerType()) &&
@@ -820,7 +825,9 @@ void ConsumedStmtVisitor::VisitParmVarDe
 }
 
 void ConsumedStmtVisitor::VisitReturnStmt(const ReturnStmt *Ret) {
-  if (ConsumedState ExpectedState = Analyzer.getExpectedReturnState()) {
+  ConsumedState ExpectedState = Analyzer.getExpectedReturnState();
+  
+  if (ExpectedState != CS_None) {
     InfoEntry Entry = PropagationMap.find(Ret->getRetValue());
     
     if (Entry != PropagationMap.end()) {
@@ -835,6 +842,9 @@ void ConsumedStmtVisitor::VisitReturnStm
           stateToString(RetState));
     }
   }
+  
+  StateMap->checkParamsForReturnTypestate(Ret->getLocStart(),
+                                          Analyzer.WarningsHandler);
 }
 
 void ConsumedStmtVisitor::VisitUnaryOperator(const UnaryOperator *UOp) {
@@ -1054,6 +1064,31 @@ bool ConsumedBlockInfo::isBackEdgeTarget
   return false;
 }
 
+void ConsumedStateMap::checkParamsForReturnTypestate(SourceLocation BlameLoc,
+  ConsumedWarningsHandlerBase &WarningsHandler) const {
+  
+  ConsumedState ExpectedState;
+  
+  for (MapType::const_iterator DMI = Map.begin(), DME = Map.end(); DMI != DME;
+       ++DMI) {
+    
+    if (isa<ParmVarDecl>(DMI->first)) {
+      const ParmVarDecl *Param = cast<ParmVarDecl>(DMI->first);
+      
+      if (!Param->hasAttr<ReturnTypestateAttr>()) continue;
+      
+      ExpectedState =
+        mapReturnTypestateAttrState(Param->getAttr<ReturnTypestateAttr>());
+      
+      if (DMI->second != ExpectedState) {
+        WarningsHandler.warnParamReturnTypestateMismatch(BlameLoc,
+          Param->getNameAsString(), stateToString(ExpectedState),
+          stateToString(DMI->second));
+      }
+    }
+  }
+}
+
 ConsumedState ConsumedStateMap::getState(const VarDecl *Var) const {
   MapType::const_iterator Entry = Map.find(Var);
   
@@ -1375,6 +1410,11 @@ void ConsumedAnalyzer::run(AnalysisDeclC
         CurrStates = NULL;
       }
     }
+    
+    if (CurrBlock == &AC.getCFG()->getExit() &&
+        D->getCallResultType()->isVoidType())
+      CurrStates->checkParamsForReturnTypestate(D->getLocation(),
+                                                WarningsHandler);
   } // End of block iterator.
   
   // Delete the last existing state map.

Modified: cfe/trunk/lib/Sema/AnalysisBasedWarnings.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/AnalysisBasedWarnings.cpp?rev=192932&r1=192931&r2=192932&view=diff
==============================================================================
--- cfe/trunk/lib/Sema/AnalysisBasedWarnings.cpp (original)
+++ cfe/trunk/lib/Sema/AnalysisBasedWarnings.cpp Thu Oct 17 17:53:04 2013
@@ -1484,6 +1484,18 @@ public:
     Warnings.push_back(DelayedDiag(Warning, OptionalNotes()));
   }
   
+  void warnParamReturnTypestateMismatch(SourceLocation Loc,
+                                        StringRef VariableName,
+                                        StringRef ExpectedState,
+                                        StringRef ObservedState) {
+    
+    PartialDiagnosticAt Warning(Loc, S.PDiag(
+      diag::warn_param_return_typestate_mismatch) << VariableName <<
+        ExpectedState << ObservedState);
+    
+    Warnings.push_back(DelayedDiag(Warning, OptionalNotes()));
+  }
+  
   void warnReturnTypestateForUnconsumableType(SourceLocation Loc,
                                               StringRef TypeName) {
     PartialDiagnosticAt Warning(Loc, S.PDiag(

Modified: cfe/trunk/lib/Sema/SemaDeclAttr.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaDeclAttr.cpp?rev=192932&r1=192931&r2=192932&view=diff
==============================================================================
--- cfe/trunk/lib/Sema/SemaDeclAttr.cpp (original)
+++ cfe/trunk/lib/Sema/SemaDeclAttr.cpp Thu Oct 17 17:53:04 2013
@@ -1068,6 +1068,14 @@ static void handleCallableWhenAttr(Sema
 
 static void handleReturnTypestateAttr(Sema &S, Decl *D,
                                       const AttributeList &Attr) {
+  if (!checkAttributeNumArgs(S, Attr, 1)) return;
+  
+  if (!(isa<FunctionDecl>(D) || isa<ParmVarDecl>(D))) {
+    S.Diag(Attr.getLoc(), diag::warn_attribute_wrong_decl_type) <<
+      Attr.getName() << ExpectedFunctionMethodOrParameter;
+    return;
+  }
+
   ReturnTypestateAttr::ConsumedState ReturnState;
   
   if (Attr.isArgIdent(0)) {
@@ -1084,18 +1092,16 @@ static void handleReturnTypestateAttr(Se
     return;
   }
   
-  if (!isa<FunctionDecl>(D)) {
-    S.Diag(Attr.getLoc(), diag::warn_attribute_wrong_decl_type) <<
-      Attr.getName() << ExpectedFunction;
-    return;
-  }
-  
   // FIXME: This check is currently being done in the analysis.  It can be
   //        enabled here only after the parser propagates attributes at
   //        template specialization definition, not declaration.
   //QualType ReturnType;
   //
-  //if (const CXXConstructorDecl *Constructor = dyn_cast<CXXConstructorDecl>(D)) {
+  //if (const ParmVarDecl *Param = dyn_cast<ParmVarDecl>(D)) {
+  //  ReturnType = Param->getType();
+  //
+  //} else if (const CXXConstructorDecl *Constructor =
+  //             dyn_cast<CXXConstructorDecl>(D)) {
   //  ReturnType = Constructor->getThisType(S.getASTContext())->getPointeeType();
   //  
   //} else {

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=192932&r1=192931&r2=192932&view=diff
==============================================================================
--- cfe/trunk/test/SemaCXX/warn-consumed-analysis.cpp (original)
+++ cfe/trunk/test/SemaCXX/warn-consumed-analysis.cpp Thu Oct 17 17:53:04 2013
@@ -388,6 +388,24 @@ void testFunctionParam(ConsumableClass<i
   *param; // expected-warning {{invocation of method 'operator*' on object 'param' while it is in the 'consumed' state}}
 }
 
+void testParamReturnTypestateCallee(bool cond, ConsumableClass<int> &Param RETURN_TYPESTATE(unconsumed)) { // expected-warning {{parameter 'Param' not in expected state when the function returns: expected 'unconsumed', observed 'consumed'}}
+  
+  if (cond) {
+    Param.consume();
+    return; // expected-warning {{parameter 'Param' not in expected state when the function returns: expected 'unconsumed', observed 'consumed'}}
+  }
+  
+  Param.consume();
+}
+
+void testParamReturnTypestateCaller() {
+  ConsumableClass<int> var;
+  
+  testParamReturnTypestateCallee(true, var);
+  
+  *var;
+}
+
 void testCallingConventions() {
   ConsumableClass<int> var(42);
   

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=192932&r1=192931&r2=192932&view=diff
==============================================================================
--- cfe/trunk/test/SemaCXX/warn-consumed-parsing.cpp (original)
+++ cfe/trunk/test/SemaCXX/warn-consumed-parsing.cpp Thu Oct 17 17:53:04 2013
@@ -44,6 +44,8 @@ class CONSUMABLE(unknown) AttrTester1 {
 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}}
 
+void returnTypestateTester2(AttrTester1 &Param RETURN_TYPESTATE(unconsumed));
+
 class AttrTester2 {
   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()        SET_TYPESTATE(consumed); // expected-warning {{consumed analysis attribute is attached to member of class 'AttrTester2' which isn't marked as consumable}}





More information about the cfe-commits mailing list