//===- 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 "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";
  }
  llvm_unreachable("invalid enum");
}

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;

  AnalysisDeclContext &AC;
  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(AnalysisDeclContext &AC, ConsumedAnalyzer &Analyzer,
                      ConsumedStateMap *StateMap)
      : AC(AC), 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<ConsumedStmtVisitor>::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 = AC.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;
}


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(AC, *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();
}

bool isTestingFunction(const CXXMethodDecl *Method) {
  return Method->hasAttr<TestsUnconsumedAttr>();
}

}} // end namespace clang::consumed
