r188206 - Patch by Chris Wailes <chris.wailes at gmail.com>.

DeLesley Hutchins delesley at google.com
Mon Aug 12 14:20:56 PDT 2013


Author: delesley
Date: Mon Aug 12 16:20:55 2013
New Revision: 188206

URL: http://llvm.org/viewvc/llvm-project?rev=188206&view=rev
Log:
Patch by Chris Wailes <chris.wailes at gmail.com>.
Reviewed by delesley, dblaikie.

Add the annotations and code needed to support a basic 'consumed' analysis.

Summary:
This new analysis is based on academic literature on linear types.  It tracks
the state of a value, either as unconsumed, consumed, or unknown.  Methods are
then annotated as CallableWhenUnconsumed, and when an annotated method is
called while the value is in the 'consumed' state a warning is issued.  A value
may be tested in the conditional statement of an if-statement; when this occurs
we know the state of the value in the different branches, and this information
is added to our analysis.  The code is still highly experimental, and the names
of annotations or the algorithm may be subject to change.

Added:
    cfe/trunk/include/clang/Analysis/Analyses/Consumed.h
    cfe/trunk/include/clang/Sema/ConsumedWarningsHandler.h
    cfe/trunk/lib/Analysis/Consumed.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
Modified:
    cfe/trunk/include/clang/Basic/Attr.td
    cfe/trunk/include/clang/Basic/DiagnosticGroups.td
    cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td
    cfe/trunk/include/clang/Sema/AnalysisBasedWarnings.h
    cfe/trunk/lib/Analysis/CMakeLists.txt
    cfe/trunk/lib/Sema/AnalysisBasedWarnings.cpp
    cfe/trunk/lib/Sema/SemaDeclAttr.cpp

Added: cfe/trunk/include/clang/Analysis/Analyses/Consumed.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Analysis/Analyses/Consumed.h?rev=188206&view=auto
==============================================================================
--- cfe/trunk/include/clang/Analysis/Analyses/Consumed.h (added)
+++ cfe/trunk/include/clang/Analysis/Analyses/Consumed.h Mon Aug 12 16:20:55 2013
@@ -0,0 +1,140 @@
+//===- Consumed.h ----------------------------------------------*- C++ --*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// A intra-procedural analysis for checking consumed properties.  This is based,
+// in part, on research on linear types.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_CONSUMED_H
+#define LLVM_CLANG_CONSUMED_H
+
+#include "clang/AST/DeclCXX.h"
+#include "clang/AST/ExprCXX.h"
+#include "clang/AST/StmtCXX.h"
+#include "clang/Analysis/AnalysisContext.h"
+#include "clang/Analysis/Analyses/PostOrderCFGView.h"
+#include "clang/Basic/SourceLocation.h"
+#include "clang/Sema/ConsumedWarningsHandler.h"
+#include "clang/Sema/Sema.h"
+
+namespace clang {
+namespace consumed {
+  
+  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;
+    typedef std::pair<const VarDecl *, ConsumedState> PairType;
+    
+  protected:
+    
+    MapType Map;
+    
+  public:
+    /// \brief Get the consumed state of a given variable.
+    ConsumedState getState(const VarDecl *Var);
+    
+    /// \brief Merge this state map with another map.
+    void intersect(const ConsumedStateMap *Other);
+    
+    /// \brief Mark all variables as unknown.
+    void makeUnknown();
+    
+    /// \brief Set the consumed state of a given variable.
+    void setState(const VarDecl *Var, ConsumedState State);
+  };
+  
+  class ConsumedBlockInfo {
+    
+    ConsumedStateMap **StateMapsArray;
+    PostOrderCFGView::CFGBlockSet VisitedBlocks;
+    
+  public:
+    
+    ConsumedBlockInfo() : StateMapsArray(NULL) {}
+    
+    ConsumedBlockInfo(const CFG *CFGraph)
+      : StateMapsArray(new ConsumedStateMap*[CFGraph->getNumBlockIDs()]()),
+        VisitedBlocks(CFGraph) {}
+    
+    void addInfo(const CFGBlock *Block, ConsumedStateMap *StateMap,
+                 bool &AlreadyOwned);
+    void addInfo(const CFGBlock *Block, ConsumedStateMap *StateMap);
+    
+    ConsumedStateMap* getInfo(const CFGBlock *Block);
+    
+    void markVisited(const CFGBlock *Block);
+  };
+  
+  struct VarTestResult {
+    const VarDecl *Var;
+    SourceLocation Loc;
+    bool UnconsumedInTrueBranch;
+    
+    VarTestResult() : Var(NULL), Loc(), UnconsumedInTrueBranch(true) {}
+    
+    VarTestResult(const VarDecl *Var, SourceLocation Loc,
+                  bool UnconsumedInTrueBranch)
+      : Var(Var), Loc(Loc), UnconsumedInTrueBranch(UnconsumedInTrueBranch) {}
+  };
+
+  /// A class that handles the analysis of uniqueness violations.
+  class ConsumedAnalyzer {
+    
+    typedef llvm::DenseMap<const CXXRecordDecl *, bool> CacheMapType;
+    typedef std::pair<const CXXRecordDecl *, bool> CachePairType;
+    
+    Sema &S;
+    
+    ConsumedBlockInfo BlockInfo;
+    ConsumedStateMap *CurrStates;
+    
+    CacheMapType ConsumableTypeCache;
+    
+    bool hasConsumableAttributes(const CXXRecordDecl *RD);
+    void splitState(const CFGBlock *CurrBlock, const IfStmt *Terminator);
+    
+  public:
+    
+    ConsumedWarningsHandlerBase &WarningsHandler;
+    
+    ConsumedAnalyzer(Sema &S, ConsumedWarningsHandlerBase &WarningsHandler)
+        : S(S), WarningsHandler(WarningsHandler) {}
+    
+    /// \brief Get a constant reference to the Sema object.
+    const Sema & getSema(void);
+    
+    /// \brief Check to see if the type is a consumable type.
+    bool isConsumableType(QualType Type);
+    
+    /// \brief Check a function's CFG for consumed violations.
+    ///
+    /// We traverse the blocks in the CFG, keeping track of the state of each
+    /// value who's type has uniquness annotations.  If methods are invoked in
+    /// the wrong state a warning is issued.  Each block in the CFG is traversed
+    /// exactly once.
+    void run(AnalysisDeclContext &AC);
+  };
+  
+  unsigned checkEnabled(DiagnosticsEngine &D);
+  /// \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=188206&r1=188205&r2=188206&view=diff
==============================================================================
--- cfe/trunk/include/clang/Basic/Attr.td (original)
+++ cfe/trunk/include/clang/Basic/Attr.td Mon Aug 12 16:20:55 2013
@@ -918,6 +918,28 @@ def SharedLocksRequired : InheritableAtt
   let TemplateDependent = 1;
 }
 
+// C/C++ consumed attributes.
+
+def CallableWhenUnconsumed : InheritableAttr {
+  let Spellings = [GNU<"callable_when_unconsumed">];
+  let Subjects = [CXXMethod];
+}
+
+def TestsUnconsumed : InheritableAttr {
+  let Spellings = [GNU<"tests_unconsumed">];
+  let Subjects = [CXXMethod];
+}
+
+def Consumes : InheritableAttr {
+  let Spellings = [GNU<"consumes">];
+  let Subjects = [CXXMethod];
+}
+
+def TestsConsumed : InheritableAttr {
+  let Spellings = [GNU<"tests_consumed">];
+  let Subjects = [CXXMethod];
+}
+
 // Type safety attributes for `void *' pointers and type tags.
 
 def ArgumentWithTypeTag : InheritableAttr {

Modified: cfe/trunk/include/clang/Basic/DiagnosticGroups.td
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Basic/DiagnosticGroups.td?rev=188206&r1=188205&r2=188206&view=diff
==============================================================================
--- cfe/trunk/include/clang/Basic/DiagnosticGroups.td (original)
+++ cfe/trunk/include/clang/Basic/DiagnosticGroups.td Mon Aug 12 16:20:55 2013
@@ -478,6 +478,10 @@ def ThreadSafety : DiagGroup<"thread-saf
                               ThreadSafetyPrecise]>;
 def ThreadSafetyBeta : DiagGroup<"thread-safety-beta">;
 
+// Uniqueness Analysis warnings
+def Consumed       : DiagGroup<"consumed">;
+def ConsumedStrict : DiagGroup<"consumed-strict", [Consumed]>;
+
 // Note that putting warnings in -Wall will not disable them by default. If a
 // warning should be active _only_ when -Wall is passed in, mark it as
 // DefaultIgnore in addition to putting it here.

Modified: cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td?rev=188206&r1=188205&r2=188206&view=diff
==============================================================================
--- cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td (original)
+++ cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td Mon Aug 12 16:20:55 2013
@@ -2178,6 +2178,28 @@ def note_found_mutex_near_match : Note<"
 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' "
+  "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_uniqueness_attribute_wrong_decl_type : Warning<
+  "%0 attribute only applies to methods">,
+  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;
+
 def warn_impcast_vector_scalar : Warning<
   "implicit conversion turns vector to scalar: %0 to %1">,
   InGroup<Conversion>, DefaultIgnore;

Modified: cfe/trunk/include/clang/Sema/AnalysisBasedWarnings.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Sema/AnalysisBasedWarnings.h?rev=188206&r1=188205&r2=188206&view=diff
==============================================================================
--- cfe/trunk/include/clang/Sema/AnalysisBasedWarnings.h (original)
+++ cfe/trunk/include/clang/Sema/AnalysisBasedWarnings.h Mon Aug 12 16:20:55 2013
@@ -38,6 +38,7 @@ public:
     unsigned enableCheckFallThrough : 1;
     unsigned enableCheckUnreachable : 1;
     unsigned enableThreadSafetyAnalysis : 1;
+    unsigned enableConsumedAnalysis : 1;
   public:
     Policy();
     void disableCheckFallThrough() { enableCheckFallThrough = 0; }

Added: cfe/trunk/include/clang/Sema/ConsumedWarningsHandler.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Sema/ConsumedWarningsHandler.h?rev=188206&view=auto
==============================================================================
--- cfe/trunk/include/clang/Sema/ConsumedWarningsHandler.h (added)
+++ cfe/trunk/include/clang/Sema/ConsumedWarningsHandler.h Mon Aug 12 16:20:55 2013
@@ -0,0 +1,98 @@
+//===- ConsumedWarningsHandler.h -------------------------------*- C++ --*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// A handler class for warnings issued by the consumed analysis.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_CONSUMED_WARNING_HANDLER_H
+#define LLVM_CLANG_CONSUMED_WARNING_HANDLER_H
+
+#include <list>
+#include <utility>
+
+#include "clang/Basic/SourceLocation.h"
+#include "clang/Sema/Sema.h"
+#include "llvm/ADT/SmallVector.h"
+#include "llvm/ADT/StringRef.h"
+
+namespace clang {
+namespace consumed {
+
+  typedef SmallVector<PartialDiagnosticAt, 1> OptionalNotes;
+  typedef std::pair<PartialDiagnosticAt, OptionalNotes> DelayedDiag;
+  typedef std::list<DelayedDiag> DiagList;
+
+  class ConsumedWarningsHandlerBase {
+    
+  public:
+    
+    virtual ~ConsumedWarningsHandlerBase();
+    
+    /// \brief Emit the warnings and notes left by the analysis.
+    virtual void emitDiagnostics() {}
+    
+    /// Warn about unnecessary-test errors.
+    /// \param VariableName -- The name of the variable that holds the unique
+    /// value.
+    ///
+    /// \param Loc -- The SourceLocation of the unnecessary test.
+    virtual void warnUnnecessaryTest(StringRef VariableName,
+                                     StringRef VariableState,
+                                     SourceLocation Loc) {}
+    
+    /// 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 warnUseOfTempWhileConsumed(StringRef MethodName,
+                                            SourceLocation Loc) {}
+    
+    /// Warn about use-in-unknown-state 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 warnUseOfTempInUnknownState(StringRef MethodName,
+                                             SourceLocation Loc) {}
+    
+    /// 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) {}
+    
+    /// Warn about use-in-unknown-state 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 warnUseInUnknownState(StringRef MethodName,
+                                       StringRef VariableName,
+                                       SourceLocation Loc) {}
+  };
+}} // end clang::consumed
+
+#endif

Modified: cfe/trunk/lib/Analysis/CMakeLists.txt
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Analysis/CMakeLists.txt?rev=188206&r1=188205&r2=188206&view=diff
==============================================================================
--- cfe/trunk/lib/Analysis/CMakeLists.txt (original)
+++ cfe/trunk/lib/Analysis/CMakeLists.txt Mon Aug 12 16:20:55 2013
@@ -6,6 +6,7 @@ add_clang_library(clangAnalysis
   CFGStmtMap.cpp
   CallGraph.cpp
   CocoaConventions.cpp
+  Consumed.cpp
   Dominators.cpp
   FormatString.cpp
   LiveVariables.cpp

Added: cfe/trunk/lib/Analysis/Consumed.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Analysis/Consumed.cpp?rev=188206&view=auto
==============================================================================
--- cfe/trunk/lib/Analysis/Consumed.cpp (added)
+++ cfe/trunk/lib/Analysis/Consumed.cpp Mon Aug 12 16:20:55 2013
@@ -0,0 +1,802 @@
+//===- Consumed.cpp --------------------------------------------*- C++ --*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// A intra-procedural analysis for checking consumed properties.  This is based,
+// in part, on research on linear types.
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/AST/ASTContext.h"
+#include "clang/AST/Attr.h"
+#include "clang/AST/DeclCXX.h"
+#include "clang/AST/ExprCXX.h"
+#include "clang/AST/RecursiveASTVisitor.h"
+#include "clang/AST/StmtVisitor.h"
+#include "clang/AST/StmtCXX.h"
+#include "clang/AST/Type.h"
+#include "clang/Analysis/Analyses/PostOrderCFGView.h"
+#include "clang/Analysis/AnalysisContext.h"
+#include "clang/Analysis/CFG.h"
+#include "clang/Analysis/Analyses/Consumed.h"
+#include "clang/Basic/OperatorKinds.h"
+#include "clang/Basic/SourceLocation.h"
+#include "clang/Sema/ConsumedWarningsHandler.h"
+#include "clang/Sema/SemaDiagnostic.h"
+#include "llvm/ADT/DenseMap.h"
+#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
+//       transitions. (Deferred)
+// TODO: Take notes on state transitions to provide better warning messages.
+//       (Deferred)
+// TODO: Test nested conditionals: A) Checking the same value multiple times,
+//       and 2) Checking different values. (Deferred)
+// TODO: Test IsFalseVisitor with values in the unknown state. (Deferred)
+// TODO: Look into combining IsFalseVisitor and TestedVarsVisitor. (Deferred)
+
+using namespace clang;
+using namespace consumed;
+
+// Key method definition
+ConsumedWarningsHandlerBase::~ConsumedWarningsHandlerBase() {}
+
+static StringRef stateToString(ConsumedState State) {
+  switch (State) {
+  case consumed::CS_None:
+    return "none";
+  
+  case consumed::CS_Unknown:
+    return "unknown";
+  
+  case consumed::CS_Unconsumed:
+    return "unconsumed";
+  
+  case consumed::CS_Consumed:
+    return "consumed";
+  }
+}
+
+namespace {
+class ConsumedStmtVisitor : public ConstStmtVisitor<ConsumedStmtVisitor> {
+  
+  union PropagationUnion {
+    ConsumedState State;
+    const VarDecl *Var;
+  };
+  
+  class PropagationInfo {
+    PropagationUnion StateOrVar;
+  
+  public:
+    bool IsVar;
+    
+    PropagationInfo() : IsVar(false) {
+      StateOrVar.State = consumed::CS_None;
+    }
+    
+    PropagationInfo(ConsumedState State) : IsVar(false) {
+      StateOrVar.State = State;
+    }
+    
+    PropagationInfo(const VarDecl *Var) : IsVar(true) {
+      StateOrVar.Var = Var;
+    }
+    
+    ConsumedState getState() { return StateOrVar.State; };
+    
+    const VarDecl * getVar() { return IsVar ? StateOrVar.Var : NULL; };
+  };
+  
+  typedef llvm::DenseMap<const Stmt *, PropagationInfo> MapType;
+  typedef std::pair<const Stmt *, PropagationInfo> PairType;
+  typedef MapType::iterator InfoEntry;
+  
+  ConsumedAnalyzer &Analyzer;
+  ConsumedStateMap *StateMap;
+  MapType PropagationMap;
+  
+  void forwardInfo(const Stmt *From, const Stmt *To);
+  bool isLikeMoveAssignment(const CXXMethodDecl *MethodDecl);
+  
+public:
+  
+  void Visit(const Stmt *StmtNode);
+  
+  void VisitBinaryOperator(const BinaryOperator *BinOp);
+  void VisitCallExpr(const CallExpr *Call);
+  void VisitCastExpr(const CastExpr *Cast);
+  void VisitCXXConstructExpr(const CXXConstructExpr *Call);
+  void VisitCXXMemberCallExpr(const CXXMemberCallExpr *Call);
+  void VisitCXXOperatorCallExpr(const CXXOperatorCallExpr *Call);
+  void VisitDeclRefExpr(const DeclRefExpr *DeclRef);
+  void VisitDeclStmt(const DeclStmt *DelcS);
+  void VisitMaterializeTemporaryExpr(const MaterializeTemporaryExpr *Temp);
+  void VisitMemberExpr(const MemberExpr *MExpr);
+  void VisitUnaryOperator(const UnaryOperator *UOp);
+  void VisitVarDecl(const VarDecl *Var);
+  
+  ConsumedStmtVisitor(ConsumedAnalyzer &Analyzer, ConsumedStateMap *StateMap) :
+    Analyzer(Analyzer), StateMap(StateMap) {}
+  
+  void reset() {
+    PropagationMap.clear();
+  }
+};
+
+void ConsumedStmtVisitor::forwardInfo(const Stmt *From, const Stmt *To) {
+  InfoEntry Entry = PropagationMap.find(From);
+  
+  if (Entry != PropagationMap.end()) {
+    PropagationMap.insert(PairType(To, PropagationInfo(Entry->second)));
+  }
+}
+
+bool ConsumedStmtVisitor::isLikeMoveAssignment(
+  const CXXMethodDecl *MethodDecl) {
+  
+  return MethodDecl->isMoveAssignmentOperator() ||
+         (MethodDecl->getOverloadedOperator() == OO_Equal &&
+          MethodDecl->getNumParams() == 1 &&
+          MethodDecl->getParamDecl(0)->getType()->isRValueReferenceType());
+}
+
+void ConsumedStmtVisitor::VisitBinaryOperator(const BinaryOperator *BinOp) {
+  switch (BinOp->getOpcode()) {
+  case BO_PtrMemD:
+  case BO_PtrMemI:
+    forwardInfo(BinOp->getLHS(), BinOp);
+    break;
+    
+  default:
+    break;
+  }
+}
+
+void ConsumedStmtVisitor::Visit(const Stmt *StmtNode) {
+  ConstStmtVisitor::Visit(StmtNode);
+  
+  for (Stmt::const_child_iterator CI = StmtNode->child_begin(),
+       CE = StmtNode->child_end(); CI != CE; ++CI) {
+    
+    PropagationMap.erase(*CI);
+  }
+}
+
+void ConsumedStmtVisitor::VisitCallExpr(const CallExpr *Call) {
+  if (const FunctionDecl *FunDecl =
+    dyn_cast_or_null<FunctionDecl>(Call->getDirectCallee())) {
+    
+    // Special case for the std::move function.
+    // TODO: Make this more specific. (Deferred)
+    if (FunDecl->getNameAsString() == "move") {
+      InfoEntry Entry = PropagationMap.find(Call->getArg(0));
+      
+      if (Entry != PropagationMap.end()) {
+        PropagationMap.insert(PairType(Call, Entry->second));
+      }
+      
+      return;
+    }
+    
+    unsigned Offset = Call->getNumArgs() - FunDecl->getNumParams();
+    
+    for (unsigned Index = Offset; Index < Call->getNumArgs(); ++Index) {
+      QualType ParamType = FunDecl->getParamDecl(Index - Offset)->getType();
+      
+      InfoEntry Entry = PropagationMap.find(Call->getArg(Index));
+      
+      if (Entry == PropagationMap.end() || !Entry->second.IsVar) {
+        continue;
+      }
+      
+      PropagationInfo PState = Entry->second;
+      
+      if (ParamType->isRValueReferenceType() ||
+          (ParamType->isLValueReferenceType() &&
+           !cast<LValueReferenceType>(*ParamType).isSpelledAsLValue())) {
+        
+        StateMap->setState(PState.getVar(), consumed::CS_Consumed);
+        
+      } else if (!(ParamType.isConstQualified() ||
+                   ((ParamType->isReferenceType() ||
+                     ParamType->isPointerType()) &&
+                    ParamType->getPointeeType().isConstQualified()))) {
+        
+        StateMap->setState(PState.getVar(), consumed::CS_Unknown);
+      }
+    }
+  }
+}
+
+void ConsumedStmtVisitor::VisitCastExpr(const CastExpr *Cast) {
+  InfoEntry Entry = PropagationMap.find(Cast->getSubExpr());
+  
+  if (Entry != PropagationMap.end())
+    PropagationMap.insert(PairType(Cast, Entry->second));
+}
+
+void ConsumedStmtVisitor::VisitCXXConstructExpr(const CXXConstructExpr *Call) {
+  CXXConstructorDecl *Constructor = Call->getConstructor();
+  
+  ASTContext &CurrContext = Analyzer.getSema().getASTContext();
+  QualType ThisType = Constructor->getThisType(CurrContext)->getPointeeType();
+  
+  if (Analyzer.isConsumableType(ThisType)) {
+    if (Constructor->hasAttr<ConsumesAttr>() ||
+        Constructor->isDefaultConstructor()) {
+      
+      PropagationMap.insert(PairType(Call,
+        PropagationInfo(consumed::CS_Consumed)));
+      
+    } else if (Constructor->isMoveConstructor()) {
+      
+      PropagationInfo PState =
+        PropagationMap.find(Call->getArg(0))->second;
+      
+      if (PState.IsVar) {
+        const VarDecl* Var = PState.getVar();
+        
+        PropagationMap.insert(PairType(Call,
+          PropagationInfo(StateMap->getState(Var))));
+        
+        StateMap->setState(Var, consumed::CS_Consumed);
+        
+      } else {
+        PropagationMap.insert(PairType(Call, PState));
+      }
+        
+    } else if (Constructor->isCopyConstructor()) {
+      MapType::iterator Entry = PropagationMap.find(Call->getArg(0));
+    
+      if (Entry != PropagationMap.end())
+        PropagationMap.insert(PairType(Call, Entry->second));
+      
+    } else {
+      PropagationMap.insert(PairType(Call,
+        PropagationInfo(consumed::CS_Unconsumed)));
+    }
+  }
+}
+
+void ConsumedStmtVisitor::VisitCXXMemberCallExpr(
+  const CXXMemberCallExpr *Call) {
+  
+  VisitCallExpr(Call);
+  
+  InfoEntry Entry = PropagationMap.find(Call->getCallee()->IgnoreParens());
+  
+  if (Entry != PropagationMap.end()) {
+    PropagationInfo PState = Entry->second;
+    if (!PState.IsVar) return;
+    
+    const CXXMethodDecl *Method = Call->getMethodDecl();
+    
+    if (Method->hasAttr<ConsumesAttr>())
+      StateMap->setState(PState.getVar(), consumed::CS_Consumed);
+    else if (!Method->isConst())
+      StateMap->setState(PState.getVar(), consumed::CS_Unknown);
+  }
+}
+
+void ConsumedStmtVisitor::VisitCXXOperatorCallExpr(
+  const CXXOperatorCallExpr *Call) {
+  
+  const FunctionDecl *FunDecl =
+    dyn_cast_or_null<FunctionDecl>(Call->getDirectCallee());
+  
+  if (!FunDecl) return;
+    
+  if (isa<CXXMethodDecl>(FunDecl) &&
+      isLikeMoveAssignment(cast<CXXMethodDecl>(FunDecl))) {
+    
+    InfoEntry LEntry = PropagationMap.find(Call->getArg(0));
+    InfoEntry REntry = PropagationMap.find(Call->getArg(1));
+    
+    PropagationInfo LPState, RPState;
+    
+    if (LEntry != PropagationMap.end() &&
+        REntry != PropagationMap.end()) {
+      
+      LPState = LEntry->second;
+      RPState = REntry->second;
+      
+      if (LPState.IsVar && RPState.IsVar) {
+        StateMap->setState(LPState.getVar(),
+          StateMap->getState(RPState.getVar()));
+        
+        StateMap->setState(RPState.getVar(), consumed::CS_Consumed);
+        
+        PropagationMap.insert(PairType(Call, LPState));
+        
+      } else if (LPState.IsVar && !RPState.IsVar) {
+        StateMap->setState(LPState.getVar(), RPState.getState());
+        
+        PropagationMap.insert(PairType(Call, LPState));
+        
+      } else if (!LPState.IsVar && RPState.IsVar) {
+        PropagationMap.insert(PairType(Call,
+          PropagationInfo(StateMap->getState(RPState.getVar()))));
+        
+        StateMap->setState(RPState.getVar(), consumed::CS_Consumed);
+        
+      } else {
+        PropagationMap.insert(PairType(Call, RPState));
+      }
+      
+    } else if (LEntry != PropagationMap.end() &&
+               REntry == PropagationMap.end()) {
+      
+      LPState = LEntry->second;
+      
+      if (LPState.IsVar) {
+        StateMap->setState(LPState.getVar(), consumed::CS_Unknown);
+        
+        PropagationMap.insert(PairType(Call, LPState));
+        
+      } else {
+        PropagationMap.insert(PairType(Call,
+          PropagationInfo(consumed::CS_Unknown)));
+      }
+      
+    } else if (LEntry == PropagationMap.end() &&
+               REntry != PropagationMap.end()) {
+      
+      RPState = REntry->second;
+      
+      if (RPState.IsVar) {
+        const VarDecl *Var = RPState.getVar();
+        
+        PropagationMap.insert(PairType(Call,
+          PropagationInfo(StateMap->getState(Var))));
+        
+        StateMap->setState(Var, consumed::CS_Consumed);
+        
+      } else {
+        PropagationMap.insert(PairType(Call, RPState));
+      }
+    }
+    
+  } else {
+    
+    VisitCallExpr(Call);
+    
+    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;
+          
+          case CS_Unknown:
+            Analyzer.WarningsHandler.warnUseOfTempInUnknownState(
+              FunDecl->getNameAsString(), Call->getExprLoc());
+            break;
+            
+          default:
+            break;
+          }
+        }
+      }
+      
+      // 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);
+      }
+    }
+  }
+}
+
+void ConsumedStmtVisitor::VisitDeclRefExpr(const DeclRefExpr *DeclRef) {
+  if (const VarDecl *Var = dyn_cast_or_null<VarDecl>(DeclRef->getDecl()))
+    if (StateMap->getState(Var) != consumed::CS_None)
+      PropagationMap.insert(PairType(DeclRef, PropagationInfo(Var)));
+}
+
+void ConsumedStmtVisitor::VisitDeclStmt(const DeclStmt *DeclS) {
+  for (DeclStmt::const_decl_iterator DI = DeclS->decl_begin(),
+       DE = DeclS->decl_end(); DI != DE; ++DI) {
+    
+    if (isa<VarDecl>(*DI)) VisitVarDecl(cast<VarDecl>(*DI));
+  }
+  
+  if (DeclS->isSingleDecl())
+    if (const VarDecl *Var = dyn_cast_or_null<VarDecl>(DeclS->getSingleDecl()))
+      PropagationMap.insert(PairType(DeclS, PropagationInfo(Var)));
+}
+
+void ConsumedStmtVisitor::VisitMaterializeTemporaryExpr(
+  const MaterializeTemporaryExpr *Temp) {
+  
+  InfoEntry Entry = PropagationMap.find(Temp->GetTemporaryExpr());
+  
+  if (Entry != PropagationMap.end())
+    PropagationMap.insert(PairType(Temp, Entry->second));
+}
+
+void ConsumedStmtVisitor::VisitMemberExpr(const MemberExpr *MExpr) {
+  forwardInfo(MExpr->getBase(), MExpr);
+}
+
+void ConsumedStmtVisitor::VisitUnaryOperator(const UnaryOperator *UOp) {
+  if (UOp->getOpcode() == UO_AddrOf) {
+    InfoEntry Entry = PropagationMap.find(UOp->getSubExpr());
+    
+    if (Entry != PropagationMap.end())
+      PropagationMap.insert(PairType(UOp, Entry->second));
+  }
+}
+
+void ConsumedStmtVisitor::VisitVarDecl(const VarDecl *Var) {
+  if (Analyzer.isConsumableType(Var->getType())) {
+    PropagationInfo PState =
+      PropagationMap.find(Var->getInit())->second;
+    
+    StateMap->setState(Var, PState.IsVar ?
+      StateMap->getState(PState.getVar()) : PState.getState());
+  }
+}
+} // end anonymous::ConsumedStmtVisitor
+
+namespace {
+
+// TODO: Handle variable definitions, e.g. bool valid = x.isValid();
+//       if (valid) ...; (Deferred)
+class TestedVarsVisitor : public RecursiveASTVisitor<TestedVarsVisitor> {
+  
+  bool Invert;
+  SourceLocation CurrTestLoc;
+  
+  ConsumedStateMap *StateMap;
+  
+public:
+  bool IsUsefulConditional;
+  VarTestResult Test;
+  
+  TestedVarsVisitor(ConsumedStateMap *StateMap) : Invert(false),
+    StateMap(StateMap), IsUsefulConditional(false) {}
+  
+  bool VisitCallExpr(CallExpr *Call);
+  bool VisitDeclRefExpr(DeclRefExpr *DeclRef);
+  bool VisitUnaryOperator(UnaryOperator *UnaryOp);
+};
+
+bool TestedVarsVisitor::VisitCallExpr(CallExpr *Call) {
+  if (const CXXMethodDecl *Method =
+    dyn_cast_or_null<CXXMethodDecl>(Call->getDirectCallee())) {
+    
+    if (isTestingFunction(Method)) {
+      CurrTestLoc = Call->getExprLoc();
+      IsUsefulConditional = true;
+      return true;
+    }
+    
+    IsUsefulConditional = false;
+  }
+  
+  return false;
+}
+
+bool TestedVarsVisitor::VisitDeclRefExpr(DeclRefExpr *DeclRef) {
+  if (const VarDecl *Var = dyn_cast_or_null<VarDecl>(DeclRef->getDecl())) {
+    if (StateMap->getState(Var) != consumed::CS_None) {
+      Test = VarTestResult(Var, CurrTestLoc, !Invert);
+    }
+    
+  } else {
+    IsUsefulConditional = false;
+  }
+  
+  return IsUsefulConditional;
+}
+
+bool TestedVarsVisitor::VisitUnaryOperator(UnaryOperator *UnaryOp) {
+  if (UnaryOp->getOpcode() == UO_LNot) {
+    Invert = true;
+    TraverseStmt(UnaryOp->getSubExpr());
+    
+  } else {
+    IsUsefulConditional = false;
+  }
+  
+  return false;
+}
+} // end anonymouse::TestedVarsVisitor
+
+namespace clang {
+namespace consumed {
+
+void ConsumedBlockInfo::addInfo(const CFGBlock *Block,
+                                ConsumedStateMap *StateMap,
+                                bool &AlreadyOwned) {
+  
+  if (VisitedBlocks.alreadySet(Block)) return;
+  
+  ConsumedStateMap *Entry = StateMapsArray[Block->getBlockID()];
+    
+  if (Entry) {
+    Entry->intersect(StateMap);
+    
+  } else if (AlreadyOwned) {
+    StateMapsArray[Block->getBlockID()] = new ConsumedStateMap(*StateMap);
+    
+  } else {
+    StateMapsArray[Block->getBlockID()] = StateMap;
+    AlreadyOwned = true;
+  }
+}
+
+void ConsumedBlockInfo::addInfo(const CFGBlock *Block,
+                                ConsumedStateMap *StateMap) {
+  
+  if (VisitedBlocks.alreadySet(Block)) {
+    delete StateMap;
+    return;
+  }
+  
+  ConsumedStateMap *Entry = StateMapsArray[Block->getBlockID()];
+    
+  if (Entry) {
+    Entry->intersect(StateMap);
+    delete StateMap;
+    
+  } else {
+    StateMapsArray[Block->getBlockID()] = StateMap;
+  }
+}
+
+ConsumedStateMap* ConsumedBlockInfo::getInfo(const CFGBlock *Block) {
+  return StateMapsArray[Block->getBlockID()];
+}
+
+void ConsumedBlockInfo::markVisited(const CFGBlock *Block) {
+  VisitedBlocks.insert(Block);
+}
+
+ConsumedState ConsumedStateMap::getState(const VarDecl *Var) {
+  MapType::const_iterator Entry = Map.find(Var);
+  
+  if (Entry != Map.end()) {
+    return Entry->second;
+    
+  } else {
+    return CS_None;
+  }
+}
+
+void ConsumedStateMap::intersect(const ConsumedStateMap *Other) {
+  ConsumedState LocalState;
+  
+  for (MapType::const_iterator DMI = Other->Map.begin(),
+       DME = Other->Map.end(); DMI != DME; ++DMI) {
+    
+    LocalState = this->getState(DMI->first);
+    
+    if (LocalState != CS_None && LocalState != DMI->second)
+      setState(DMI->first, CS_Unknown);
+  }
+}
+
+void ConsumedStateMap::makeUnknown() {
+  PairType Pair;
+  
+  for (MapType::const_iterator DMI = Map.begin(), DME = Map.end(); DMI != DME;
+       ++DMI) {
+    
+    Pair = *DMI;
+    
+    Map.erase(Pair.first);
+    Map.insert(PairType(Pair.first, CS_Unknown));
+  }
+}
+
+void ConsumedStateMap::setState(const VarDecl *Var, ConsumedState State) {
+  Map[Var] = State;
+}
+
+const Sema & ConsumedAnalyzer::getSema() {
+  return S;
+}
+
+
+bool ConsumedAnalyzer::isConsumableType(QualType Type) {
+  const CXXRecordDecl *RD =
+    dyn_cast_or_null<CXXRecordDecl>(Type->getAsCXXRecordDecl());
+  
+  if (!RD) return false;
+  
+  std::pair<CacheMapType::iterator, bool> Entry =
+    ConsumableTypeCache.insert(std::make_pair(RD, false));
+  
+  if (Entry.second)
+    Entry.first->second = hasConsumableAttributes(RD);
+  
+  return Entry.first->second;
+}
+
+// TODO: Walk the base classes to see if any of them are unique types.
+//       (Deferred)
+bool ConsumedAnalyzer::hasConsumableAttributes(const CXXRecordDecl *RD) {
+  for (CXXRecordDecl::method_iterator MI = RD->method_begin(),
+       ME = RD->method_end(); MI != ME; ++MI) {
+    
+    for (Decl::attr_iterator AI = (*MI)->attr_begin(), AE = (*MI)->attr_end();
+         AI != AE; ++AI) {
+      
+      switch ((*AI)->getKind()) {
+      case attr::CallableWhenUnconsumed:
+      case attr::TestsUnconsumed:
+        return true;
+      
+      default:
+        break;
+      }
+    }
+  }
+  
+  return false;
+}
+
+// TODO: Handle other forms of branching with precision, including while- and
+//       for-loops. (Deferred)
+void ConsumedAnalyzer::splitState(const CFGBlock *CurrBlock,
+                                  const IfStmt *Terminator) {
+  
+  TestedVarsVisitor Visitor(CurrStates);
+  Visitor.TraverseStmt(const_cast<Expr*>(Terminator->getCond()));
+  
+  bool HasElse = Terminator->getElse() != NULL;
+  
+  ConsumedStateMap *ElseOrMergeStates = new ConsumedStateMap(*CurrStates);
+  
+  if (Visitor.IsUsefulConditional) {
+    ConsumedState VarState = CurrStates->getState(Visitor.Test.Var);
+    
+    if (VarState != CS_Unknown) {
+      // FIXME: Make this not warn if the test is from a macro expansion.
+      //        (Deferred)
+      WarningsHandler.warnUnnecessaryTest(Visitor.Test.Var->getNameAsString(),
+        stateToString(VarState), Visitor.Test.Loc);
+    }
+    
+    if (Visitor.Test.UnconsumedInTrueBranch) {
+      CurrStates->setState(Visitor.Test.Var, CS_Unconsumed);
+      if (HasElse) ElseOrMergeStates->setState(Visitor.Test.Var, CS_Consumed);
+      
+    } else {
+      CurrStates->setState(Visitor.Test.Var, CS_Consumed);
+      if (HasElse) ElseOrMergeStates->setState(Visitor.Test.Var, CS_Unconsumed);
+    }
+  }
+    
+  CFGBlock::const_succ_iterator SI = CurrBlock->succ_begin();
+  
+  if (*SI)   BlockInfo.addInfo(*SI,        CurrStates);
+  if (*++SI) BlockInfo.addInfo(*SI, ElseOrMergeStates);
+}
+
+void ConsumedAnalyzer::run(AnalysisDeclContext &AC) {
+  const FunctionDecl *D = dyn_cast_or_null<FunctionDecl>(AC.getDecl());
+  
+  if (!D) return;
+  
+  BlockInfo = ConsumedBlockInfo(AC.getCFG());
+  
+  PostOrderCFGView *SortedGraph = AC.getAnalysis<PostOrderCFGView>();
+  
+  CurrStates = new ConsumedStateMap();
+  
+  // Visit all of the function's basic blocks.
+  for (PostOrderCFGView::iterator I = SortedGraph->begin(),
+       E = SortedGraph->end(); I != E; ++I) {
+    
+    const CFGBlock *CurrBlock = *I;
+    BlockInfo.markVisited(CurrBlock);
+    
+    if (CurrStates == NULL)
+      CurrStates = BlockInfo.getInfo(CurrBlock);
+    
+    ConsumedStmtVisitor Visitor(*this, CurrStates);
+    
+    // Visit all of the basic block's statements.
+    for (CFGBlock::const_iterator BI = CurrBlock->begin(),
+         BE = CurrBlock->end(); BI != BE; ++BI) {
+      
+      if (BI->getKind() == CFGElement::Statement)
+        Visitor.Visit(BI->castAs<CFGStmt>().getStmt());
+    }
+    
+    // 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())) {
+      
+      splitState(CurrBlock, Terminator);
+      CurrStates = NULL;
+    
+    } else if (CurrBlock->succ_size() > 1) {
+      CurrStates->makeUnknown();
+      
+      bool OwnershipTaken = false;
+      
+      for (CFGBlock::const_succ_iterator SI = CurrBlock->succ_begin(),
+           SE = CurrBlock->succ_end(); SI != SE; ++SI) {
+        
+        if (*SI) BlockInfo.addInfo(*SI, CurrStates, OwnershipTaken);
+      }
+      
+      if (!OwnershipTaken)
+        delete CurrStates;
+      
+      CurrStates = NULL;
+      
+    } else if (CurrBlock->succ_size() == 1 &&
+               (*CurrBlock->succ_begin())->pred_size() > 1) {
+      
+      BlockInfo.addInfo(*CurrBlock->succ_begin(), CurrStates);
+      CurrStates = NULL;
+    }
+    
+    Visitor.reset();
+  } // End of block iterator.
+  
+  // Delete the last existing state map.
+  delete CurrStates;
+  
+  WarningsHandler.emitDiagnostics();
+}
+
+unsigned checkEnabled(DiagnosticsEngine &D) {
+  return (unsigned)
+    (D.getDiagnosticLevel(diag::warn_use_while_consumed, SourceLocation()) !=
+     DiagnosticsEngine::Ignored);
+}
+
+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=188206&r1=188205&r2=188206&view=diff
==============================================================================
--- cfe/trunk/lib/Sema/AnalysisBasedWarnings.cpp (original)
+++ cfe/trunk/lib/Sema/AnalysisBasedWarnings.cpp Mon Aug 12 16:20:55 2013
@@ -25,6 +25,7 @@
 #include "clang/AST/StmtObjC.h"
 #include "clang/AST/StmtVisitor.h"
 #include "clang/Analysis/Analyses/CFGReachabilityAnalysis.h"
+#include "clang/Analysis/Analyses/Consumed.h"
 #include "clang/Analysis/Analyses/ReachableCode.h"
 #include "clang/Analysis/Analyses/ThreadSafety.h"
 #include "clang/Analysis/Analyses/UninitializedValues.h"
@@ -1241,12 +1242,8 @@ private:
 };
 }
 
-
-//===----------------------------------------------------------------------===//
-// -Wthread-safety
-//===----------------------------------------------------------------------===//
 namespace clang {
-namespace thread_safety {
+namespace {
 typedef SmallVector<PartialDiagnosticAt, 1> OptionalNotes;
 typedef std::pair<PartialDiagnosticAt, OptionalNotes> DelayedDiag;
 typedef std::list<DelayedDiag> DiagList;
@@ -1261,7 +1258,13 @@ struct SortDiagBySourceLocation {
     return SM.isBeforeInTranslationUnit(left.first.first, right.first.first);
   }
 };
+}}
 
+//===----------------------------------------------------------------------===//
+// -Wthread-safety
+//===----------------------------------------------------------------------===//
+namespace clang {
+namespace thread_safety {
 namespace {
 class ThreadSafetyReporter : public clang::thread_safety::ThreadSafetyHandler {
   Sema &S;
@@ -1412,6 +1415,119 @@ class ThreadSafetyReporter : public clan
 }
 
 //===----------------------------------------------------------------------===//
+// -Wconsumed
+//===----------------------------------------------------------------------===//
+
+namespace clang {
+namespace consumed {
+namespace {
+class ConsumedWarningsHandler : public ConsumedWarningsHandlerBase {
+  
+  Sema &S;
+  DiagList Warnings;
+  
+public:
+  
+  ConsumedWarningsHandler(Sema &S) : S(S) {}
+  
+  void emitDiagnostics() {
+    Warnings.sort(SortDiagBySourceLocation(S.getSourceManager()));
+    
+    for (DiagList::iterator I = Warnings.begin(), E = Warnings.end();
+         I != E; ++I) {
+      
+      const OptionalNotes &Notes = I->second;
+      S.Diag(I->first.first, I->first.second);
+      
+      for (unsigned NoteI = 0, NoteN = Notes.size(); NoteI != NoteN; ++NoteI) {
+        S.Diag(Notes[NoteI].first, Notes[NoteI].second);
+      }
+    }
+  }
+  
+  /// Warn about unnecessary-test errors.
+  /// \param VariableName -- The name of the variable that holds the unique
+  /// value.
+  ///
+  /// \param Loc -- The SourceLocation of the unnecessary test.
+  void warnUnnecessaryTest(StringRef VariableName, StringRef VariableState,
+                           SourceLocation Loc) {
+
+    PartialDiagnosticAt Warning(Loc, S.PDiag(diag::warn_unnecessary_test) <<
+                                 VariableName << VariableState);
+    
+    Warnings.push_back(DelayedDiag(Warning, OptionalNotes()));
+  }
+  
+  /// 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.
+  void warnUseOfTempWhileConsumed(StringRef MethodName, SourceLocation Loc) {
+                                                    
+    PartialDiagnosticAt Warning(Loc, S.PDiag(
+      diag::warn_use_of_temp_while_consumed) << MethodName);
+    
+    Warnings.push_back(DelayedDiag(Warning, OptionalNotes()));
+  }
+  
+  /// Warn about use-in-unknown-state 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.
+  void warnUseOfTempInUnknownState(StringRef MethodName, SourceLocation Loc) {
+  
+    PartialDiagnosticAt Warning(Loc, S.PDiag(
+      diag::warn_use_of_temp_in_unknown_state) << MethodName);
+    
+    Warnings.push_back(DelayedDiag(Warning, OptionalNotes()));
+  }
+  
+  /// 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.
+  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()));
+  }
+  
+  /// Warn about use-in-unknown-state 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.
+  void warnUseInUnknownState(StringRef MethodName, StringRef VariableName,
+                             SourceLocation Loc) {
+
+    PartialDiagnosticAt Warning(Loc, S.PDiag(diag::warn_use_in_unknown_state) <<
+                                MethodName << VariableName);
+    
+    Warnings.push_back(DelayedDiag(Warning, OptionalNotes()));
+  }
+};
+}}}
+
+//===----------------------------------------------------------------------===//
 // AnalysisBasedWarnings - Worker object used by Sema to execute analysis-based
 //  warnings on a function, method, or block.
 //===----------------------------------------------------------------------===//
@@ -1420,6 +1536,7 @@ clang::sema::AnalysisBasedWarnings::Poli
   enableCheckFallThrough = 1;
   enableCheckUnreachable = 0;
   enableThreadSafetyAnalysis = 0;
+  enableConsumedAnalysis = 0;
 }
 
 clang::sema::AnalysisBasedWarnings::AnalysisBasedWarnings(Sema &s)
@@ -1440,6 +1557,7 @@ clang::sema::AnalysisBasedWarnings::Anal
   DefaultPolicy.enableThreadSafetyAnalysis = (unsigned)
     (D.getDiagnosticLevel(diag::warn_double_lock, SourceLocation()) !=
      DiagnosticsEngine::Ignored);
+  DefaultPolicy.enableConsumedAnalysis = consumed::checkEnabled(D);
 
 }
 
@@ -1501,7 +1619,8 @@ AnalysisBasedWarnings::IssueWarnings(sem
   // prototyping, but we need a way for analyses to say what expressions they
   // expect to always be CFGElements and then fill in the BuildOptions
   // appropriately.  This is essentially a layering violation.
-  if (P.enableCheckUnreachable || P.enableThreadSafetyAnalysis) {
+  if (P.enableCheckUnreachable || P.enableThreadSafetyAnalysis ||
+      P.enableConsumedAnalysis) {
     // Unreachable code analysis and thread safety require a linearized CFG.
     AC.getCFGBuildOptions().setAllAlwaysAdd();
   }
@@ -1605,6 +1724,13 @@ AnalysisBasedWarnings::IssueWarnings(sem
     Reporter.emitDiagnostics();
   }
 
+  // Check for violations of consumed properties.
+  if (P.enableConsumedAnalysis) {
+    consumed::ConsumedWarningsHandler WarningHandler(S);
+    consumed::ConsumedAnalyzer Analyzer(S, WarningHandler);
+    Analyzer.run(AC);
+  }
+
   if (Diags.getDiagnosticLevel(diag::warn_uninit_var, D->getLocStart())
       != DiagnosticsEngine::Ignored ||
       Diags.getDiagnosticLevel(diag::warn_sometimes_uninit_var,D->getLocStart())

Modified: cfe/trunk/lib/Sema/SemaDeclAttr.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaDeclAttr.cpp?rev=188206&r1=188205&r2=188206&view=diff
==============================================================================
--- cfe/trunk/lib/Sema/SemaDeclAttr.cpp (original)
+++ cfe/trunk/lib/Sema/SemaDeclAttr.cpp Mon Aug 12 16:20:55 2013
@@ -997,6 +997,69 @@ static void handleLocksExcludedAttr(Sema
                                Attr.getAttributeSpellingListIndex()));
 }
 
+static void handleConsumesAttr(Sema &S, Decl *D,
+                               const AttributeList &Attr) {
+  assert(!Attr.isInvalid());
+  if (!checkAttributeNumArgs(S, Attr, 0)) return;
+
+  if (!(isa<CXXMethodDecl>(D) || isa<CXXConstructorDecl>(D))) {
+    S.Diag(Attr.getLoc(), diag::warn_uniqueness_attribute_wrong_decl_type) <<
+      Attr.getName();
+    return;
+  }
+  
+  D->addAttr(::new (S.Context)
+             ConsumesAttr(Attr.getRange(), S.Context,
+              Attr.getAttributeSpellingListIndex()));
+}
+
+static void handleCallableWhenUnconsumedAttr(Sema &S, Decl *D,
+                                             const AttributeList &Attr) {
+  assert(!Attr.isInvalid());
+  if (!checkAttributeNumArgs(S, Attr, 0)) return;
+
+  if (!isa<CXXMethodDecl>(D)) {
+    S.Diag(Attr.getLoc(), diag::warn_uniqueness_attribute_wrong_decl_type) <<
+      Attr.getName();
+    return;
+  }
+  
+  D->addAttr(::new (S.Context)
+             CallableWhenUnconsumedAttr(Attr.getRange(), S.Context,
+              Attr.getAttributeSpellingListIndex()));
+}
+
+static void handleTestsConsumedAttr(Sema &S, Decl *D,
+                                    const AttributeList &Attr) {
+  assert(!Attr.isInvalid());
+  if (!checkAttributeNumArgs(S, Attr, 0)) return;
+
+  if (!isa<CXXMethodDecl>(D)) {
+    S.Diag(Attr.getLoc(), diag::warn_uniqueness_attribute_wrong_decl_type) <<
+      Attr.getName();
+    return;
+  }
+  
+  D->addAttr(::new (S.Context)
+             TestsConsumedAttr(Attr.getRange(), S.Context,
+              Attr.getAttributeSpellingListIndex()));
+}
+
+static void handleTestsUnconsumedAttr(Sema &S, Decl *D,
+                                      const AttributeList &Attr) {
+  assert(!Attr.isInvalid());
+  if (!checkAttributeNumArgs(S, Attr, 0)) return;
+
+  if (!isa<CXXMethodDecl>(D)) {
+    S.Diag(Attr.getLoc(), diag::warn_uniqueness_attribute_wrong_decl_type) <<
+      Attr.getName();
+    return;
+  }
+  
+  D->addAttr(::new (S.Context)
+             TestsUnconsumedAttr(Attr.getRange(), S.Context,
+              Attr.getAttributeSpellingListIndex()));
+}
 
 static void handleExtVectorTypeAttr(Sema &S, Scope *scope, Decl *D,
                                     const AttributeList &Attr) {
@@ -4952,6 +5015,20 @@ static void ProcessInheritableDeclAttr(S
     handleAcquiredAfterAttr(S, D, Attr);
     break;
 
+  // Uniqueness analysis attributes.
+  case AttributeList::AT_Consumes:
+    handleConsumesAttr(S, D, Attr);
+    break;
+  case AttributeList::AT_CallableWhenUnconsumed:
+    handleCallableWhenUnconsumedAttr(S, D, Attr);
+    break;
+  case AttributeList::AT_TestsConsumed:
+    handleTestsConsumedAttr(S, D, Attr);
+    break;
+  case AttributeList::AT_TestsUnconsumed:
+    handleTestsUnconsumedAttr(S, D, Attr);
+    break;
+
   // Type safety attributes.
   case AttributeList::AT_ArgumentWithTypeTag:
     handleArgumentWithTypeTagAttr(S, D, Attr);

Added: 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=188206&view=auto
==============================================================================
--- cfe/trunk/test/SemaCXX/warn-consumed-analysis-strict.cpp (added)
+++ cfe/trunk/test/SemaCXX/warn-consumed-analysis-strict.cpp Mon Aug 12 16:20:55 2013
@@ -0,0 +1,123 @@
+// RUN: %clang_cc1 -fsyntax-only -verify -Wconsumed-strict -std=c++11 %s
+
+#define CALLABLE_WHEN_UNCONSUMED __attribute__ ((callable_when_unconsumed))
+#define CONSUMES __attribute__ ((consumes))
+#define TESTS_UNCONSUMED __attribute__ ((tests_unconsumed))
+
+typedef decltype(nullptr) nullptr_t;
+
+template <typename T>
+class Bar {
+  T var;
+  
+  public:
+  Bar(void);
+  Bar(T val);
+  Bar(Bar<T> &other);
+  Bar(Bar<T> &&other);
+  
+  Bar<T>& operator=(Bar<T>  &other);
+  Bar<T>& operator=(Bar<T> &&other);
+  Bar<T>& operator=(nullptr_t);
+  
+  template <typename U>
+  Bar<T>& operator=(Bar<U>  &other);
+  
+  template <typename U>
+  Bar<T>& operator=(Bar<U> &&other);
+  
+  void operator*(void) const CALLABLE_WHEN_UNCONSUMED;
+  
+  bool isValid(void) const TESTS_UNCONSUMED;
+  
+  void constCall(void) const;
+  void nonconstCall(void);
+  
+  void consume(void) CONSUMES;
+};
+
+void baf0(Bar<int>  &var);
+void baf1(Bar<int>  *var);
+
+void testIfStmt(void) {
+  Bar<int> var;
+  
+  if (var.isValid()) { // expected-warning {{unnecessary test. Variable 'var' is known to be in the 'consumed' state}}
+    
+    // Empty
+    
+  } else {
+    // Empty
+  }
+}
+
+void testConditionalMerge(void) {
+  Bar<int> var;
+  
+  if (var.isValid()) {// expected-warning {{unnecessary test. Variable 'var' is known to be in the 'consumed' state}}
+    
+    // Empty
+  }
+  
+  *var; // expected-warning {{invocation of method 'operator*' on object 'var' while it is in an unknown state}}
+  
+  if (var.isValid()) {
+    // Empty
+    
+  } else {
+    // Empty
+  }
+  
+  *var; // expected-warning {{invocation of method 'operator*' on object 'var' while it is in an unknown state}}
+}
+
+void testCallingConventions(void) {
+  Bar<int> var(42);
+  
+  baf0(var);  
+  *var; // expected-warning {{invocation of method 'operator*' on object 'var' while it is in an unknown state}}
+  
+  var = Bar<int>(42);
+  baf1(&var);  
+  *var; // expected-warning {{invocation of method 'operator*' on object 'var' while it is in an unknown state}}
+}
+
+void testMoveAsignmentish(void) {
+  Bar<int> var;
+  
+  var = nullptr;
+  *var; // expected-warning {{invocation of method 'operator*' on object 'var' while it is in an unknown state}}
+}
+
+void testConstAndNonConstMemberFunctions(void) {
+  Bar<int> var(42);
+  
+  var.constCall();
+  *var;
+  
+  var.nonconstCall();
+  *var; // expected-warning {{invocation of method 'operator*' on object 'var' while it is in an unknown state}}
+}
+
+void testSimpleForLoop(void) {
+  Bar<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(void) {
+  int i = 0;
+  
+  Bar<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}}
+}

Added: cfe/trunk/test/SemaCXX/warn-consumed-analysis.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/SemaCXX/warn-consumed-analysis.cpp?rev=188206&view=auto
==============================================================================
--- cfe/trunk/test/SemaCXX/warn-consumed-analysis.cpp (added)
+++ cfe/trunk/test/SemaCXX/warn-consumed-analysis.cpp Mon Aug 12 16:20:55 2013
@@ -0,0 +1,188 @@
+// RUN: %clang_cc1 -fsyntax-only -verify -Wconsumed -std=c++11 %s
+
+#define CALLABLE_WHEN_UNCONSUMED __attribute__ ((callable_when_unconsumed))
+#define CONSUMES __attribute__ ((consumes))
+#define TESTS_UNCONSUMED __attribute__ ((tests_unconsumed))
+
+typedef decltype(nullptr) nullptr_t;
+
+template <typename T>
+class Bar {
+  T var;
+  
+  public:
+  Bar(void);
+  Bar(nullptr_t p) CONSUMES;
+  Bar(T val);
+  Bar(Bar<T> &other);
+  Bar(Bar<T> &&other);
+  
+  Bar<T>& operator=(Bar<T>  &other);
+  Bar<T>& operator=(Bar<T> &&other);
+  Bar<T>& operator=(nullptr_t);
+  
+  template <typename U>
+  Bar<T>& operator=(Bar<U>  &other);
+  
+  template <typename U>
+  Bar<T>& operator=(Bar<U> &&other);
+  
+  void operator*(void) const CALLABLE_WHEN_UNCONSUMED;
+  
+  bool isValid(void) const TESTS_UNCONSUMED;
+  operator bool() const TESTS_UNCONSUMED;
+  
+  void constCall(void) const;
+  void nonconstCall(void);
+  
+  void consume(void) CONSUMES;
+};
+
+void baf0(const Bar<int>  var);
+void baf1(const Bar<int> &var);
+void baf2(const Bar<int> *var);
+
+void baf3(Bar<int> &&var);
+
+void testInitialization(void) {
+  Bar<int> var0;
+  Bar<int> var1 = Bar<int>();
+  
+  var0 = Bar<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}}
+  
+  if (var0.isValid()) {
+    *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 the 'consumed' state}}
+  }
+}
+
+void testSimpleRValueRefs(void) {
+  Bar<int> var0;
+  Bar<int> var1(42);
+  
+  *var0; // expected-warning {{invocation of method 'operator*' on object 'var0' while it is in the 'consumed' state}}
+  *var1;
+  
+  var0 = static_cast<Bar<int>&&>(var1);
+  
+  *var0;
+  *var1; // expected-warning {{invocation of method 'operator*' on object 'var1' while it is in the 'consumed' state}}
+}
+
+void testIfStmt(void) {
+  Bar<int> var;
+  
+  if (var.isValid()) {
+    // Empty
+    
+  } else {
+    *var; // expected-warning {{invocation of method 'operator*' on object 'var' while it is in the 'consumed' state}}
+  }
+  
+  if (!var.isValid()) {
+    *var; // expected-warning {{invocation of method 'operator*' on object 'var' while it is in the 'consumed' state}}
+    
+  } else {
+    *var;
+  }
+  
+  if (var) {
+    // Empty
+    
+  } else {
+    *var; // expected-warning {{invocation of method 'operator*' on object 'var' while it is in the 'consumed' state}}
+  }
+}
+
+void testCallingConventions(void) {
+  Bar<int> var(42);
+  
+  baf0(var);  
+  *var;
+  
+  baf1(var);  
+  *var;
+  
+  baf2(&var);  
+  *var;
+  
+  baf3(static_cast<Bar<int>&&>(var));  
+  *var; // expected-warning {{invocation of method 'operator*' on object 'var' while it is in the 'consumed' state}}
+}
+
+void testMoveAsignmentish(void) {
+  Bar<int>  var0;
+  Bar<long> var1(42);
+  
+  *var0; // expected-warning {{invocation of method 'operator*' on object 'var0' while it is in the 'consumed' state}}
+  *var1;
+  
+  var0 = static_cast<Bar<long>&&>(var1);
+  
+  *var0;
+  *var1; // expected-warning {{invocation of method 'operator*' on object 'var1' while it is in the 'consumed' state}}
+}
+
+void testConditionalMerge(void) {
+  Bar<int> var;
+  
+  if (var.isValid()) {
+    // Empty
+  }
+  
+  *var;
+  
+  if (var.isValid()) {
+    // Empty
+    
+  } else {
+    // Empty
+  }
+  
+  *var;
+}
+
+void testConsumes0(void) {
+  Bar<int> var(42);
+  
+  *var;
+  
+  var.consume();
+  
+  *var; // expected-warning {{invocation of method 'operator*' on object 'var' while it is in the 'consumed' state}}
+}
+
+void testConsumes1(void) {
+  Bar<int> var(nullptr);
+  
+  *var; // expected-warning {{invocation of method 'operator*' on object 'var' while it is in the 'consumed' state}}
+}
+
+void testSimpleForLoop(void) {
+  Bar<int> var;
+  
+  for (int i = 0; i < 10; ++i) {
+    *var;
+  }
+  
+  *var;
+}
+
+void testSimpleWhileLoop(void) {
+  int i = 0;
+  
+  Bar<int> var;
+  
+  while (i < 10) {
+    *var;
+    ++i;
+  }
+  
+  *var;
+}

Added: cfe/trunk/test/SemaCXX/warn-consumed-parsing.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/SemaCXX/warn-consumed-parsing.cpp?rev=188206&view=auto
==============================================================================
--- cfe/trunk/test/SemaCXX/warn-consumed-parsing.cpp (added)
+++ cfe/trunk/test/SemaCXX/warn-consumed-parsing.cpp Mon Aug 12 16:20:55 2013
@@ -0,0 +1,29 @@
+// RUN: %clang_cc1 -fsyntax-only -verify -Wconsumed -std=c++11 %s
+
+#define CONSUMES                  __attribute__ ((consumes))
+#define TESTS_UNCONSUMED          __attribute__ ((tests_unconsumed))
+#define CALLABLE_WHEN_UNCONSUMED  __attribute__ ((callable_when_unconsumed))
+
+class AttrTester0 {
+  void Consumes(void)        __attribute__ ((consumes(42))); // expected-error {{attribute takes no arguments}}
+  bool TestsUnconsumed(void) __attribute__ ((tests_unconsumed(42))); // expected-error {{attribute takes no arguments}}
+  void CallableWhenUnconsumed(void) 
+    __attribute__ ((callable_when_unconsumed(42))); // expected-error {{attribute takes no arguments}}
+};
+
+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}}
+
+void function0(void) CONSUMES; // expected-warning {{'consumes' attribute only applies to methods}}
+void function1(void) TESTS_UNCONSUMED; // expected-warning {{'tests_unconsumed' attribute only applies to methods}}
+void function2(void) CALLABLE_WHEN_UNCONSUMED; // expected-warning {{'callable_when_unconsumed' attribute only applies to methods}}
+
+class AttrTester1 {
+  void consumes(void)        CONSUMES;
+  bool testsUnconsumed(void) TESTS_UNCONSUMED;
+};
+
+class AttrTester2 {
+  void callableWhenUnconsumed(void) CALLABLE_WHEN_UNCONSUMED;
+};





More information about the cfe-commits mailing list