r189059 - Update to consumed analysis.

DeLesley Hutchins delesley at google.com
Thu Aug 22 13:44:47 PDT 2013


Author: delesley
Date: Thu Aug 22 15:44:47 2013
New Revision: 189059

URL: http://llvm.org/viewvc/llvm-project?rev=189059&view=rev
Log:
Update to consumed analysis.

Patch by chris.wailes at gmail.com.  The following functionality was added:

* The same functionality is now supported for both CXXOperatorCallExprs and CXXMemberCallExprs.
* Factored out some code in StmtVisitor.
* Removed variables from the state map when their destructors are encountered.
* Started adding documentation for the consumed analysis attributes.

Modified:
    cfe/trunk/docs/LanguageExtensions.rst
    cfe/trunk/include/clang/Analysis/Analyses/Consumed.h
    cfe/trunk/include/clang/Basic/Attr.td
    cfe/trunk/lib/Analysis/Consumed.cpp
    cfe/trunk/lib/Sema/AnalysisBasedWarnings.cpp
    cfe/trunk/test/SemaCXX/warn-consumed-analysis.cpp

Modified: cfe/trunk/docs/LanguageExtensions.rst
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/docs/LanguageExtensions.rst?rev=189059&r1=189058&r2=189059&view=diff
==============================================================================
--- cfe/trunk/docs/LanguageExtensions.rst (original)
+++ cfe/trunk/docs/LanguageExtensions.rst Thu Aug 22 15:44:47 2013
@@ -2022,6 +2022,33 @@ to specify that the function must be cal
 locks.  Arguments must be lockable type, and there must be at least one
 argument.
 
+Consumed Annotation Checking
+============================
+
+Clang supports additional attributes for checking basic resource management
+properties, specifically for unique objects that have a single owning reference.
+The following attributes are currently supported, although **the implementation
+for these annotations is currently in development and are subject to change.**
+
+``consumes``
+------------
+
+Use ``__attribute__((consumes))`` on a method that transitions an object into
+the consumed state.
+
+``callable_when_unconsumed``
+----------------------------
+
+Use ``__attribute__((callable_when_unconsumed))`` to indicate that a method may
+only be called when the object is not in the consumed state.
+
+``tests_unconsumed``
+--------------------
+
+Use `__attribute__((tests_unconsumed))`` to indicate that a method returns true
+if the object is in the unconsumed state.
+
+
 Type Safety Checking
 ====================
 

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=189059&r1=189058&r2=189059&view=diff
==============================================================================
--- cfe/trunk/include/clang/Analysis/Analyses/Consumed.h (original)
+++ cfe/trunk/include/clang/Analysis/Analyses/Consumed.h Thu Aug 22 15:44:47 2013
@@ -118,6 +118,9 @@ namespace consumed {
     
     /// \brief Set the consumed state of a given variable.
     void setState(const VarDecl *Var, ConsumedState State);
+    
+    /// \brief Remove the variable from our state map.
+    void remove(const VarDecl *Var);
   };
   
   class ConsumedBlockInfo {
@@ -186,10 +189,6 @@ namespace consumed {
     /// exactly once.
     void run(AnalysisDeclContext &AC);
   };
-  
-  /// \brief Check to see if a function tests an object's validity.
-  bool isTestingFunction(const CXXMethodDecl *MethodDecl);
-  
 }} // end namespace clang::consumed
 
 #endif

Modified: cfe/trunk/include/clang/Basic/Attr.td
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Basic/Attr.td?rev=189059&r1=189058&r2=189059&view=diff
==============================================================================
--- cfe/trunk/include/clang/Basic/Attr.td (original)
+++ cfe/trunk/include/clang/Basic/Attr.td Thu Aug 22 15:44:47 2013
@@ -932,7 +932,7 @@ def TestsUnconsumed : InheritableAttr {
 
 def Consumes : InheritableAttr {
   let Spellings = [GNU<"consumes">];
-  let Subjects = [CXXMethod];
+  let Subjects = [CXXMethod, CXXConstructor];
 }
 
 def TestsConsumed : InheritableAttr {

Modified: cfe/trunk/lib/Analysis/Consumed.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Analysis/Consumed.cpp?rev=189059&r1=189058&r2=189059&view=diff
==============================================================================
--- cfe/trunk/lib/Analysis/Consumed.cpp (original)
+++ cfe/trunk/lib/Analysis/Consumed.cpp Thu Aug 22 15:44:47 2013
@@ -30,7 +30,6 @@
 #include "llvm/ADT/SmallVector.h"
 #include "llvm/Support/raw_ostream.h"
 
-// TODO: Add support for methods with CallableWhenUnconsumed.
 // TODO: Mark variables as Unknown going into while- or for-loops only if they
 //       are referenced inside that block. (Deferred)
 // TODO: Add a method(s) to identify which method calls perform what state
@@ -48,6 +47,10 @@ using namespace consumed;
 // Key method definition
 ConsumedWarningsHandlerBase::~ConsumedWarningsHandlerBase() {}
 
+static bool isTestingFunction(const FunctionDecl *FunDecl) {
+  return FunDecl->hasAttr<TestsUnconsumedAttr>();
+}
+
 static StringRef stateToString(ConsumedState State) {
   switch (State) {
   case consumed::CS_None:
@@ -91,9 +94,9 @@ class ConsumedStmtVisitor : public Const
       StateOrVar.Var = Var;
     }
     
-    ConsumedState getState() { return StateOrVar.State; };
+    ConsumedState getState() const { return StateOrVar.State; }
     
-    const VarDecl * getVar() { return IsVar ? StateOrVar.Var : NULL; };
+    const VarDecl * getVar() const { return IsVar ? StateOrVar.Var : NULL; }
   };
   
   typedef llvm::DenseMap<const Stmt *, PropagationInfo> MapType;
@@ -105,6 +108,9 @@ class ConsumedStmtVisitor : public Const
   ConsumedStateMap *StateMap;
   MapType PropagationMap;
   
+  void checkCallability(const PropagationInfo &PState,
+                        const FunctionDecl *FunDecl,
+                        const CallExpr *Call);
   void forwardInfo(const Stmt *From, const Stmt *To);
   bool isLikeMoveAssignment(const CXXMethodDecl *MethodDecl);
   
@@ -134,6 +140,54 @@ 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 &PState,
+                                           const FunctionDecl *FunDecl,
+                                           const CallExpr *Call) {
+  
+  if (!FunDecl->hasAttr<CallableWhenUnconsumedAttr>()) return;
+  
+  if (PState.IsVar) {
+    const VarDecl *Var = PState.getVar();
+    
+    switch (StateMap->getState(Var)) {
+    case CS_Consumed:
+      Analyzer.WarningsHandler.warnUseWhileConsumed(
+        FunDecl->getNameAsString(), Var->getNameAsString(),
+        Call->getExprLoc());
+      break;
+    
+    case CS_Unknown:
+      Analyzer.WarningsHandler.warnUseInUnknownState(
+        FunDecl->getNameAsString(), Var->getNameAsString(),
+        Call->getExprLoc());
+      break;
+      
+    case CS_None:
+    case CS_Unconsumed:
+      break;
+    }
+    
+  } else {
+    switch (PState.getState()) {
+    case CS_Consumed:
+      Analyzer.WarningsHandler.warnUseOfTempWhileConsumed(
+        FunDecl->getNameAsString(), Call->getExprLoc());
+      break;
+    
+    case CS_Unknown:
+      Analyzer.WarningsHandler.warnUseOfTempInUnknownState(
+        FunDecl->getNameAsString(), Call->getExprLoc());
+      break;
+      
+    case CS_None:
+    case CS_Unconsumed:
+      break;
+    }
+  }
+}
+
 void ConsumedStmtVisitor::forwardInfo(const Stmt *From, const Stmt *To) {
   InfoEntry Entry = PropagationMap.find(From);
   
@@ -278,14 +332,16 @@ void ConsumedStmtVisitor::VisitCXXMember
   
   if (Entry != PropagationMap.end()) {
     PropagationInfo PState = Entry->second;
-    if (!PState.IsVar) return;
+    const CXXMethodDecl *MethodDecl = Call->getMethodDecl();
     
-    const CXXMethodDecl *Method = Call->getMethodDecl();
+    checkCallability(PState, MethodDecl, Call);
     
-    if (Method->hasAttr<ConsumesAttr>())
-      StateMap->setState(PState.getVar(), consumed::CS_Consumed);
-    else if (!Method->isConst())
-      StateMap->setState(PState.getVar(), consumed::CS_Unknown);
+    if (PState.IsVar) {
+      if (MethodDecl->hasAttr<ConsumesAttr>())
+        StateMap->setState(PState.getVar(), consumed::CS_Consumed);
+      else if (!MethodDecl->isConst())
+        StateMap->setState(PState.getVar(), consumed::CS_Unknown);
+    }
   }
 }
 
@@ -374,58 +430,22 @@ void ConsumedStmtVisitor::VisitCXXOperat
     InfoEntry Entry = PropagationMap.find(Call->getArg(0));
     
     if (Entry != PropagationMap.end()) {
-      
       PropagationInfo PState = Entry->second;
       
-      // TODO: When we support CallableWhenConsumed this will have to check for
-      //       the different attributes and change the behavior bellow.
-      //       (Deferred)
-      if (FunDecl->hasAttr<CallableWhenUnconsumedAttr>()) {
-        if (PState.IsVar) {
-          const VarDecl *Var = PState.getVar();
-          
-          switch (StateMap->getState(Var)) {
-          case CS_Consumed:
-            Analyzer.WarningsHandler.warnUseWhileConsumed(
-              FunDecl->getNameAsString(), Var->getNameAsString(),
-              Call->getExprLoc());
-            break;
-          
-          case CS_Unknown:
-            Analyzer.WarningsHandler.warnUseInUnknownState(
-              FunDecl->getNameAsString(), Var->getNameAsString(),
-              Call->getExprLoc());
-            break;
-            
-          default:
-            break;
-          }
-          
-        } else {
-          switch (PState.getState()) {
-          case CS_Consumed:
-            Analyzer.WarningsHandler.warnUseOfTempWhileConsumed(
-              FunDecl->getNameAsString(), Call->getExprLoc());
-            break;
+      checkCallability(PState, FunDecl, Call);
+      
+      if (PState.IsVar) {
+        if (FunDecl->hasAttr<ConsumesAttr>()) {
+          // Handle consuming operators.
+          StateMap->setState(PState.getVar(), consumed::CS_Consumed);
+        } else if (const CXXMethodDecl *MethodDecl =
+          dyn_cast_or_null<CXXMethodDecl>(FunDecl)) {
           
-          case CS_Unknown:
-            Analyzer.WarningsHandler.warnUseOfTempInUnknownState(
-              FunDecl->getNameAsString(), Call->getExprLoc());
-            break;
-            
-          default:
-            break;
-          }
+          // Handle non-constant member operators.
+          if (!MethodDecl->isConst())
+            StateMap->setState(PState.getVar(), consumed::CS_Unknown);
         }
       }
-      
-      // Handle non-constant member operators.
-      if (const CXXMethodDecl *MethodDecl =
-        dyn_cast_or_null<CXXMethodDecl>(FunDecl)) {
-        
-        if (!MethodDecl->isConst() && PState.IsVar)
-          StateMap->setState(PState.getVar(), consumed::CS_Unknown);
-      }
     }
   }
 }
@@ -505,10 +525,10 @@ public:
 };
 
 bool TestedVarsVisitor::VisitCallExpr(CallExpr *Call) {
-  if (const CXXMethodDecl *Method =
-    dyn_cast_or_null<CXXMethodDecl>(Call->getDirectCallee())) {
+  if (const FunctionDecl *FunDecl =
+    dyn_cast_or_null<FunctionDecl>(Call->getDirectCallee())) {
     
-    if (isTestingFunction(Method)) {
+    if (isTestingFunction(FunDecl)) {
       CurrTestLoc = Call->getExprLoc();
       IsUsefulConditional = true;
       return true;
@@ -521,16 +541,11 @@ bool TestedVarsVisitor::VisitCallExpr(Ca
 }
 
 bool TestedVarsVisitor::VisitDeclRefExpr(DeclRefExpr *DeclRef) {
-  if (const VarDecl *Var = dyn_cast_or_null<VarDecl>(DeclRef->getDecl())) {
-    if (StateMap->getState(Var) != consumed::CS_None) {
+  if (const VarDecl *Var = dyn_cast_or_null<VarDecl>(DeclRef->getDecl()))
+    if (StateMap->getState(Var) != consumed::CS_None)
       Test = VarTestResult(Var, CurrTestLoc, !Invert);
-    }
-    
-  } else {
-    IsUsefulConditional = false;
-  }
   
-  return IsUsefulConditional;
+  return true;
 }
 
 bool TestedVarsVisitor::VisitUnaryOperator(UnaryOperator *UnaryOp) {
@@ -637,6 +652,9 @@ void ConsumedStateMap::setState(const Va
   Map[Var] = State;
 }
 
+void ConsumedStateMap::remove(const VarDecl *Var) {
+  Map.erase(Var);
+}
 
 bool ConsumedAnalyzer::isConsumableType(QualType Type) {
   const CXXRecordDecl *RD =
@@ -741,13 +759,17 @@ void ConsumedAnalyzer::run(AnalysisDeclC
     for (CFGBlock::const_iterator BI = CurrBlock->begin(),
          BE = CurrBlock->end(); BI != BE; ++BI) {
       
-      if (BI->getKind() == CFGElement::Statement)
+      switch (BI->getKind()) {
+      case CFGElement::Statement:
         Visitor.Visit(BI->castAs<CFGStmt>().getStmt());
+        break;
+      case CFGElement::AutomaticObjectDtor:
+        CurrStates->remove(BI->castAs<CFGAutomaticObjDtor>().getVarDecl());
+      default:
+        break;
+      }
     }
     
-    // TODO: Remove any variables that have reached the end of their
-    //       lifetimes from the state map. (Deferred)
-    
     if (const IfStmt *Terminator =
       dyn_cast_or_null<IfStmt>(CurrBlock->getTerminator().getStmt())) {
       
@@ -785,9 +807,4 @@ void ConsumedAnalyzer::run(AnalysisDeclC
   
   WarningsHandler.emitDiagnostics();
 }
-
-bool isTestingFunction(const CXXMethodDecl *Method) {
-  return Method->hasAttr<TestsUnconsumedAttr>();
-}
-
 }} // end namespace clang::consumed

Modified: cfe/trunk/lib/Sema/AnalysisBasedWarnings.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/AnalysisBasedWarnings.cpp?rev=189059&r1=189058&r2=189059&view=diff
==============================================================================
--- cfe/trunk/lib/Sema/AnalysisBasedWarnings.cpp (original)
+++ cfe/trunk/lib/Sema/AnalysisBasedWarnings.cpp Thu Aug 22 15:44:47 2013
@@ -1551,10 +1551,9 @@ clang::sema::AnalysisBasedWarnings::Anal
   DefaultPolicy.enableThreadSafetyAnalysis = (unsigned)
     (D.getDiagnosticLevel(diag::warn_double_lock, SourceLocation()) !=
      DiagnosticsEngine::Ignored);
-  DefaultPolicy.enableConsumedAnalysis =
-      (unsigned)(D.getDiagnosticLevel(diag::warn_use_while_consumed,
-                                      SourceLocation()) !=
-                 DiagnosticsEngine::Ignored);
+  DefaultPolicy.enableConsumedAnalysis = (unsigned)
+    (D.getDiagnosticLevel(diag::warn_use_while_consumed, SourceLocation()) !=
+     DiagnosticsEngine::Ignored);
 }
 
 static void flushDiagnostics(Sema &S, sema::FunctionScopeInfo *fscope) {

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=189059&r1=189058&r2=189059&view=diff
==============================================================================
--- cfe/trunk/test/SemaCXX/warn-consumed-analysis.cpp (original)
+++ cfe/trunk/test/SemaCXX/warn-consumed-analysis.cpp Thu Aug 22 15:44:47 2013
@@ -27,10 +27,13 @@ class Bar {
   template <typename U>
   Bar<T>& operator=(Bar<U> &&other);
   
+  void operator()(int a) CONSUMES;
   void operator*(void) const CALLABLE_WHEN_UNCONSUMED;
+  void unconsumedCall(void) const CALLABLE_WHEN_UNCONSUMED;
   
   bool isValid(void) const TESTS_UNCONSUMED;
   operator bool() const TESTS_UNCONSUMED;
+  bool operator!=(nullptr_t) const TESTS_UNCONSUMED;
   
   void constCall(void) const;
   void nonconstCall(void);
@@ -62,6 +65,10 @@ void testInitialization(void) {
   }
 }
 
+void testTempValue(void) {
+  *Bar<int>(); // expected-warning {{invocation of method 'operator*' on a temporary object while it is in the 'consumed' state}}
+}
+
 void testSimpleRValueRefs(void) {
   Bar<int> var0;
   Bar<int> var1(42);
@@ -98,6 +105,13 @@ void testIfStmt(void) {
   } else {
     *var; // expected-warning {{invocation of method 'operator*' on object 'var' while it is in the 'consumed' state}}
   }
+  
+  if (var != nullptr) {
+    // Empty
+    
+  } else {
+    *var; // expected-warning {{invocation of method 'operator*' on object 'var' while it is in the 'consumed' state}}
+  }
 }
 
 void testCallingConventions(void) {
@@ -164,6 +178,15 @@ void testConsumes1(void) {
   *var; // expected-warning {{invocation of method 'operator*' on object 'var' while it is in the 'consumed' state}}
 }
 
+void testConsumes2(void) {
+  Bar<int> var(42);
+  
+  var.unconsumedCall();
+  var(6);
+  
+  var.unconsumedCall(); // expected-warning {{invocation of method 'unconsumedCall' on object 'var' while it is in the 'consumed' state}}
+}
+
 void testSimpleForLoop(void) {
   Bar<int> var;
   





More information about the cfe-commits mailing list