Hi dblaikie,

This patch adds the beginnings of an analysis that tracks the "consumed" state 
of objects (this was previously mentioned on the list as uniqueness analysis).

The analysis currently provides enough functionality to detect when a class 
like std::unique_ptr is dereferenced after it is created with a default 
constructor.  It can also track the state of an object across the branches of 
an if-statement if the statement contains a single test of a tracked variable.  
Lastly, the state of a value that is passed as a RValue References to a 
function is correctly tracked.

Future work, to follow in the next couple of weeks, will include more precise 
handling of for- and while-loops, support for member functions that consume an 
object, and tracking of state across function boundaries.

http://llvm-reviews.chandlerc.com/D1233

Files:
  include/clang/Analysis/Analyses/Consumed.h
  include/clang/Basic/Attr.td
  include/clang/Basic/DiagnosticGroups.td
  include/clang/Basic/DiagnosticSemaKinds.td
  include/clang/Sema/AnalysisBasedWarnings.h
  lib/Analysis/CMakeLists.txt
  lib/Analysis/Consumed.cpp
  lib/Sema/AnalysisBasedWarnings.cpp
  lib/Sema/SemaDeclAttr.cpp
  test/SemaCXX/warn-consumed-analysis.cpp
  test/SemaCXX/warn-consumed-parsing.cpp
Index: include/clang/Analysis/Analyses/Consumed.h
===================================================================
--- /dev/null
+++ include/clang/Analysis/Analyses/Consumed.h
@@ -0,0 +1,218 @@
+//===- 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_UNIQUENESS_H
+#define LLVM_CLANG_UNIQUENESS_H
+
+#include "clang/AST/DeclCXX.h"
+#include "clang/AST/ExprCXX.h"
+#include "clang/AST/StmtCXX.h"
+#include "clang/Analysis/AnalysisContext.h"
+#include "clang/Basic/SourceLocation.h"
+#include "clang/Sema/Sema.h"
+#include "llvm/ADT/StringRef.h"
+
+namespace clang {
+namespace consumed {
+  
+  ///////////
+  // Types //
+  ///////////
+  
+  typedef SmallVector<PartialDiagnosticAt, 1> OptionalNotes;
+  typedef std::pair<PartialDiagnosticAt, OptionalNotes> DelayedDiag;
+  typedef std::list<DelayedDiag> DiagList;
+  
+  enum ConsumedState {
+    // No state information for the given variable.
+    None,
+    
+    Unknown,
+    Unconsumed,
+    Consumed
+  };
+  
+  /////////////////////////
+  // Structs and Classes //
+  /////////////////////////
+
+  class ConsumedStateMap {
+    
+    ///////////
+    // Types //
+    ///////////
+    
+    typedef llvm::DenseMap<const VarDecl*, ConsumedState> MapType;
+    typedef std::pair<const VarDecl*, ConsumedState> PairType;
+    
+  protected:
+    
+    //////////////////
+    // Data Members //
+    //////////////////
+    
+    MapType Map;
+    
+    ////////////////////
+    // Public Methods //
+    ////////////////////
+    
+  public:
+    ConsumedStateMap(void) {}
+    ConsumedStateMap(ConsumedStateMap &Other) : Map(Other.Map) {}
+    
+    /// \brief Get the consumed state of a given variable.
+    ConsumedState getState(const VarDecl *Var);
+    
+    /// \brief Mark all variables as unknown.
+    void makeUnknown(void);
+    
+    /// \brief Merge this state map with another map.
+    void merge(const ConsumedStateMap *Other);
+    
+    /// \brief Set the consumed state of a given variable.
+    void setState(const VarDecl *Var, ConsumedState State);
+  };
+  
+  class ConsumedBlockInfo {
+    
+    ///////////
+    // Types //
+    ///////////
+    
+    typedef llvm::DenseMap<const CFGBlock*, ConsumedStateMap*> MapType;
+    typedef std::pair<const CFGBlock*, ConsumedStateMap*> PairType;
+    
+    //////////////////
+    // Data Members //
+    //////////////////
+    
+    MapType Map;
+    
+  public:
+    
+    ////////////////////
+    // Public Methods //
+    ////////////////////
+    
+    void addInfo(const CFGBlock *Block, ConsumedStateMap *StateMap);
+    
+    ConsumedStateMap* getInfo(const CFGBlock *Block);
+  };
+  
+  struct VarTestResult {
+    const VarDecl *Var;
+    SourceLocation Loc;
+    bool UnconsumedInTrueBranch;
+    
+    VarTestResult(void) : 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 {
+    
+    ///////////
+    // Types //
+    ///////////
+    
+    typedef llvm::DenseMap<const CXXRecordDecl*, bool> CacheMapType;
+    typedef std::pair<const CXXRecordDecl*, bool> CachePairType;
+    
+    //////////////////
+    // Data Members //
+    //////////////////
+    
+    Sema &S;
+    
+    DiagList Warnings;
+    SourceLocation FL, FEL;
+    
+    ConsumedBlockInfo BlockInfo;
+    ConsumedStateMap *CurrStates;
+    
+    CacheMapType ConsumableTypeCache;
+    
+    /////////////////////
+    // Private Methods //
+    /////////////////////
+    
+    /// \brief Emit the warnings and notes left by the analysis.
+    void emitDiagnostics(void);
+    
+    // Tests
+    
+    bool isConsumableType(QualType Type);
+    
+    // End tests
+    
+    void splitState(const CFGBlock *CurrBlock, const IfStmt *Terminator);
+    
+  public:
+
+    //////////////////
+    // Constructors //
+    //////////////////
+    
+    ConsumedAnalyzer(Sema &S) : S(S) {}
+    
+    ////////////////////
+    // Public Methods //
+    ////////////////////
+    
+    /// \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);
+    
+    // Warning Methods
+    
+    /// 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);
+    
+    /// 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);
+    
+    /// 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);
+  };
+  
+  //////////////////////
+  // Module Functions //
+  //////////////////////
+  
+  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
Index: include/clang/Basic/Attr.td
===================================================================
--- include/clang/Basic/Attr.td
+++ include/clang/Basic/Attr.td
@@ -913,6 +913,33 @@
   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 CallableAlways : InheritableAttr {
+  let Spellings = [GNU<"callable_always">];
+  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 {
Index: include/clang/Basic/DiagnosticGroups.td
===================================================================
--- include/clang/Basic/DiagnosticGroups.td
+++ include/clang/Basic/DiagnosticGroups.td
@@ -475,6 +475,9 @@
                               ThreadSafetyPrecise]>;
 def ThreadSafetyBeta : DiagGroup<"thread-safety-beta">;
 
+// Uniqueness Analysis warnings
+def Consumed : DiagGroup<"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.
Index: include/clang/Basic/DiagnosticSemaKinds.td
===================================================================
--- include/clang/Basic/DiagnosticSemaKinds.td
+++ include/clang/Basic/DiagnosticSemaKinds.td
@@ -2170,6 +2170,21 @@
   InGroup<ThreadSafetyPrecise>, DefaultIgnore;
 def note_found_mutex_near_match : Note<"found near match '%0'">;
 
+// Uniqueness 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_in_unknown_state : Warning<
+  "Invocation of method '%0' on object '%1' while it is in an unknown state">,
+  InGroup<Consumed>, DefaultIgnore;
+def warn_unnecessary_test : Warning<
+  "Unnecessary test. Variable '%0' is known to be in the '%1' state">,
+  InGroup<Consumed>, DefaultIgnore;
+def warn_uniqueness_attribute_wrong_decl_type : Warning<
+  "%0 attribute only applies to methods">,
+  InGroup<Consumed>, DefaultIgnore;
+
 // Dummy warning that will trigger "beta" warnings from the analysis if enabled. 
 def warn_thread_safety_beta : Warning<
   "Thread safety beta warning.">, InGroup<ThreadSafetyBeta>, DefaultIgnore;
Index: include/clang/Sema/AnalysisBasedWarnings.h
===================================================================
--- include/clang/Sema/AnalysisBasedWarnings.h
+++ include/clang/Sema/AnalysisBasedWarnings.h
@@ -38,6 +38,7 @@
     unsigned enableCheckFallThrough : 1;
     unsigned enableCheckUnreachable : 1;
     unsigned enableThreadSafetyAnalysis : 1;
+    unsigned enableConsumedAnalysis : 1;
   public:
     Policy();
     void disableCheckFallThrough() { enableCheckFallThrough = 0; }
Index: lib/Analysis/CMakeLists.txt
===================================================================
--- lib/Analysis/CMakeLists.txt
+++ lib/Analysis/CMakeLists.txt
@@ -6,6 +6,7 @@
   CFGStmtMap.cpp
   CallGraph.cpp
   CocoaConventions.cpp
+  Consumed.cpp
   Dominators.cpp
   FormatString.cpp
   LiveVariables.cpp
Index: lib/Analysis/Consumed.cpp
===================================================================
--- /dev/null
+++ lib/Analysis/Consumed.cpp
@@ -0,0 +1,648 @@
+//===- 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/Attr.h"
+#include "clang/AST/DeclCXX.h"
+#include "clang/AST/ExprCXX.h"
+#include "clang/AST/RecursiveASTVisitor.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/Basic/SourceManager.h"
+#include "clang/Sema/SemaDiagnostic.h"
+#include "llvm/ADT/DenseMap.h"
+#include "llvm/ADT/SmallVector.h"
+#include "llvm/Support/raw_ostream.h"
+
+using namespace clang;
+using namespace consumed;
+
+// TODO: Add tests cases.
+// TODO: Test to make sure merging is happening correctly.  One branch should
+//       make the value consumed, and one should make it unconsumed.  Make sure
+//       it is in an unknown state, and then try and dereference it.
+// TODO: Mark variables as Consumed going into while- or for-loops only if they
+//       are referenced inside that block. (Deferred)
+// TODO: Support CONSUME methods. (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)
+
+//////////////////////
+// Helper Functions //
+//////////////////////
+
+static StringRef stateToString(ConsumedState State) {
+  switch (State) {
+    case consumed::None:
+      return "none";
+    
+    case consumed::Unknown:
+      return "unknown";
+    
+    case consumed::Unconsumed:
+      return "unconsumed";
+    
+    case consumed::Consumed:
+      return "consumed";
+  }
+}
+
+/////////////////////////
+// ConsumedStmtVisitor //
+/////////////////////////
+
+namespace {
+class ConsumedStmtVisitor
+  : public RecursiveASTVisitor<ConsumedStmtVisitor> {
+  
+  enum PropagationMode {
+    Disabled,
+    Set,
+    Consume,
+    ConsumeAndPropagate
+  };
+  
+  llvm::SmallVector<PropagationMode, 5> ModeStack;
+  
+  ConsumedAnalyzer &Analyzer;
+  ConsumedStateMap *StateMap;
+  ConsumedState PropagatingState;
+  
+public:
+  
+  bool VisitCallExpr(CallExpr *Call);
+  bool VisitCXXConstructExpr(CXXConstructExpr *Call);
+  bool VisitCXXOperatorCallExpr(CXXOperatorCallExpr *Call);
+  bool VisitDeclRefExpr(DeclRefExpr *DeclRef);
+  bool VisitVarDecl(VarDecl *Var);
+  
+  ConsumedStmtVisitor(ConsumedAnalyzer &Analyzer,
+    ConsumedStateMap *StateMap) : Analyzer(Analyzer), StateMap(StateMap) {
+    ModeStack.push_back(Disabled);
+  }
+  
+  void reset(void) {
+    ModeStack.clear();
+    ModeStack.push_back(Disabled);
+  }
+};
+
+bool ConsumedStmtVisitor::VisitCallExpr(CallExpr *Call) {
+  // FIXME: Should this be a different kind of ignore?
+  if (const CastExpr *Cast =
+    dyn_cast<CastExpr>(Call->getCallee()->IgnoreImpCasts())) {
+    
+    if (Cast->getCastKind() == CK_LValueToRValue) {
+      
+      ModeStack.push_back(ConsumeAndPropagate);
+      TraverseStmt(Call->getArg(0));
+      ModeStack.pop_back();
+      
+      return false;
+    }
+    
+    return true;
+    
+  } else if (const FunctionDecl *FunDecl =
+    dyn_cast_or_null<FunctionDecl>(Call->getCalleeDecl())) {
+    
+    if (const CXXMethodDecl *MethodDecl =
+      dyn_cast_or_null<CXXMethodDecl>(FunDecl)) {
+      
+      if (MethodDecl->isMoveAssignmentOperator()) {
+        
+        ModeStack.push_back(ConsumeAndPropagate);
+        TraverseStmt(Call->getArg(1));
+        
+        ModeStack.back() = Set;
+        TraverseStmt(Call->getArg(0));
+        
+        ModeStack.pop_back();
+        
+        return false;
+      }
+    }
+    
+    unsigned NumArgParamPairs =
+      std::min(Call->getNumArgs(), FunDecl->getNumParams());
+    
+    ModeStack.push_back(Disabled);
+    for (unsigned Index = 0; Index < NumArgParamPairs; ++Index) {
+      if (FunDecl->getParamDecl(Index)->getType()->isRValueReferenceType()) {
+        ModeStack.back() = Consume;
+      } else {
+        ModeStack.back() = Disabled;
+      }
+      
+      TraverseStmt(Call->getArg(Index));
+    }
+    ModeStack.pop_back();
+  }
+  
+  return true;
+}
+
+bool ConsumedStmtVisitor::VisitCXXConstructExpr(CXXConstructExpr *Call) {
+  const CXXConstructorDecl *ConDecl = Call->getConstructor();
+  
+  if (ConDecl->isDefaultConstructor()) {
+    PropagatingState = Consumed;
+    
+  } else if (ConDecl->isMoveConstructor()) {
+    
+    ModeStack.push_back(ConsumeAndPropagate);
+    TraverseStmt(Call->getArg(0));
+    ModeStack.pop_back();
+    
+  } else {
+    PropagatingState = Unconsumed;
+  }
+  
+  return false;
+}
+
+// TODO: Copy this block for CXXMemberCallExpr. (Deferred)
+bool ConsumedStmtVisitor::VisitCXXOperatorCallExpr(
+  CXXOperatorCallExpr *Call) {
+  
+  if (const FunctionDecl *FunDecl =
+    dyn_cast_or_null<FunctionDecl>(Call->getDirectCallee())) {
+    
+    // TODO: When we support CallableAlways (or CallableWhenConsumed) this will
+    //       have to check for the different attributes and change the behavior
+    //       bellow. (Deferred)
+    if (!FunDecl->hasAttr<CallableWhenUnconsumedAttr>()) return false;
+    
+    if (const DeclRefExpr *DeclRef =
+      dyn_cast_or_null<DeclRefExpr>(Call->getArg(0))) {
+    
+      if (const VarDecl *Var = dyn_cast_or_null<VarDecl>(DeclRef->getDecl())) {
+        switch (StateMap->getState(Var)) {
+        case Consumed:
+          Analyzer.warnUseWhileConsumed(FunDecl->getNameAsString(),
+            Var->getNameAsString(), Call->getExprLoc());
+          break;
+        
+        case Unknown:
+          Analyzer.warnUseInUnknownState(FunDecl->getNameAsString(),
+            Var->getNameAsString(), Call->getExprLoc());
+          break;
+          
+        default:
+          break;
+        }
+      }
+    }
+  }
+  
+  return false;
+}
+
+bool ConsumedStmtVisitor::VisitDeclRefExpr(DeclRefExpr *DeclRef) {
+  if (const VarDecl *Var = dyn_cast<VarDecl>(DeclRef->getDecl())) {
+    
+    // Do nothing if we aren't tracking the state of this variable.
+    if (StateMap->getState(Var) == consumed::None) return true;
+    
+    switch (ModeStack.back()) {
+    case Disabled:
+      break;
+      
+    case Set:
+      StateMap->setState(Var, PropagatingState);
+      break;
+    
+    case ConsumeAndPropagate:
+      PropagatingState = StateMap->getState(Var);
+    
+    case Consume:
+      StateMap->setState(Var, Consumed);
+      break;
+    }
+  }
+  
+  return true;
+}
+
+// TODO: Handle the case where a var decl happens in sub expressions. (Deferred)
+bool ConsumedStmtVisitor::VisitVarDecl(VarDecl *Var) {
+  TraverseStmt(const_cast<Expr*>(Var->getInit()));
+  StateMap->setState(Var, PropagatingState);
+  return false;
+}
+}
+
+//////////////////////////////
+// SortDiagBySourceLocation //
+//////////////////////////////
+
+namespace {
+struct SortDiagBySourceLocation {
+  SourceManager &SM;
+  SortDiagBySourceLocation(SourceManager &SM) : SM(SM) {}
+
+  bool operator()(const DelayedDiag &Left, const DelayedDiag &Right);
+};
+
+bool SortDiagBySourceLocation::operator()(const DelayedDiag &Left,
+                                          const DelayedDiag &Right) {
+  
+  // Although this call will be slow, this is only called when outputting
+  // multiple warnings.
+  return SM.isBeforeInTranslationUnit(Left.first.first, Right.first.first);
+}
+}
+
+///////////////////////
+// TestedVarsVisitor //
+///////////////////////
+
+namespace {
+class TestedVarsVisitor : public RecursiveASTVisitor<TestedVarsVisitor> {
+  
+  bool Invert;
+  SourceLocation CurrTestLoc;
+  
+  ConsumedStateMap *StateMap;
+  
+public:
+  bool IsUsefulConditional;
+  VarTestResult Test;
+  
+  TestedVarsVisitor(ConsumedStateMap *StateMap) : Invert(false),
+    StateMap(StateMap), IsUsefulConditional(true) {}
+  
+  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();
+      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::None) {
+      Test = VarTestResult(Var, CurrTestLoc, !Invert);
+    }
+    
+  } else {
+    IsUsefulConditional = false;
+  }
+  
+  return IsUsefulConditional;
+}
+
+bool TestedVarsVisitor::VisitUnaryOperator(UnaryOperator *UnaryOp) {
+  if (UnaryOp->getOpcode() == UO_Not) {
+    Invert = true;
+    TraverseStmt(UnaryOp->getSubExpr());
+    
+  } else {
+    IsUsefulConditional = false;
+  }
+  
+  return false;
+}
+}
+
+namespace clang {
+namespace consumed {
+
+///////////////////////
+// ConsumedBlockInfo //
+///////////////////////
+
+void ConsumedBlockInfo::addInfo(const CFGBlock *Block,
+                                ConsumedStateMap *StateMap) {
+
+  MapType::iterator Entry = Map.find(Block);
+  
+  if (Entry == Map.end()) {
+    // No info for this block is currently in the map.
+    Map.insert(PairType(Block, StateMap));
+    
+  } else {
+    // Merge the new state map with the existing one, and then free the new map.
+    (*Entry).second->merge(StateMap);
+    delete StateMap;
+  }
+}
+
+ConsumedStateMap* ConsumedBlockInfo::getInfo(const CFGBlock *Block) {
+  ConsumedStateMap *RetValue = (*Map.find(Block)).second;
+  
+  Map.erase(Block);
+  
+  return RetValue;
+}
+
+//////////////////////
+// ConsumedStateMap //
+//////////////////////
+
+ConsumedState ConsumedStateMap::getState(const VarDecl *Var) {
+  MapType::const_iterator Entry = Map.find(Var);
+  
+  if (Entry != Map.end()) {
+    return (*Entry).second;
+    
+  } else {
+    return None;
+  }
+}
+
+void ConsumedStateMap::makeUnknown(void) {
+  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, Unknown));
+  }
+}
+
+void ConsumedStateMap::merge(const ConsumedStateMap *Other) {
+  ConsumedState LocalState;
+  
+  for (MapType::const_iterator DMI = Other->Map.begin(),
+    DME = Other->Map.end(); DMI != DME; ++DMI) {
+    
+    PairType OtherPair = *DMI;
+    
+    LocalState = this->getState(OtherPair.first);
+    
+    if (LocalState == None) {
+      Map.insert(OtherPair);
+      
+    } else if (LocalState != OtherPair.second) {
+      Map.erase(OtherPair.first);
+      Map.insert(PairType(OtherPair.first, Unknown));
+    }
+  }
+  
+  llvm::errs() << "\n";
+}
+
+void ConsumedStateMap::setState(const VarDecl *Var, ConsumedState State) {
+  Map.erase(Var);
+  Map.insert(PairType(Var, State));
+}
+
+//////////////////////
+// ConsumedAnalyzer //
+//////////////////////
+
+/// \brief Emit all buffered diagnostics in order of sourcelocation.
+/// We need to output diagnostics produced while checking operations on
+/// unique types in deterministic order, so this function orders diagnostics
+/// and outputs them.
+void ConsumedAnalyzer::emitDiagnostics(void) {
+  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);
+    }
+  }
+}
+
+// TODO: Walk the base classes to see if any of them are unique types.
+//       (Deferred)
+bool ConsumedAnalyzer::isConsumableType(QualType Type) {
+  if (const CXXRecordDecl *Klass =
+    dyn_cast_or_null<CXXRecordDecl>(Type->getAsCXXRecordDecl())) {
+    
+    CacheMapType::const_iterator Entry = ConsumableTypeCache.find(Klass);
+    
+    if (Entry != ConsumableTypeCache.end()) {
+      return (*Entry).second;
+    }
+    
+    for (CXXRecordDecl::method_iterator MI = Klass->method_begin(),
+      ME = Klass->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:
+          ConsumableTypeCache.insert(CachePairType(Klass, true));
+          return true;
+        
+        default:
+          break;
+        }
+      }
+    }
+      
+    ConsumableTypeCache.insert(CachePairType(Klass, false));
+  }
+  
+  return false;
+}
+
+// FIXME: Make this not generate false positives for while- and for-loops.
+// 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 != Unknown) {
+      warnUnnecessaryTest(Visitor.Test.Var->getNameAsString(),
+        stateToString(VarState), Visitor.Test.Loc);
+    }
+    
+    if (Visitor.Test.UnconsumedInTrueBranch) {
+      CurrStates->setState(Visitor.Test.Var, Unconsumed);
+      
+      if (HasElse) ElseOrMergeStates->setState(Visitor.Test.Var, Consumed);
+  
+    } else {
+      CurrStates->setState(Visitor.Test.Var, Consumed);
+      
+      if (HasElse) ElseOrMergeStates->setState(Visitor.Test.Var, Unconsumed);
+    }
+  }
+    
+  CFGBlock::const_succ_iterator SI = CurrBlock->succ_begin();
+  
+  BlockInfo.addInfo(  *SI,        CurrStates);
+  BlockInfo.addInfo(*++SI, ElseOrMergeStates);
+}
+
+void ConsumedAnalyzer::run(AnalysisDeclContext &AC) {
+  const FunctionDecl *D = dyn_cast_or_null<FunctionDecl>(AC.getDecl());
+  
+  if (!D) {
+    return;
+  }
+  
+  FL  = AC.getDecl()->getLocation();
+  FEL = AC.getDecl()->getLocEnd();
+  
+  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;
+    
+    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.TraverseStmt(const_cast<Stmt*>(
+          BI->castAs<CFGStmt>().getStmt()));
+        
+        Visitor.reset();
+      }
+    }
+    
+    // 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() == 2) {
+      // Handle while- and for-loops.
+      CurrStates->makeUnknown();
+      ConsumedStateMap *StatesCopy = new ConsumedStateMap(*CurrStates);
+      
+      CFGBlock::const_succ_iterator SI = CurrBlock->succ_begin();
+      
+      BlockInfo.addInfo(  *SI, CurrStates);
+      BlockInfo.addInfo(*++SI, StatesCopy);
+      
+      CurrStates = NULL;
+      
+    } else if (CurrBlock->succ_size() == 1 &&
+               (*CurrBlock->succ_begin())->pred_size() > 1) {
+      
+      BlockInfo.addInfo(*CurrBlock->succ_begin(), CurrStates);
+      CurrStates = NULL;
+    }
+  } // End of block iterator.
+  
+  // Delete the last existing state map.
+  delete CurrStates;
+  
+  emitDiagnostics();
+}
+
+void ConsumedAnalyzer::warnUnnecessaryTest(StringRef VariableName,
+                                           StringRef VariableState,
+                                           SourceLocation Loc) {
+
+  PartialDiagnosticAt Warning(Loc, S.PDiag(diag::warn_unnecessary_test) <<
+                               VariableName << VariableState);
+  
+  Warnings.push_back(DelayedDiag(Warning, OptionalNotes()));
+}
+
+void ConsumedAnalyzer::warnUseWhileConsumed(StringRef MethodName,
+                                            StringRef VariableName,
+                                            SourceLocation Loc) {
+  
+  PartialDiagnosticAt Warning(Loc, S.PDiag(diag::warn_use_while_consumed) <<
+                              MethodName << VariableName);
+  
+  Warnings.push_back(DelayedDiag(Warning, OptionalNotes()));
+}
+
+void ConsumedAnalyzer::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()));
+}
+
+//////////////////////
+// Module Functions //
+//////////////////////
+
+unsigned checkEnabled(DiagnosticsEngine &D) {
+  return (unsigned)
+    (D.getDiagnosticLevel(diag::warn_use_while_consumed, SourceLocation()) !=
+     DiagnosticsEngine::Ignored);
+}
+
+bool isTestingFunction(const CXXMethodDecl *Method) {
+  if (Method->hasAttr<TestsUnconsumedAttr>()) {
+    return true;
+  } else {
+    return false;
+  }
+}
+
+}} // end namespace clang::consumed
Index: lib/Sema/AnalysisBasedWarnings.cpp
===================================================================
--- lib/Sema/AnalysisBasedWarnings.cpp
+++ lib/Sema/AnalysisBasedWarnings.cpp
@@ -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"
@@ -1420,6 +1421,7 @@
   enableCheckFallThrough = 1;
   enableCheckUnreachable = 0;
   enableThreadSafetyAnalysis = 0;
+  enableConsumedAnalysis = 0;
 }
 
 clang::sema::AnalysisBasedWarnings::AnalysisBasedWarnings(Sema &s)
@@ -1440,6 +1442,7 @@
   DefaultPolicy.enableThreadSafetyAnalysis = (unsigned)
     (D.getDiagnosticLevel(diag::warn_double_lock, SourceLocation()) !=
      DiagnosticsEngine::Ignored);
+  DefaultPolicy.enableConsumedAnalysis = consumed::checkEnabled(D);
 
 }
 
@@ -1501,7 +1504,8 @@
   // 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 +1609,12 @@
     Reporter.emitDiagnostics();
   }
 
+  // Check for violations of consumed properties.
+  if (P.enableConsumedAnalysis) {
+    consumed::ConsumedAnalyzer Analyzer(S);
+    Analyzer.run(AC);
+  }
+
   if (Diags.getDiagnosticLevel(diag::warn_uninit_var, D->getLocStart())
       != DiagnosticsEngine::Ignored ||
       Diags.getDiagnosticLevel(diag::warn_sometimes_uninit_var,D->getLocStart())
Index: lib/Sema/SemaDeclAttr.cpp
===================================================================
--- lib/Sema/SemaDeclAttr.cpp
+++ lib/Sema/SemaDeclAttr.cpp
@@ -1004,6 +1004,110 @@
                                Attr.getAttributeSpellingListIndex()));
 }
 
+static void handleConsumesAttr(Sema &S, Decl *D,
+                               const AttributeList &Attr) {
+  // Handle functions with the CONSUMES attribute.
+  
+  assert(not Attr.isInvalid());
+
+  if (not checkAttributeNumArgs(S, Attr, 0)) {
+    return;
+  }
+
+  if (not isa<CXXMethodDecl>(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 handleCallableAlwaysAttr(Sema &S, Decl *D,
+                                     const AttributeList &Attr) {
+  // Handle functions with the CALLABLE_ALWAYS attribute.
+  
+  assert(not Attr.isInvalid());
+
+  if (not checkAttributeNumArgs(S, Attr, 0)) {
+    return;
+  }
+
+  if (not isa<CXXMethodDecl>(D)) {
+    S.Diag(Attr.getLoc(), diag::warn_uniqueness_attribute_wrong_decl_type) <<
+      Attr.getName();
+    return;
+  }
+  
+  D->addAttr(::new (S.Context)
+             CallableAlwaysAttr(Attr.getRange(), S.Context,
+              Attr.getAttributeSpellingListIndex()));
+}
+
+static void handleCallableWhenUnconsumedAttr(Sema &S, Decl *D,
+                                             const AttributeList &Attr) {
+  // Handle functions with the CALLABLE_WHEN_UNCSONSUMED attribute.
+  
+  assert(not Attr.isInvalid());
+
+  if (not checkAttributeNumArgs(S, Attr, 0)) {
+    return;
+  }
+
+  if (not 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) {
+  // Handle functions with the TESTS_CONSUMED attribute.
+  
+  assert(not Attr.isInvalid());
+
+  if (not checkAttributeNumArgs(S, Attr, 0)) {
+    return;
+  }
+
+  if (not 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) {
+  // Handle functions with the TESTS_UNCONSUMED attribute.
+  
+  assert(not Attr.isInvalid());
+
+  if (not checkAttributeNumArgs(S, Attr, 0)) {
+    return;
+  }
+
+  if (not 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) {
@@ -5076,6 +5180,23 @@
     handleAcquiredAfterAttr(S, D, Attr);
     break;
 
+  // Uniqueness analysis attributes.
+  case AttributeList::AT_Consumes:
+    handleConsumesAttr(S, D, Attr);
+    break;
+  case AttributeList::AT_CallableAlways:
+    handleCallableAlwaysAttr(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);
Index: test/SemaCXX/warn-consumed-analysis.cpp
===================================================================
--- /dev/null
+++ test/SemaCXX/warn-consumed-analysis.cpp
@@ -0,0 +1,97 @@
+// RUN: %clang_cc1 -fsyntax-only -verify -Wconsumed -std=c++11 %s
+
+#define CALLABLE_WHEN_UNCONSUMED __attribute__ ((callable_when_unconsumed))
+#define TESTS_UNCONSUMED __attribute__ ((tests_unconsumed))
+
+class Bar {
+  int var;
+  
+  public:
+  Bar(void);
+  Bar(int val);
+  Bar(Bar &other);
+  Bar(Bar &&other);
+  
+  Bar& operator=(Bar &other);
+  Bar& operator=(Bar &&other);
+  
+  void operator*(void) CALLABLE_WHEN_UNCONSUMED;
+  
+  bool isValid(void) TESTS_UNCONSUMED;
+};
+
+void testInitializationAndIfStmt(void) {
+  Bar var0;
+  Bar var1 = Bar();
+  
+  var0 = Bar();
+  
+  *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()) { // \
+    \\ expected-warning {{Unnecessary test. Variable 'var0' is known to be in the 'consumed' state}}
+    *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 var0;
+  Bar var1 = Bar(42);
+  
+  *var0; // expected-warning {{Invocation of method 'operator*' on object 'var0' while it is in the 'consumed' state}}
+  *var1;
+  
+  var0 = static_cast<Bar&&>(var1);
+  
+  *var0;
+  *var1; // expected-warning {{Invocation of method 'operator*' on object 'var1' while it is in the 'consumed' state}}
+}
+
+void testConditionalMerge(void) {
+  Bar 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 testSimpleForLoop(void) {
+  Bar 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 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}}
+}
Index: test/SemaCXX/warn-consumed-parsing.cpp
===================================================================
--- /dev/null
+++ test/SemaCXX/warn-consumed-parsing.cpp
@@ -0,0 +1,46 @@
+// RUN: %clang_cc1 -fsyntax-only -verify -Wconsumed -std=c++11 %s
+
+#define CONSUMES                  __attribute__ ((consumes))
+#define TESTS_UNCONSUMED          __attribute__ ((tests_unconsumed))
+#define CALLABLE_ALWAYS           __attribute__ ((callable_always))
+#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 CallableAlways(void)  __attribute__ ((callable_always(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_ALWAYS; // \
+\\ expected-warning {{'callable_always' attribute only applies to methods}}
+int var3 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 function0(void) TESTS_UNCONSUMED; // \
+\\ expected-warning {{'tests_unconsumed' attribute only applies to methods}}
+void function0(void) CALLABLE_ALWAYS; // \
+\\ expected-warning {{'callable_always' attribute only applies to methods}}
+void function0(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;
+  void CallableAlways(void)  CALLABLE_ALWAYS;
+};
+
+class AttrTester2 {
+  void CallableWhenUnconsumed(void) CALLABLE_WHEN_UNCONSUMED;
+};
_______________________________________________
cfe-commits mailing list
[email protected]
http://lists.cs.uiuc.edu/mailman/listinfo/cfe-commits

Reply via email to