]> granicus.if.org Git - clang/commitdiff
Patch by Chris Wailes <chris.wailes@gmail.com>.
authorDeLesley Hutchins <delesley@google.com>
Mon, 12 Aug 2013 21:20:55 +0000 (21:20 +0000)
committerDeLesley Hutchins <delesley@google.com>
Mon, 12 Aug 2013 21:20:55 +0000 (21:20 +0000)
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.

git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@188206 91177308-0d34-0410-b5e6-96231b3b80d8

13 files changed:
include/clang/Analysis/Analyses/Consumed.h [new file with mode: 0644]
include/clang/Basic/Attr.td
include/clang/Basic/DiagnosticGroups.td
include/clang/Basic/DiagnosticSemaKinds.td
include/clang/Sema/AnalysisBasedWarnings.h
include/clang/Sema/ConsumedWarningsHandler.h [new file with mode: 0644]
lib/Analysis/CMakeLists.txt
lib/Analysis/Consumed.cpp [new file with mode: 0644]
lib/Sema/AnalysisBasedWarnings.cpp
lib/Sema/SemaDeclAttr.cpp
test/SemaCXX/warn-consumed-analysis-strict.cpp [new file with mode: 0644]
test/SemaCXX/warn-consumed-analysis.cpp [new file with mode: 0644]
test/SemaCXX/warn-consumed-parsing.cpp [new file with mode: 0644]

diff --git a/include/clang/Analysis/Analyses/Consumed.h b/include/clang/Analysis/Analyses/Consumed.h
new file mode 100644 (file)
index 0000000..2800b01
--- /dev/null
@@ -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
index f592bcbb4f609f8dd459e833149765a106f38afe..f81098dc8a1d1042086fedcf5e932602803cf7ba 100644 (file)
@@ -918,6 +918,28 @@ def SharedLocksRequired : InheritableAttr {
   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 {
index 93cba3edd9b38da7c891151df298fcce8565f4b4..ec9c8eb08c3d35a4d97ad7b3ed4d71ee4e8f3a81 100644 (file)
@@ -478,6 +478,10 @@ def ThreadSafety : DiagGroup<"thread-safety",
                               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.
index d231af330bfd33521be7bed04316c73198ad9208..fd1494445af59b7201da9f474c6285970715cd70 100644 (file)
@@ -2178,6 +2178,28 @@ def note_found_mutex_near_match : Note<"found near match '%0'">;
 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;
index eeac97332dfbca0b6549a318a280deb59264416f..432c4e23a993d45a1a02167024d34549a08d1c17 100644 (file)
@@ -38,6 +38,7 @@ public:
     unsigned enableCheckFallThrough : 1;
     unsigned enableCheckUnreachable : 1;
     unsigned enableThreadSafetyAnalysis : 1;
+    unsigned enableConsumedAnalysis : 1;
   public:
     Policy();
     void disableCheckFallThrough() { enableCheckFallThrough = 0; }
diff --git a/include/clang/Sema/ConsumedWarningsHandler.h b/include/clang/Sema/ConsumedWarningsHandler.h
new file mode 100644 (file)
index 0000000..392d781
--- /dev/null
@@ -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
index ca166669fc89dd8d2991196248ee1f4d87dba896..deab8f1f22fde3fb93a25bb66ea4c474613cbcff 100644 (file)
@@ -6,6 +6,7 @@ add_clang_library(clangAnalysis
   CFGStmtMap.cpp
   CallGraph.cpp
   CocoaConventions.cpp
+  Consumed.cpp
   Dominators.cpp
   FormatString.cpp
   LiveVariables.cpp
diff --git a/lib/Analysis/Consumed.cpp b/lib/Analysis/Consumed.cpp
new file mode 100644 (file)
index 0000000..b6a18a8
--- /dev/null
@@ -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
index af404a5782e8561d2beb8987dfa2597e3e106c80..19fc29b9071386dce5e333e3dff838263b1ad187 100644 (file)
@@ -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;
@@ -1411,6 +1414,119 @@ class ThreadSafetyReporter : public clang::thread_safety::ThreadSafetyHandler {
 }
 }
 
+//===----------------------------------------------------------------------===//
+// -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::Policy::Policy() {
   enableCheckFallThrough = 1;
   enableCheckUnreachable = 0;
   enableThreadSafetyAnalysis = 0;
+  enableConsumedAnalysis = 0;
 }
 
 clang::sema::AnalysisBasedWarnings::AnalysisBasedWarnings(Sema &s)
@@ -1440,6 +1557,7 @@ clang::sema::AnalysisBasedWarnings::AnalysisBasedWarnings(Sema &s)
   DefaultPolicy.enableThreadSafetyAnalysis = (unsigned)
     (D.getDiagnosticLevel(diag::warn_double_lock, SourceLocation()) !=
      DiagnosticsEngine::Ignored);
+  DefaultPolicy.enableConsumedAnalysis = consumed::checkEnabled(D);
 
 }
 
@@ -1501,7 +1619,8 @@ AnalysisBasedWarnings::IssueWarnings(sema::AnalysisBasedWarnings::Policy P,
   // 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(sema::AnalysisBasedWarnings::Policy P,
     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())
index cae855e0065874167a04cf604d00cc3097ca271d..77b242d19f67895324fd27d6ba89acad5cc325e4 100644 (file)
@@ -997,6 +997,69 @@ static void handleLocksExcludedAttr(Sema &S, Decl *D,
                                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(Sema &S, Scope *scope, Decl *D,
     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);
diff --git a/test/SemaCXX/warn-consumed-analysis-strict.cpp b/test/SemaCXX/warn-consumed-analysis-strict.cpp
new file mode 100644 (file)
index 0000000..0190359
--- /dev/null
@@ -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}}
+}
diff --git a/test/SemaCXX/warn-consumed-analysis.cpp b/test/SemaCXX/warn-consumed-analysis.cpp
new file mode 100644 (file)
index 0000000..fc86317
--- /dev/null
@@ -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;
+}
diff --git a/test/SemaCXX/warn-consumed-parsing.cpp b/test/SemaCXX/warn-consumed-parsing.cpp
new file mode 100644 (file)
index 0000000..23df1d1
--- /dev/null
@@ -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;
+};