head	1.1;
branch	1.1.1;
access;
symbols
	netbsd-11-0-RC4:1.1.1.9
	netbsd-11-0-RC3:1.1.1.9
	netbsd-11-0-RC2:1.1.1.9
	netbsd-11-0-RC1:1.1.1.9
	perseant-exfatfs-base-20250801:1.1.1.9
	netbsd-11:1.1.1.9.0.10
	netbsd-11-base:1.1.1.9
	netbsd-10-1-RELEASE:1.1.1.9
	perseant-exfatfs-base-20240630:1.1.1.9
	perseant-exfatfs:1.1.1.9.0.8
	perseant-exfatfs-base:1.1.1.9
	netbsd-8-3-RELEASE:1.1.1.7
	netbsd-9-4-RELEASE:1.1.1.8
	netbsd-10-0-RELEASE:1.1.1.9
	netbsd-10-0-RC6:1.1.1.9
	netbsd-10-0-RC5:1.1.1.9
	netbsd-10-0-RC4:1.1.1.9
	netbsd-10-0-RC3:1.1.1.9
	netbsd-10-0-RC2:1.1.1.9
	netbsd-10-0-RC1:1.1.1.9
	netbsd-10:1.1.1.9.0.6
	netbsd-10-base:1.1.1.9
	netbsd-9-3-RELEASE:1.1.1.8
	cjep_sun2x:1.1.1.9.0.4
	cjep_sun2x-base:1.1.1.9
	cjep_staticlib_x-base1:1.1.1.9
	netbsd-9-2-RELEASE:1.1.1.8
	cjep_staticlib_x:1.1.1.9.0.2
	cjep_staticlib_x-base:1.1.1.9
	netbsd-9-1-RELEASE:1.1.1.8
	phil-wifi-20200421:1.1.1.9
	phil-wifi-20200411:1.1.1.9
	phil-wifi-20200406:1.1.1.9
	netbsd-8-2-RELEASE:1.1.1.7
	netbsd-9-0-RELEASE:1.1.1.8
	netbsd-9-0-RC2:1.1.1.8
	netbsd-9-0-RC1:1.1.1.8
	netbsd-9:1.1.1.8.0.2
	netbsd-9-base:1.1.1.8
	phil-wifi-20190609:1.1.1.8
	netbsd-8-1-RELEASE:1.1.1.7
	netbsd-8-1-RC1:1.1.1.7
	pgoyette-compat-merge-20190127:1.1.1.7.12.1
	pgoyette-compat-20190127:1.1.1.8
	pgoyette-compat-20190118:1.1.1.8
	pgoyette-compat-1226:1.1.1.8
	pgoyette-compat-1126:1.1.1.8
	pgoyette-compat-1020:1.1.1.8
	pgoyette-compat-0930:1.1.1.8
	pgoyette-compat-0906:1.1.1.8
	netbsd-7-2-RELEASE:1.1.1.5
	pgoyette-compat-0728:1.1.1.8
	clang-337282:1.1.1.8
	netbsd-8-0-RELEASE:1.1.1.7
	phil-wifi:1.1.1.7.0.14
	phil-wifi-base:1.1.1.7
	pgoyette-compat-0625:1.1.1.7
	netbsd-8-0-RC2:1.1.1.7
	pgoyette-compat-0521:1.1.1.7
	pgoyette-compat-0502:1.1.1.7
	pgoyette-compat-0422:1.1.1.7
	netbsd-8-0-RC1:1.1.1.7
	pgoyette-compat-0415:1.1.1.7
	pgoyette-compat-0407:1.1.1.7
	pgoyette-compat-0330:1.1.1.7
	pgoyette-compat-0322:1.1.1.7
	pgoyette-compat-0315:1.1.1.7
	netbsd-7-1-2-RELEASE:1.1.1.5
	pgoyette-compat:1.1.1.7.0.12
	pgoyette-compat-base:1.1.1.7
	netbsd-7-1-1-RELEASE:1.1.1.5
	clang-319952:1.1.1.7
	matt-nb8-mediatek:1.1.1.7.0.10
	matt-nb8-mediatek-base:1.1.1.7
	clang-309604:1.1.1.7
	perseant-stdc-iso10646:1.1.1.7.0.8
	perseant-stdc-iso10646-base:1.1.1.7
	netbsd-8:1.1.1.7.0.6
	netbsd-8-base:1.1.1.7
	prg-localcount2-base3:1.1.1.7
	prg-localcount2-base2:1.1.1.7
	prg-localcount2-base1:1.1.1.7
	prg-localcount2:1.1.1.7.0.4
	prg-localcount2-base:1.1.1.7
	pgoyette-localcount-20170426:1.1.1.7
	bouyer-socketcan-base1:1.1.1.7
	pgoyette-localcount-20170320:1.1.1.7
	netbsd-7-1:1.1.1.5.0.10
	netbsd-7-1-RELEASE:1.1.1.5
	netbsd-7-1-RC2:1.1.1.5
	clang-294123:1.1.1.7
	netbsd-7-nhusb-base-20170116:1.1.1.5
	bouyer-socketcan:1.1.1.7.0.2
	bouyer-socketcan-base:1.1.1.7
	clang-291444:1.1.1.7
	pgoyette-localcount-20170107:1.1.1.6
	netbsd-7-1-RC1:1.1.1.5
	pgoyette-localcount-20161104:1.1.1.6
	netbsd-7-0-2-RELEASE:1.1.1.5
	localcount-20160914:1.1.1.6
	netbsd-7-nhusb:1.1.1.5.0.8
	netbsd-7-nhusb-base:1.1.1.5
	clang-280599:1.1.1.6
	pgoyette-localcount-20160806:1.1.1.6
	pgoyette-localcount-20160726:1.1.1.6
	pgoyette-localcount:1.1.1.6.0.2
	pgoyette-localcount-base:1.1.1.6
	netbsd-7-0-1-RELEASE:1.1.1.5
	clang-261930:1.1.1.6
	netbsd-7-0:1.1.1.5.0.6
	netbsd-7-0-RELEASE:1.1.1.5
	netbsd-7-0-RC3:1.1.1.5
	netbsd-7-0-RC2:1.1.1.5
	netbsd-7-0-RC1:1.1.1.5
	clang-237755:1.1.1.5
	clang-232565:1.1.1.5
	clang-227398:1.1.1.5
	tls-maxphys-base:1.1.1.5
	tls-maxphys:1.1.1.5.0.4
	netbsd-7:1.1.1.5.0.2
	netbsd-7-base:1.1.1.5
	clang-215315:1.1.1.5
	clang-209886:1.1.1.5
	yamt-pagecache:1.1.1.4.0.4
	yamt-pagecache-base9:1.1.1.4
	tls-earlyentropy:1.1.1.4.0.2
	tls-earlyentropy-base:1.1.1.5
	riastradh-xf86-video-intel-2-7-1-pre-2-21-15:1.1.1.4
	riastradh-drm2-base3:1.1.1.4
	clang-202566:1.1.1.4
	clang-201163:1.1.1.4
	clang-199312:1.1.1.3
	clang-198450:1.1.1.2
	clang-196603:1.1.1.1
	clang-195771:1.1.1.1
	LLVM:1.1.1;
locks; strict;
comment	@// @;


1.1
date	2013.11.28.14.14.49;	author joerg;	state Exp;
branches
	1.1.1.1;
next	;
commitid	ow8OybrawrB1f3fx;

1.1.1.1
date	2013.11.28.14.14.49;	author joerg;	state Exp;
branches;
next	1.1.1.2;
commitid	ow8OybrawrB1f3fx;

1.1.1.2
date	2014.01.05.15.38.38;	author joerg;	state Exp;
branches;
next	1.1.1.3;
commitid	wh3aCSIWykURqWjx;

1.1.1.3
date	2014.01.15.21.26.20;	author joerg;	state Exp;
branches;
next	1.1.1.4;
commitid	NQXlzzA0SPkc5glx;

1.1.1.4
date	2014.02.14.20.07.13;	author joerg;	state Exp;
branches
	1.1.1.4.2.1
	1.1.1.4.4.1;
next	1.1.1.5;
commitid	annVkZ1sc17rF6px;

1.1.1.5
date	2014.05.30.18.14.40;	author joerg;	state Exp;
branches
	1.1.1.5.4.1;
next	1.1.1.6;
commitid	8q0kdlBlCn09GACx;

1.1.1.6
date	2016.02.27.22.11.54;	author joerg;	state Exp;
branches
	1.1.1.6.2.1;
next	1.1.1.7;
commitid	tIimz3oDlh1NpBWy;

1.1.1.7
date	2017.01.11.10.34.43;	author joerg;	state Exp;
branches
	1.1.1.7.12.1
	1.1.1.7.14.1;
next	1.1.1.8;
commitid	CNnUNfII1jgNmxBz;

1.1.1.8
date	2018.07.17.18.31.08;	author joerg;	state Exp;
branches;
next	1.1.1.9;
commitid	wDzL46ALjrCZgwKA;

1.1.1.9
date	2019.11.13.22.19.20;	author joerg;	state dead;
branches;
next	;
commitid	QD8YATxuNG34YJKB;

1.1.1.4.2.1
date	2014.08.10.07.08.07;	author tls;	state Exp;
branches;
next	;
commitid	t01A1TLTYxkpGMLx;

1.1.1.4.4.1
date	2014.02.14.20.07.13;	author yamt;	state dead;
branches;
next	1.1.1.4.4.2;
commitid	WSrDtL5nYAUyiyBx;

1.1.1.4.4.2
date	2014.05.22.16.18.26;	author yamt;	state Exp;
branches;
next	;
commitid	WSrDtL5nYAUyiyBx;

1.1.1.5.4.1
date	2014.05.30.18.14.40;	author tls;	state dead;
branches;
next	1.1.1.5.4.2;
commitid	jTnpym9Qu0o4R1Nx;

1.1.1.5.4.2
date	2014.08.19.23.47.26;	author tls;	state Exp;
branches;
next	;
commitid	jTnpym9Qu0o4R1Nx;

1.1.1.6.2.1
date	2017.03.20.06.52.36;	author pgoyette;	state Exp;
branches;
next	;
commitid	jjw7cAwgyKq7RfKz;

1.1.1.7.12.1
date	2018.07.28.04.33.16;	author pgoyette;	state Exp;
branches;
next	;
commitid	1UP1xAIUxv1ZgRLA;

1.1.1.7.14.1
date	2019.06.10.21.45.20;	author christos;	state Exp;
branches;
next	1.1.1.7.14.2;
commitid	jtc8rnCzWiEEHGqB;

1.1.1.7.14.2
date	2020.04.13.07.46.31;	author martin;	state dead;
branches;
next	;
commitid	X01YhRUPVUDaec4C;


desc
@@


1.1
log
@Initial revision
@
text
@//===- 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/OwningPtr.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/Support/Compiler.h"
#include "llvm/Support/raw_ostream.h"

// TODO: Adjust states of args to constructors in the same way that arguments to
//       function calls are handled.
// TODO: Use information from tests in for- and while-loop conditional.
// TODO: Add notes about the actual and expected state for 
// TODO: Correctly identify unreachable blocks when chaining boolean operators.
// TODO: Adjust the parser and AttributesList class to support lists of
//       identifiers.
// TODO: Warn about unreachable code.
// TODO: Switch to using a bitmap to track unreachable blocks.
// TODO: Handle variable definitions, e.g. bool valid = x.isValid();
//       if (valid) ...; (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)

using namespace clang;
using namespace consumed;

// Key method definition
ConsumedWarningsHandlerBase::~ConsumedWarningsHandlerBase() {}

static SourceLocation getFirstStmtLoc(const CFGBlock *Block) {
  // Find the source location of the first statement in the block, if the block
  // is not empty.
  for (CFGBlock::const_iterator BI = Block->begin(), BE = Block->end();
       BI != BE; ++BI) {
    if (Optional<CFGStmt> CS = BI->getAs<CFGStmt>())
      return CS->getStmt()->getLocStart();
  }

  // Block is empty.
  // If we have one successor, return the first statement in that block
  if (Block->succ_size() == 1 && *Block->succ_begin())
    return getFirstStmtLoc(*Block->succ_begin());

  return SourceLocation();
}

static SourceLocation getLastStmtLoc(const CFGBlock *Block) {
  // Find the source location of the last statement in the block, if the block
  // is not empty.
  if (const Stmt *StmtNode = Block->getTerminator()) {
    return StmtNode->getLocStart();
  } else {
    for (CFGBlock::const_reverse_iterator BI = Block->rbegin(),
         BE = Block->rend(); BI != BE; ++BI) {
      if (Optional<CFGStmt> CS = BI->getAs<CFGStmt>())
        return CS->getStmt()->getLocStart();
    }
  }

  // If we have one successor, return the first statement in that block
  SourceLocation Loc;
  if (Block->succ_size() == 1 && *Block->succ_begin())
    Loc = getFirstStmtLoc(*Block->succ_begin());
  if (Loc.isValid())
    return Loc;

  // If we have one predecessor, return the last statement in that block
  if (Block->pred_size() == 1 && *Block->pred_begin())
    return getLastStmtLoc(*Block->pred_begin());

  return Loc;
}

static ConsumedState invertConsumedUnconsumed(ConsumedState State) {
  switch (State) {
  case CS_Unconsumed:
    return CS_Consumed;
  case CS_Consumed:
    return CS_Unconsumed;
  case CS_None:
    return CS_None;
  case CS_Unknown:
    return CS_Unknown;
  }
  llvm_unreachable("invalid enum");
}

static bool isCallableInState(const CallableWhenAttr *CWAttr,
                              ConsumedState State) {
  
  CallableWhenAttr::callableState_iterator I = CWAttr->callableState_begin(),
                                           E = CWAttr->callableState_end();
  
  for (; I != E; ++I) {
    
    ConsumedState MappedAttrState = CS_None;
    
    switch (*I) {
    case CallableWhenAttr::Unknown:
      MappedAttrState = CS_Unknown;
      break;
      
    case CallableWhenAttr::Unconsumed:
      MappedAttrState = CS_Unconsumed;
      break;
      
    case CallableWhenAttr::Consumed:
      MappedAttrState = CS_Consumed;
      break;
    }
    
    if (MappedAttrState == State)
      return true;
  }
  
  return false;
}

static bool isConsumableType(const QualType &QT) {
  if (QT->isPointerType() || QT->isReferenceType())
    return false;
  
  if (const CXXRecordDecl *RD = QT->getAsCXXRecordDecl())
    return RD->hasAttr<ConsumableAttr>();
  
  return false;
}

static bool isKnownState(ConsumedState State) {
  switch (State) {
  case CS_Unconsumed:
  case CS_Consumed:
    return true;
  case CS_None:
  case CS_Unknown:
    return false;
  }
  llvm_unreachable("invalid enum");
}

static bool isRValueRefish(QualType ParamType) {
  return ParamType->isRValueReferenceType() ||
        (ParamType->isLValueReferenceType() &&
         !cast<LValueReferenceType>(
           ParamType.getCanonicalType())->isSpelledAsLValue());
}

static bool isTestingFunction(const FunctionDecl *FunDecl) {
  return FunDecl->hasAttr<TestTypestateAttr>();
}

static bool isValueType(QualType ParamType) {
  return !(ParamType->isPointerType() || ParamType->isReferenceType());
}

static ConsumedState mapConsumableAttrState(const QualType QT) {
  assert(isConsumableType(QT));

  const ConsumableAttr *CAttr =
      QT->getAsCXXRecordDecl()->getAttr<ConsumableAttr>();

  switch (CAttr->getDefaultState()) {
  case ConsumableAttr::Unknown:
    return CS_Unknown;
  case ConsumableAttr::Unconsumed:
    return CS_Unconsumed;
  case ConsumableAttr::Consumed:
    return CS_Consumed;
  }
  llvm_unreachable("invalid enum");
}

static ConsumedState
mapParamTypestateAttrState(const ParamTypestateAttr *PTAttr) {
  switch (PTAttr->getParamState()) {
  case ParamTypestateAttr::Unknown:
    return CS_Unknown;
  case ParamTypestateAttr::Unconsumed:
    return CS_Unconsumed;
  case ParamTypestateAttr::Consumed:
    return CS_Consumed;
  }
  llvm_unreachable("invalid_enum");
}

static ConsumedState
mapReturnTypestateAttrState(const ReturnTypestateAttr *RTSAttr) {
  switch (RTSAttr->getState()) {
  case ReturnTypestateAttr::Unknown:
    return CS_Unknown;
  case ReturnTypestateAttr::Unconsumed:
    return CS_Unconsumed;
  case ReturnTypestateAttr::Consumed:
    return CS_Consumed;
  }
  llvm_unreachable("invalid enum");
}

static ConsumedState mapSetTypestateAttrState(const SetTypestateAttr *STAttr) {
  switch (STAttr->getNewState()) {
  case SetTypestateAttr::Unknown:
    return CS_Unknown;
  case SetTypestateAttr::Unconsumed:
    return CS_Unconsumed;
  case SetTypestateAttr::Consumed:
    return CS_Consumed;
  }
  llvm_unreachable("invalid_enum");
}

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");
}

static ConsumedState testsFor(const FunctionDecl *FunDecl) {
  assert(isTestingFunction(FunDecl));
  switch (FunDecl->getAttr<TestTypestateAttr>()->getTestState()) {
  case TestTypestateAttr::Unconsumed:
    return CS_Unconsumed;
  case TestTypestateAttr::Consumed:
    return CS_Consumed;
  }
  llvm_unreachable("invalid enum");
}

namespace {
struct VarTestResult {
  const VarDecl *Var;
  ConsumedState TestsFor;
};
} // end anonymous::VarTestResult

namespace clang {
namespace consumed {

enum EffectiveOp {
  EO_And,
  EO_Or
};

class PropagationInfo {
  enum {
    IT_None,
    IT_State,
    IT_VarTest,
    IT_BinTest,
    IT_Var,
    IT_Tmp
  } InfoType;

  struct BinTestTy {
    const BinaryOperator *Source;
    EffectiveOp EOp;
    VarTestResult LTest;
    VarTestResult RTest;
  };
  
  union {
    ConsumedState State;
    VarTestResult VarTest;
    const VarDecl *Var;
    const CXXBindTemporaryExpr *Tmp;
    BinTestTy BinTest;
  };
  
public:
  PropagationInfo() : InfoType(IT_None) {}
  
  PropagationInfo(const VarTestResult &VarTest)
    : InfoType(IT_VarTest), VarTest(VarTest) {}
  
  PropagationInfo(const VarDecl *Var, ConsumedState TestsFor)
    : InfoType(IT_VarTest) {
    
    VarTest.Var      = Var;
    VarTest.TestsFor = TestsFor;
  }
  
  PropagationInfo(const BinaryOperator *Source, EffectiveOp EOp,
                  const VarTestResult &LTest, const VarTestResult &RTest)
    : InfoType(IT_BinTest) {
    
    BinTest.Source  = Source;
    BinTest.EOp     = EOp;
    BinTest.LTest   = LTest;
    BinTest.RTest   = RTest;
  }
  
  PropagationInfo(const BinaryOperator *Source, EffectiveOp EOp,
                  const VarDecl *LVar, ConsumedState LTestsFor,
                  const VarDecl *RVar, ConsumedState RTestsFor)
    : InfoType(IT_BinTest) {
    
    BinTest.Source         = Source;
    BinTest.EOp            = EOp;
    BinTest.LTest.Var      = LVar;
    BinTest.LTest.TestsFor = LTestsFor;
    BinTest.RTest.Var      = RVar;
    BinTest.RTest.TestsFor = RTestsFor;
  }
  
  PropagationInfo(ConsumedState State)
    : InfoType(IT_State), State(State) {}
  
  PropagationInfo(const VarDecl *Var) : InfoType(IT_Var), Var(Var) {}
  PropagationInfo(const CXXBindTemporaryExpr *Tmp)
    : InfoType(IT_Tmp), Tmp(Tmp) {}
  
  const ConsumedState & getState() const {
    assert(InfoType == IT_State);
    return State;
  }
  
  const VarTestResult & getVarTest() const {
    assert(InfoType == IT_VarTest);
    return VarTest;
  }
  
  const VarTestResult & getLTest() const {
    assert(InfoType == IT_BinTest);
    return BinTest.LTest;
  }
  
  const VarTestResult & getRTest() const {
    assert(InfoType == IT_BinTest);
    return BinTest.RTest;
  }
  
  const VarDecl * getVar() const {
    assert(InfoType == IT_Var);
    return Var;
  }
  
  const CXXBindTemporaryExpr * getTmp() const {
    assert(InfoType == IT_Tmp);
    return Tmp;
  }
  
  ConsumedState getAsState(const ConsumedStateMap *StateMap) const {
    assert(isVar() || isTmp() || isState());
    
    if (isVar())
      return StateMap->getState(Var);
    else if (isTmp())
      return StateMap->getState(Tmp);
    else if (isState())
      return State;
    else
      return CS_None;
  }
  
  EffectiveOp testEffectiveOp() const {
    assert(InfoType == IT_BinTest);
    return BinTest.EOp;
  }
  
  const BinaryOperator * testSourceNode() const {
    assert(InfoType == IT_BinTest);
    return BinTest.Source;
  }
  
  inline bool isValid()   const { return InfoType != IT_None;    }
  inline bool isState()   const { return InfoType == IT_State;   }
  inline bool isVarTest() const { return InfoType == IT_VarTest; }
  inline bool isBinTest() const { return InfoType == IT_BinTest; }
  inline bool isVar()     const { return InfoType == IT_Var;     }
  inline bool isTmp()     const { return InfoType == IT_Tmp;     }
  
  bool isTest() const {
    return InfoType == IT_VarTest || InfoType == IT_BinTest;
  }
  
  bool isPointerToValue() const {
    return InfoType == IT_Var || InfoType == IT_Tmp;
  }
  
  PropagationInfo invertTest() const {
    assert(InfoType == IT_VarTest || InfoType == IT_BinTest);
    
    if (InfoType == IT_VarTest) {
      return PropagationInfo(VarTest.Var,
                             invertConsumedUnconsumed(VarTest.TestsFor));
    
    } else if (InfoType == IT_BinTest) {
      return PropagationInfo(BinTest.Source,
        BinTest.EOp == EO_And ? EO_Or : EO_And,
        BinTest.LTest.Var, invertConsumedUnconsumed(BinTest.LTest.TestsFor),
        BinTest.RTest.Var, invertConsumedUnconsumed(BinTest.RTest.TestsFor));
    } else {
      return PropagationInfo();
    }
  }
};

static inline void
setStateForVarOrTmp(ConsumedStateMap *StateMap, const PropagationInfo &PInfo,
                    ConsumedState State) {

  assert(PInfo.isVar() || PInfo.isTmp());
  
  if (PInfo.isVar())
    StateMap->setState(PInfo.getVar(), State);
  else
    StateMap->setState(PInfo.getTmp(), State);
}

class ConsumedStmtVisitor : public ConstStmtVisitor<ConsumedStmtVisitor> {
  
  typedef llvm::DenseMap<const Stmt *, PropagationInfo> MapType;
  typedef std::pair<const Stmt *, PropagationInfo> PairType;
  typedef MapType::iterator InfoEntry;
  typedef MapType::const_iterator ConstInfoEntry;
  
  AnalysisDeclContext &AC;
  ConsumedAnalyzer &Analyzer;
  ConsumedStateMap *StateMap;
  MapType PropagationMap;
  void forwardInfo(const Stmt *From, const Stmt *To);
  bool isLikeMoveAssignment(const CXXMethodDecl *MethodDecl);
  void propagateReturnType(const Stmt *Call, const FunctionDecl *Fun,
                           QualType ReturnType);

public:
  void checkCallability(const PropagationInfo &PInfo,
                        const FunctionDecl *FunDecl,
                        SourceLocation BlameLoc);
  
  void VisitBinaryOperator(const BinaryOperator *BinOp);
  void VisitCallExpr(const CallExpr *Call);
  void VisitCastExpr(const CastExpr *Cast);
  void VisitCXXBindTemporaryExpr(const CXXBindTemporaryExpr *Temp);
  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 VisitParmVarDecl(const ParmVarDecl *Param);
  void VisitReturnStmt(const ReturnStmt *Ret);
  void VisitUnaryOperator(const UnaryOperator *UOp);
  void VisitVarDecl(const VarDecl *Var);

  ConsumedStmtVisitor(AnalysisDeclContext &AC, ConsumedAnalyzer &Analyzer,
                      ConsumedStateMap *StateMap)
      : AC(AC), Analyzer(Analyzer), StateMap(StateMap) {}
  
  PropagationInfo getInfo(const Stmt *StmtNode) const {
    ConstInfoEntry Entry = PropagationMap.find(StmtNode);
    
    if (Entry != PropagationMap.end())
      return Entry->second;
    else
      return PropagationInfo();
  }
  
  void reset(ConsumedStateMap *NewStateMap) {
    StateMap = NewStateMap;
  }
};

void ConsumedStmtVisitor::checkCallability(const PropagationInfo &PInfo,
                                           const FunctionDecl *FunDecl,
                                           SourceLocation BlameLoc) {
  assert(!PInfo.isTest());
  
  if (!FunDecl->hasAttr<CallableWhenAttr>())
    return;
  
  const CallableWhenAttr *CWAttr = FunDecl->getAttr<CallableWhenAttr>();
  
  if (PInfo.isVar()) {
    ConsumedState VarState = StateMap->getState(PInfo.getVar());
    
    if (VarState == CS_None || isCallableInState(CWAttr, VarState))
      return;
    
    Analyzer.WarningsHandler.warnUseInInvalidState(
      FunDecl->getNameAsString(), PInfo.getVar()->getNameAsString(),
      stateToString(VarState), BlameLoc);
    
  } else {
    ConsumedState TmpState = PInfo.getAsState(StateMap);
    
    if (TmpState == CS_None || isCallableInState(CWAttr, TmpState))
      return;
    
    Analyzer.WarningsHandler.warnUseOfTempInInvalidState(
      FunDecl->getNameAsString(), stateToString(TmpState), BlameLoc);
  }
}

void ConsumedStmtVisitor::forwardInfo(const Stmt *From, const Stmt *To) {
  InfoEntry Entry = PropagationMap.find(From);
  
  if (Entry != PropagationMap.end())
    PropagationMap.insert(PairType(To, 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::propagateReturnType(const Stmt *Call,
                                              const FunctionDecl *Fun,
                                              QualType ReturnType) {
  if (isConsumableType(ReturnType)) {
    
    ConsumedState ReturnState;
    
    if (Fun->hasAttr<ReturnTypestateAttr>())
      ReturnState = mapReturnTypestateAttrState(
        Fun->getAttr<ReturnTypestateAttr>());
    else
      ReturnState = mapConsumableAttrState(ReturnType);
    
    PropagationMap.insert(PairType(Call, PropagationInfo(ReturnState)));
  }
}

void ConsumedStmtVisitor::VisitBinaryOperator(const BinaryOperator *BinOp) {
  switch (BinOp->getOpcode()) {
  case BO_LAnd:
  case BO_LOr : {
    InfoEntry LEntry = PropagationMap.find(BinOp->getLHS()),
              REntry = PropagationMap.find(BinOp->getRHS());
    
    VarTestResult LTest, RTest;
    
    if (LEntry != PropagationMap.end() && LEntry->second.isVarTest()) {
      LTest = LEntry->second.getVarTest();
      
    } else {
      LTest.Var      = NULL;
      LTest.TestsFor = CS_None;
    }
    
    if (REntry != PropagationMap.end() && REntry->second.isVarTest()) {
      RTest = REntry->second.getVarTest();
      
    } else {
      RTest.Var      = NULL;
      RTest.TestsFor = CS_None;
    }
    
    if (!(LTest.Var == NULL && RTest.Var == NULL))
      PropagationMap.insert(PairType(BinOp, PropagationInfo(BinOp,
        static_cast<EffectiveOp>(BinOp->getOpcode() == BO_LOr), LTest, RTest)));
    
    break;
  }
    
  case BO_PtrMemD:
  case BO_PtrMemI:
    forwardInfo(BinOp->getLHS(), BinOp);
    break;
    
  default:
    break;
  }
}

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") {
      forwardInfo(Call->getArg(0), Call);
      return;
    }
    
    unsigned Offset = Call->getNumArgs() - FunDecl->getNumParams();
    
    for (unsigned Index = Offset; Index < Call->getNumArgs(); ++Index) {
      const ParmVarDecl *Param = FunDecl->getParamDecl(Index - Offset);
      QualType ParamType = Param->getType();
      
      InfoEntry Entry = PropagationMap.find(Call->getArg(Index));
      
      if (Entry == PropagationMap.end() || Entry->second.isTest())
        continue;
      
      PropagationInfo PInfo = Entry->second;
      
      // Check that the parameter is in the correct state.
      
      if (Param->hasAttr<ParamTypestateAttr>()) {
        ConsumedState ParamState = PInfo.getAsState(StateMap);
        
        ConsumedState ExpectedState =
          mapParamTypestateAttrState(Param->getAttr<ParamTypestateAttr>());
        
        if (ParamState != ExpectedState)
          Analyzer.WarningsHandler.warnParamTypestateMismatch(
            Call->getArg(Index - Offset)->getExprLoc(),
            stateToString(ExpectedState), stateToString(ParamState));
      }
      
      if (!(Entry->second.isVar() || Entry->second.isTmp()))
        continue;
      
      // Adjust state on the caller side.
      
      if (isRValueRefish(ParamType)) {
        setStateForVarOrTmp(StateMap, PInfo, consumed::CS_Consumed);
        
      } else if (Param->hasAttr<ReturnTypestateAttr>()) {
        setStateForVarOrTmp(StateMap, PInfo,
          mapReturnTypestateAttrState(Param->getAttr<ReturnTypestateAttr>()));
        
      } else if (!isValueType(ParamType) &&
                 !ParamType->getPointeeType().isConstQualified()) {
        
        setStateForVarOrTmp(StateMap, PInfo, consumed::CS_Unknown);
      }
    }
    
    QualType RetType = FunDecl->getCallResultType();
    if (RetType->isReferenceType())
      RetType = RetType->getPointeeType();
    
    propagateReturnType(Call, FunDecl, RetType);
  }
}

void ConsumedStmtVisitor::VisitCastExpr(const CastExpr *Cast) {
  forwardInfo(Cast->getSubExpr(), Cast);
}

void ConsumedStmtVisitor::VisitCXXBindTemporaryExpr(
  const CXXBindTemporaryExpr *Temp) {
  
  InfoEntry Entry = PropagationMap.find(Temp->getSubExpr());
  
  if (Entry != PropagationMap.end() && !Entry->second.isTest()) {
    StateMap->setState(Temp, Entry->second.getAsState(StateMap));
    PropagationMap.insert(PairType(Temp, PropagationInfo(Temp)));
  }
}

void ConsumedStmtVisitor::VisitCXXConstructExpr(const CXXConstructExpr *Call) {
  CXXConstructorDecl *Constructor = Call->getConstructor();

  ASTContext &CurrContext = AC.getASTContext();
  QualType ThisType = Constructor->getThisType(CurrContext)->getPointeeType();
  
  if (!isConsumableType(ThisType))
    return;
  
  // FIXME: What should happen if someone annotates the move constructor?
  if (Constructor->hasAttr<ReturnTypestateAttr>()) {
    // TODO: Adjust state of args appropriately.
    
    ReturnTypestateAttr *RTAttr = Constructor->getAttr<ReturnTypestateAttr>();
    ConsumedState RetState = mapReturnTypestateAttrState(RTAttr);
    PropagationMap.insert(PairType(Call, PropagationInfo(RetState)));
    
  } else if (Constructor->isDefaultConstructor()) {
    
    PropagationMap.insert(PairType(Call,
      PropagationInfo(consumed::CS_Consumed)));
    
  } else if (Constructor->isMoveConstructor()) {
    
    InfoEntry Entry = PropagationMap.find(Call->getArg(0));
    
    if (Entry != PropagationMap.end()) {
      PropagationInfo PInfo = Entry->second;
      
      if (PInfo.isVar()) {
        const VarDecl* Var = PInfo.getVar();
        
        PropagationMap.insert(PairType(Call,
          PropagationInfo(StateMap->getState(Var))));
        
        StateMap->setState(Var, consumed::CS_Consumed);
        
      } else if (PInfo.isTmp()) {
        const CXXBindTemporaryExpr *Tmp = PInfo.getTmp();
        
        PropagationMap.insert(PairType(Call,
          PropagationInfo(StateMap->getState(Tmp))));
        
        StateMap->setState(Tmp, consumed::CS_Consumed);
        
      } else {
        PropagationMap.insert(PairType(Call, PInfo));
      }
    }
  } else if (Constructor->isCopyConstructor()) {
    forwardInfo(Call->getArg(0), Call);
    
  } else {
    // TODO: Adjust state of args appropriately.
    
    ConsumedState RetState = mapConsumableAttrState(ThisType);
    PropagationMap.insert(PairType(Call, PropagationInfo(RetState)));
  }
}

void ConsumedStmtVisitor::VisitCXXMemberCallExpr(
  const CXXMemberCallExpr *Call) {
  
  VisitCallExpr(Call);
  
  InfoEntry Entry = PropagationMap.find(Call->getCallee()->IgnoreParens());
  
  if (Entry != PropagationMap.end()) {
    PropagationInfo PInfo = Entry->second;
    const CXXMethodDecl *MethodDecl = Call->getMethodDecl();
    
    checkCallability(PInfo, MethodDecl, Call->getExprLoc());
    
    if (PInfo.isVar()) {
      if (isTestingFunction(MethodDecl))
        PropagationMap.insert(PairType(Call,
          PropagationInfo(PInfo.getVar(), testsFor(MethodDecl))));
      else if (MethodDecl->hasAttr<SetTypestateAttr>())
        StateMap->setState(PInfo.getVar(),
          mapSetTypestateAttrState(MethodDecl->getAttr<SetTypestateAttr>()));
    } else if (PInfo.isTmp() && MethodDecl->hasAttr<SetTypestateAttr>()) {
      StateMap->setState(PInfo.getTmp(),
        mapSetTypestateAttrState(MethodDecl->getAttr<SetTypestateAttr>()));
    }
  }
}

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 LPInfo, RPInfo;
    
    if (LEntry != PropagationMap.end() &&
        REntry != PropagationMap.end()) {
      
      LPInfo = LEntry->second;
      RPInfo = REntry->second;
      
      if (LPInfo.isPointerToValue() && RPInfo.isPointerToValue()) {
        setStateForVarOrTmp(StateMap, LPInfo, RPInfo.getAsState(StateMap));
        PropagationMap.insert(PairType(Call, LPInfo));
        setStateForVarOrTmp(StateMap, RPInfo, consumed::CS_Consumed);
        
      } else if (RPInfo.isState()) {
        setStateForVarOrTmp(StateMap, LPInfo, RPInfo.getState());
        PropagationMap.insert(PairType(Call, LPInfo));
        
      } else {
        setStateForVarOrTmp(StateMap, RPInfo, consumed::CS_Consumed);
      }
      
    } else if (LEntry != PropagationMap.end() &&
               REntry == PropagationMap.end()) {
      
      LPInfo = LEntry->second;
      
      assert(!LPInfo.isTest());
      
      if (LPInfo.isPointerToValue()) {
        setStateForVarOrTmp(StateMap, LPInfo, consumed::CS_Unknown);
        PropagationMap.insert(PairType(Call, LPInfo));
        
      } else {
        PropagationMap.insert(PairType(Call,
          PropagationInfo(consumed::CS_Unknown)));
      }
      
    } else if (LEntry == PropagationMap.end() &&
               REntry != PropagationMap.end()) {
      
      RPInfo = REntry->second;
      
      if (RPInfo.isPointerToValue())
        setStateForVarOrTmp(StateMap, RPInfo, consumed::CS_Consumed);
    }
    
  } else {
    
    VisitCallExpr(Call);
    
    InfoEntry Entry = PropagationMap.find(Call->getArg(0));
    
    if (Entry != PropagationMap.end()) {
      PropagationInfo PInfo = Entry->second;
      
      checkCallability(PInfo, FunDecl, Call->getExprLoc());
      
      if (PInfo.isVar()) {
        if (isTestingFunction(FunDecl))
          PropagationMap.insert(PairType(Call,
            PropagationInfo(PInfo.getVar(), testsFor(FunDecl))));
        else if (FunDecl->hasAttr<SetTypestateAttr>())
          StateMap->setState(PInfo.getVar(),
            mapSetTypestateAttrState(FunDecl->getAttr<SetTypestateAttr>()));
        
      } else if (PInfo.isTmp() && FunDecl->hasAttr<SetTypestateAttr>()) {
        StateMap->setState(PInfo.getTmp(),
          mapSetTypestateAttrState(FunDecl->getAttr<SetTypestateAttr>()));
    }
    }
  }
}

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) {
  
  forwardInfo(Temp->GetTemporaryExpr(), Temp);
}

void ConsumedStmtVisitor::VisitMemberExpr(const MemberExpr *MExpr) {
  forwardInfo(MExpr->getBase(), MExpr);
}


void ConsumedStmtVisitor::VisitParmVarDecl(const ParmVarDecl *Param) {
  QualType ParamType = Param->getType();
  ConsumedState ParamState = consumed::CS_None;
  
  if (Param->hasAttr<ParamTypestateAttr>()) {
    const ParamTypestateAttr *PTAttr = Param->getAttr<ParamTypestateAttr>();
    ParamState = mapParamTypestateAttrState(PTAttr);
    
  } else if (isConsumableType(ParamType)) {
    ParamState = mapConsumableAttrState(ParamType);
    
  } else if (isRValueRefish(ParamType) &&
             isConsumableType(ParamType->getPointeeType())) {
    
    ParamState = mapConsumableAttrState(ParamType->getPointeeType());
    
  } else if (ParamType->isReferenceType() &&
             isConsumableType(ParamType->getPointeeType())) {
    ParamState = consumed::CS_Unknown;
  }
  
  if (ParamState != CS_None)
    StateMap->setState(Param, ParamState);
}

void ConsumedStmtVisitor::VisitReturnStmt(const ReturnStmt *Ret) {
  ConsumedState ExpectedState = Analyzer.getExpectedReturnState();
  
  if (ExpectedState != CS_None) {
    InfoEntry Entry = PropagationMap.find(Ret->getRetValue());
    
    if (Entry != PropagationMap.end()) {
      ConsumedState RetState = Entry->second.getAsState(StateMap);
        
      if (RetState != ExpectedState)
        Analyzer.WarningsHandler.warnReturnTypestateMismatch(
          Ret->getReturnLoc(), stateToString(ExpectedState),
          stateToString(RetState));
    }
  }
  
  StateMap->checkParamsForReturnTypestate(Ret->getLocStart(),
                                          Analyzer.WarningsHandler);
}

void ConsumedStmtVisitor::VisitUnaryOperator(const UnaryOperator *UOp) {
  InfoEntry Entry = PropagationMap.find(UOp->getSubExpr()->IgnoreParens());
  if (Entry == PropagationMap.end()) return;
  
  switch (UOp->getOpcode()) {
  case UO_AddrOf:
    PropagationMap.insert(PairType(UOp, Entry->second));
    break;
  
  case UO_LNot:
    if (Entry->second.isTest())
      PropagationMap.insert(PairType(UOp, Entry->second.invertTest()));
    break;
  
  default:
    break;
  }
}

// TODO: See if I need to check for reference types here.
void ConsumedStmtVisitor::VisitVarDecl(const VarDecl *Var) {
  if (isConsumableType(Var->getType())) {
    if (Var->hasInit()) {
      MapType::iterator VIT = PropagationMap.find(
        Var->getInit()->IgnoreImplicit());
      if (VIT != PropagationMap.end()) {
        PropagationInfo PInfo = VIT->second;
        ConsumedState St = PInfo.getAsState(StateMap);
        
        if (St != consumed::CS_None) {
          StateMap->setState(Var, St);
          return;
        }
      }
    }
    // Otherwise
    StateMap->setState(Var, consumed::CS_Unknown);
  }
}
}} // end clang::consumed::ConsumedStmtVisitor

namespace clang {
namespace consumed {

void splitVarStateForIf(const IfStmt * IfNode, const VarTestResult &Test,
                        ConsumedStateMap *ThenStates,
                        ConsumedStateMap *ElseStates) {

  ConsumedState VarState = ThenStates->getState(Test.Var);
  
  if (VarState == CS_Unknown) {
    ThenStates->setState(Test.Var, Test.TestsFor);
    ElseStates->setState(Test.Var, invertConsumedUnconsumed(Test.TestsFor));
  
  } else if (VarState == invertConsumedUnconsumed(Test.TestsFor)) {
    ThenStates->markUnreachable();
    
  } else if (VarState == Test.TestsFor) {
    ElseStates->markUnreachable();
  }
}

void splitVarStateForIfBinOp(const PropagationInfo &PInfo,
  ConsumedStateMap *ThenStates, ConsumedStateMap *ElseStates) {
  
  const VarTestResult &LTest = PInfo.getLTest(),
                      &RTest = PInfo.getRTest();
  
  ConsumedState LState = LTest.Var ? ThenStates->getState(LTest.Var) : CS_None,
                RState = RTest.Var ? ThenStates->getState(RTest.Var) : CS_None;
  
  if (LTest.Var) {
    if (PInfo.testEffectiveOp() == EO_And) {
      if (LState == CS_Unknown) {
        ThenStates->setState(LTest.Var, LTest.TestsFor);
        
      } else if (LState == invertConsumedUnconsumed(LTest.TestsFor)) {
        ThenStates->markUnreachable();
        
      } else if (LState == LTest.TestsFor && isKnownState(RState)) {
        if (RState == RTest.TestsFor)
          ElseStates->markUnreachable();
        else
          ThenStates->markUnreachable();
      }
      
    } else {
      if (LState == CS_Unknown) {
        ElseStates->setState(LTest.Var,
                             invertConsumedUnconsumed(LTest.TestsFor));
      
      } else if (LState == LTest.TestsFor) {
        ElseStates->markUnreachable();
        
      } else if (LState == invertConsumedUnconsumed(LTest.TestsFor) &&
                 isKnownState(RState)) {
        
        if (RState == RTest.TestsFor)
          ElseStates->markUnreachable();
        else
          ThenStates->markUnreachable();
      }
    }
  }
  
  if (RTest.Var) {
    if (PInfo.testEffectiveOp() == EO_And) {
      if (RState == CS_Unknown)
        ThenStates->setState(RTest.Var, RTest.TestsFor);
      else if (RState == invertConsumedUnconsumed(RTest.TestsFor))
        ThenStates->markUnreachable();
      
    } else {
      if (RState == CS_Unknown)
        ElseStates->setState(RTest.Var,
                             invertConsumedUnconsumed(RTest.TestsFor));
      else if (RState == RTest.TestsFor)
        ElseStates->markUnreachable();
    }
  }
}

bool ConsumedBlockInfo::allBackEdgesVisited(const CFGBlock *CurrBlock,
                                            const CFGBlock *TargetBlock) {
  
  assert(CurrBlock && "Block pointer must not be NULL");
  assert(TargetBlock && "TargetBlock pointer must not be NULL");
  
  unsigned int CurrBlockOrder = VisitOrder[CurrBlock->getBlockID()];
  for (CFGBlock::const_pred_iterator PI = TargetBlock->pred_begin(),
       PE = TargetBlock->pred_end(); PI != PE; ++PI) {
    if (*PI && CurrBlockOrder < VisitOrder[(*PI)->getBlockID()] )
      return false;
  }
  return true;
}

void ConsumedBlockInfo::addInfo(const CFGBlock *Block,
                                ConsumedStateMap *StateMap,
                                bool &AlreadyOwned) {
  
  assert(Block && "Block pointer must not be NULL");
  
  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) {
  
  assert(Block != NULL && "Block pointer must not be NULL");
  
  ConsumedStateMap *Entry = StateMapsArray[Block->getBlockID()];
    
  if (Entry) {
    Entry->intersect(StateMap);
    delete StateMap;
    
  } else {
    StateMapsArray[Block->getBlockID()] = StateMap;
  }
}

ConsumedStateMap* ConsumedBlockInfo::borrowInfo(const CFGBlock *Block) {
  assert(Block && "Block pointer must not be NULL");
  assert(StateMapsArray[Block->getBlockID()] && "Block has no block info");
  
  return StateMapsArray[Block->getBlockID()];
}

void ConsumedBlockInfo::discardInfo(const CFGBlock *Block) {
  unsigned int BlockID = Block->getBlockID();
  delete StateMapsArray[BlockID];
  StateMapsArray[BlockID] = NULL;
}

ConsumedStateMap* ConsumedBlockInfo::getInfo(const CFGBlock *Block) {
  assert(Block && "Block pointer must not be NULL");
  
  ConsumedStateMap *StateMap = StateMapsArray[Block->getBlockID()];
  if (isBackEdgeTarget(Block)) {
    return new ConsumedStateMap(*StateMap);
  } else {
    StateMapsArray[Block->getBlockID()] = NULL;
    return StateMap;
  }
}

bool ConsumedBlockInfo::isBackEdge(const CFGBlock *From, const CFGBlock *To) {
  assert(From && "From block must not be NULL");
  assert(To   && "From block must not be NULL");
  
  return VisitOrder[From->getBlockID()] > VisitOrder[To->getBlockID()];
}

bool ConsumedBlockInfo::isBackEdgeTarget(const CFGBlock *Block) {
  assert(Block != NULL && "Block pointer must not be NULL");
  
  // Anything with less than two predecessors can't be the target of a back
  // edge.
  if (Block->pred_size() < 2)
    return false;
  
  unsigned int BlockVisitOrder = VisitOrder[Block->getBlockID()];
  for (CFGBlock::const_pred_iterator PI = Block->pred_begin(),
       PE = Block->pred_end(); PI != PE; ++PI) {
    if (*PI && BlockVisitOrder < VisitOrder[(*PI)->getBlockID()])
      return true;
  }
  return false;
}

void ConsumedStateMap::checkParamsForReturnTypestate(SourceLocation BlameLoc,
  ConsumedWarningsHandlerBase &WarningsHandler) const {
  
  ConsumedState ExpectedState;
  
  for (VarMapType::const_iterator DMI = VarMap.begin(), DME = VarMap.end();
       DMI != DME; ++DMI) {
    
    if (isa<ParmVarDecl>(DMI->first)) {
      const ParmVarDecl *Param = cast<ParmVarDecl>(DMI->first);
      
      if (!Param->hasAttr<ReturnTypestateAttr>()) continue;
      
      ExpectedState =
        mapReturnTypestateAttrState(Param->getAttr<ReturnTypestateAttr>());
      
      if (DMI->second != ExpectedState) {
        WarningsHandler.warnParamReturnTypestateMismatch(BlameLoc,
          Param->getNameAsString(), stateToString(ExpectedState),
          stateToString(DMI->second));
      }
    }
  }
}

void ConsumedStateMap::clearTemporaries() {
  TmpMap.clear();
}

ConsumedState ConsumedStateMap::getState(const VarDecl *Var) const {
  VarMapType::const_iterator Entry = VarMap.find(Var);
  
  if (Entry != VarMap.end())
    return Entry->second;
    
  return CS_None;
}

ConsumedState
ConsumedStateMap::getState(const CXXBindTemporaryExpr *Tmp) const {
  TmpMapType::const_iterator Entry = TmpMap.find(Tmp);
  
  if (Entry != TmpMap.end())
    return Entry->second;
  
  return CS_None;
}

void ConsumedStateMap::intersect(const ConsumedStateMap *Other) {
  ConsumedState LocalState;
  
  if (this->From && this->From == Other->From && !Other->Reachable) {
    this->markUnreachable();
    return;
  }
  
  for (VarMapType::const_iterator DMI = Other->VarMap.begin(),
       DME = Other->VarMap.end(); DMI != DME; ++DMI) {
    
    LocalState = this->getState(DMI->first);
    
    if (LocalState == CS_None)
      continue;
    
    if (LocalState != DMI->second)
       VarMap[DMI->first] = CS_Unknown;
  }
}

void ConsumedStateMap::intersectAtLoopHead(const CFGBlock *LoopHead,
  const CFGBlock *LoopBack, const ConsumedStateMap *LoopBackStates,
  ConsumedWarningsHandlerBase &WarningsHandler) {
  
  ConsumedState LocalState;
  SourceLocation BlameLoc = getLastStmtLoc(LoopBack);
  
  for (VarMapType::const_iterator DMI = LoopBackStates->VarMap.begin(),
       DME = LoopBackStates->VarMap.end(); DMI != DME; ++DMI) {
    
    LocalState = this->getState(DMI->first);
    
    if (LocalState == CS_None)
      continue;
    
    if (LocalState != DMI->second) {
      VarMap[DMI->first] = CS_Unknown;
      WarningsHandler.warnLoopStateMismatch(
        BlameLoc, DMI->first->getNameAsString());
    }
  }
}

void ConsumedStateMap::markUnreachable() {
  this->Reachable = false;
  VarMap.clear();
  TmpMap.clear();
}

void ConsumedStateMap::setState(const VarDecl *Var, ConsumedState State) {
  VarMap[Var] = State;
}

void ConsumedStateMap::setState(const CXXBindTemporaryExpr *Tmp,
                                ConsumedState State) {
  TmpMap[Tmp] = State;
}

void ConsumedStateMap::remove(const VarDecl *Var) {
  VarMap.erase(Var);
}

bool ConsumedStateMap::operator!=(const ConsumedStateMap *Other) const {
  for (VarMapType::const_iterator DMI = Other->VarMap.begin(),
       DME = Other->VarMap.end(); DMI != DME; ++DMI) {
    
    if (this->getState(DMI->first) != DMI->second)
      return true;
  }
  
  return false;
}

void ConsumedAnalyzer::determineExpectedReturnState(AnalysisDeclContext &AC,
                                                    const FunctionDecl *D) {
  QualType ReturnType;
  if (const CXXConstructorDecl *Constructor = dyn_cast<CXXConstructorDecl>(D)) {
    ASTContext &CurrContext = AC.getASTContext();
    ReturnType = Constructor->getThisType(CurrContext)->getPointeeType();
  } else
    ReturnType = D->getCallResultType();

  if (D->hasAttr<ReturnTypestateAttr>()) {
    const ReturnTypestateAttr *RTSAttr = D->getAttr<ReturnTypestateAttr>();

    const CXXRecordDecl *RD = ReturnType->getAsCXXRecordDecl();
    if (!RD || !RD->hasAttr<ConsumableAttr>()) {
      // FIXME: This should be removed when template instantiation propagates
      //        attributes at template specialization definition, not
      //        declaration. When it is removed the test needs to be enabled
      //        in SemaDeclAttr.cpp.
      WarningsHandler.warnReturnTypestateForUnconsumableType(
          RTSAttr->getLocation(), ReturnType.getAsString());
      ExpectedReturnState = CS_None;
    } else
      ExpectedReturnState = mapReturnTypestateAttrState(RTSAttr);
  } else if (isConsumableType(ReturnType))
    ExpectedReturnState = mapConsumableAttrState(ReturnType);
  else
    ExpectedReturnState = CS_None;
}

bool ConsumedAnalyzer::splitState(const CFGBlock *CurrBlock,
                                  const ConsumedStmtVisitor &Visitor) {
  
  OwningPtr<ConsumedStateMap> FalseStates(new ConsumedStateMap(*CurrStates));
  PropagationInfo PInfo;
  
  if (const IfStmt *IfNode =
    dyn_cast_or_null<IfStmt>(CurrBlock->getTerminator().getStmt())) {
    
    const Stmt *Cond = IfNode->getCond();
    
    PInfo = Visitor.getInfo(Cond);
    if (!PInfo.isValid() && isa<BinaryOperator>(Cond))
      PInfo = Visitor.getInfo(cast<BinaryOperator>(Cond)->getRHS());
    
    if (PInfo.isVarTest()) {
      CurrStates->setSource(Cond);
      FalseStates->setSource(Cond);
      splitVarStateForIf(IfNode, PInfo.getVarTest(), CurrStates,
                         FalseStates.get());
      
    } else if (PInfo.isBinTest()) {
      CurrStates->setSource(PInfo.testSourceNode());
      FalseStates->setSource(PInfo.testSourceNode());
      splitVarStateForIfBinOp(PInfo, CurrStates, FalseStates.get());
      
    } else {
      return false;
    }
    
  } else if (const BinaryOperator *BinOp =
    dyn_cast_or_null<BinaryOperator>(CurrBlock->getTerminator().getStmt())) {
    
    PInfo = Visitor.getInfo(BinOp->getLHS());
    if (!PInfo.isVarTest()) {
      if ((BinOp = dyn_cast_or_null<BinaryOperator>(BinOp->getLHS()))) {
        PInfo = Visitor.getInfo(BinOp->getRHS());
        
        if (!PInfo.isVarTest())
          return false;
        
      } else {
        return false;
      }
    }
    
    CurrStates->setSource(BinOp);
    FalseStates->setSource(BinOp);
    
    const VarTestResult &Test = PInfo.getVarTest();
    ConsumedState VarState = CurrStates->getState(Test.Var);
    
    if (BinOp->getOpcode() == BO_LAnd) {
      if (VarState == CS_Unknown)
        CurrStates->setState(Test.Var, Test.TestsFor);
      else if (VarState == invertConsumedUnconsumed(Test.TestsFor))
        CurrStates->markUnreachable();
      
    } else if (BinOp->getOpcode() == BO_LOr) {
      if (VarState == CS_Unknown)
        FalseStates->setState(Test.Var,
                              invertConsumedUnconsumed(Test.TestsFor));
      else if (VarState == Test.TestsFor)
        FalseStates->markUnreachable();
    }
    
  } else {
    return false;
  }
  
  CFGBlock::const_succ_iterator SI = CurrBlock->succ_begin();
  
  if (*SI)
    BlockInfo.addInfo(*SI, CurrStates);
  else
    delete CurrStates;
    
  if (*++SI)
    BlockInfo.addInfo(*SI, FalseStates.take());
  
  CurrStates = NULL;
  return true;
}

void ConsumedAnalyzer::run(AnalysisDeclContext &AC) {
  const FunctionDecl *D = dyn_cast_or_null<FunctionDecl>(AC.getDecl());
  if (!D)
    return;
  
  CFG *CFGraph = AC.getCFG();
  if (!CFGraph)
    return;

  determineExpectedReturnState(AC, D);

  PostOrderCFGView *SortedGraph = AC.getAnalysis<PostOrderCFGView>();
  // AC.getCFG()->viewCFG(LangOptions());
  
  BlockInfo = ConsumedBlockInfo(CFGraph->getNumBlockIDs(), SortedGraph);
  
  CurrStates = new ConsumedStateMap();
  ConsumedStmtVisitor Visitor(AC, *this, CurrStates);
  
  // Add all trackable parameters to the state map.
  for (FunctionDecl::param_const_iterator PI = D->param_begin(),
       PE = D->param_end(); PI != PE; ++PI) {
    Visitor.VisitParmVarDecl(*PI);
  }
  
  // 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);
    
    if (!CurrStates) {
      continue;
      
    } else if (!CurrStates->isReachable()) {
      delete CurrStates;
      CurrStates = NULL;
      continue;
    }
    
    Visitor.reset(CurrStates);
    
    // Visit all of the basic block's statements.
    for (CFGBlock::const_iterator BI = CurrBlock->begin(),
         BE = CurrBlock->end(); BI != BE; ++BI) {
      
      switch (BI->getKind()) {
      case CFGElement::Statement:
        Visitor.Visit(BI->castAs<CFGStmt>().getStmt());
        break;
        
      case CFGElement::TemporaryDtor: {
        const CFGTemporaryDtor DTor = BI->castAs<CFGTemporaryDtor>();
        const CXXBindTemporaryExpr *BTE = DTor.getBindTemporaryExpr();
        
        Visitor.checkCallability(PropagationInfo(BTE),
                                 DTor.getDestructorDecl(AC.getASTContext()),
                                 BTE->getExprLoc());
        break;
      }
      
      case CFGElement::AutomaticObjectDtor: {
        const CFGAutomaticObjDtor DTor = BI->castAs<CFGAutomaticObjDtor>();
        SourceLocation Loc = DTor.getTriggerStmt()->getLocEnd();
        const VarDecl *Var = DTor.getVarDecl();
        
        Visitor.checkCallability(PropagationInfo(Var),
                                 DTor.getDestructorDecl(AC.getASTContext()),
                                 Loc);
        break;
      }
      
      default:
        break;
      }
    }
    
    CurrStates->clearTemporaries();
    
    // TODO: Handle other forms of branching with precision, including while-
    //       and for-loops. (Deferred)
    if (!splitState(CurrBlock, Visitor)) {
      CurrStates->setSource(NULL);
      
      if (CurrBlock->succ_size() > 1 ||
          (CurrBlock->succ_size() == 1 &&
           (*CurrBlock->succ_begin())->pred_size() > 1)) {
        
        bool OwnershipTaken = false;
        
        for (CFGBlock::const_succ_iterator SI = CurrBlock->succ_begin(),
             SE = CurrBlock->succ_end(); SI != SE; ++SI) {
          
          if (*SI == NULL) continue;
          
          if (BlockInfo.isBackEdge(CurrBlock, *SI)) {
            BlockInfo.borrowInfo(*SI)->intersectAtLoopHead(*SI, CurrBlock,
                                                           CurrStates,
                                                           WarningsHandler);
            
            if (BlockInfo.allBackEdgesVisited(*SI, CurrBlock))
              BlockInfo.discardInfo(*SI);
          } else {
            BlockInfo.addInfo(*SI, CurrStates, OwnershipTaken);
          }
        }
        
        if (!OwnershipTaken)
          delete CurrStates;
        
        CurrStates = NULL;
      }
    }
    
    if (CurrBlock == &AC.getCFG()->getExit() &&
        D->getCallResultType()->isVoidType())
      CurrStates->checkParamsForReturnTypestate(D->getLocation(),
                                                WarningsHandler);
  } // End of block iterator.
  
  // Delete the last existing state map.
  delete CurrStates;
  
  WarningsHandler.emitDiagnostics();
}
}} // end namespace clang::consumed
@


1.1.1.1
log
@Import Clang 3.4rc1 r195771.
@
text
@@


1.1.1.2
log
@Import clang 3.5svn r198450.
@
text
@d507 3
a510 2
  if (!CWAttr)
    return;
d556 3
a558 2
    if (ReturnTypestateAttr *RTA = Fun->getAttr<ReturnTypestateAttr>())
      ReturnState = mapReturnTypestateAttrState(RTA);
a607 10
static bool isStdNamespace(const DeclContext *DC) {
  if (!DC->isNamespace()) return false;
  while (DC->getParent()->isNamespace())
    DC = DC->getParent();
  const NamespaceDecl *ND = dyn_cast<NamespaceDecl>(DC);

  return ND && ND->getName() == "std" &&
         ND->getDeclContext()->isTranslationUnit();
}

d614 1
a614 3
    if (Call->getNumArgs() == 1 &&
        FunDecl->getNameAsString() == "move" &&
        isStdNamespace(FunDecl->getDeclContext())) {
d634 5
a638 3
      if (ParamTypestateAttr *PTA = Param->getAttr<ParamTypestateAttr>()) {
        ConsumedState ParamState = PInfo.getAsState(StateMap);        
        ConsumedState ExpectedState = mapParamTypestateAttrState(PTA);
d651 1
a651 1
      if (isRValueRefish(ParamType))
d653 8
a660 4
      else if (ReturnTypestateAttr *RT = Param->getAttr<ReturnTypestateAttr>())
        setStateForVarOrTmp(StateMap, PInfo, mapReturnTypestateAttrState(RT));
      else if (!isValueType(ParamType) &&
               !ParamType->getPointeeType().isConstQualified())
d662 1
d698 9
a706 5
  if (ReturnTypestateAttr *RTA = Constructor->getAttr<ReturnTypestateAttr>()) {
    // TODO: Adjust state of args appropriately.    
    ConsumedState RetState = mapReturnTypestateAttrState(RTA);
    PropagationMap.insert(PairType(Call, PropagationInfo(RetState)));    
  } else if (Constructor->isDefaultConstructor()) {    
d709 3
a711 1
  } else if (Constructor->isMoveConstructor()) {    
a760 1
    SetTypestateAttr *STA = MethodDecl->getAttr<SetTypestateAttr>();
d765 7
a771 4
      else if (STA)
        StateMap->setState(PInfo.getVar(), mapSetTypestateAttrState(STA));
    } else if (STA && PInfo.isTmp())
      StateMap->setState(PInfo.getTmp(), mapSetTypestateAttrState(STA));
a845 1
      SetTypestateAttr *STA = FunDecl->getAttr<SetTypestateAttr>();
d850 8
a857 4
        else if (STA)
          StateMap->setState(PInfo.getVar(), mapSetTypestateAttrState(STA));        
      } else if (STA && PInfo.isTmp())
        StateMap->setState(PInfo.getTmp(), mapSetTypestateAttrState(STA));
d895 14
a908 9
  if (const ParamTypestateAttr *PTA = Param->getAttr<ParamTypestateAttr>())
    ParamState = mapParamTypestateAttrState(PTA);    
  else if (isConsumableType(ParamType))
    ParamState = mapConsumableAttrState(ParamType);    
  else if (isRValueRefish(ParamType) &&
             isConsumableType(ParamType->getPointeeType()))    
    ParamState = mapConsumableAttrState(ParamType->getPointeeType());    
  else if (ParamType->isReferenceType() &&
             isConsumableType(ParamType->getPointeeType()))
d910 1
d1161 2
a1167 1
      const ReturnTypestateAttr *RTA = Param->getAttr<ReturnTypestateAttr>();
d1169 4
a1172 2
      if (!RTA)
        continue;
d1174 1
a1174 2
      ConsumedState ExpectedState = mapReturnTypestateAttrState(RTA);      
      if (DMI->second != ExpectedState)
d1178 1
d1289 3
a1291 1
  if (const ReturnTypestateAttr *RTSAttr = D->getAttr<ReturnTypestateAttr>()) {
@


1.1.1.3
log
@Import Clang 3.5svn r199312
@
text
@d20 1
a21 1
#include "clang/AST/StmtVisitor.h"
a22 1
#include "clang/Analysis/Analyses/Consumed.h"
d26 1
a145 1

a155 17
static bool isAutoCastType(const QualType &QT) {
  if (QT->isPointerType() || QT->isReferenceType())
    return false;

  if (const CXXRecordDecl *RD = QT->getAsCXXRecordDecl())
    return RD->hasAttr<ConsumableAutoCastAttr>();

  return false;
}

static bool isSetOnReadPtrType(const QualType &QT) {
  if (const CXXRecordDecl *RD = QT->getPointeeCXXRecordDecl())
    return RD->hasAttr<ConsumableSetOnReadAttr>();
  return false;
}


d169 1
a169 1
  return ParamType->isRValueReferenceType(); /* ||
d172 1
a172 1
           ParamType.getCanonicalType())->isSpelledAsLValue()); */
d179 2
a180 2
static bool isPointerOrRef(QualType ParamType) {
  return ParamType->isPointerType() || ParamType->isReferenceType();
a457 1

d459 3
a461 4
  void copyInfo(const Stmt *From, const Stmt *To, ConsumedState CS);
  ConsumedState getInfo(const Stmt *From);
  void setInfo(const Stmt *To, ConsumedState NS);
  void propagateReturnType(const Stmt *Call, const FunctionDecl *Fun);
a466 2
  bool handleCall(const CallExpr *Call, const Expr *ObjArg,
                  const FunctionDecl *FunD);
a501 49

void ConsumedStmtVisitor::forwardInfo(const Stmt *From, const Stmt *To) {
  InfoEntry Entry = PropagationMap.find(From);
  if (Entry != PropagationMap.end())
    PropagationMap.insert(PairType(To, Entry->second));
}


// Create a new state for To, which is initialized to the state of From.
// If NS is not CS_None, sets the state of From to NS.
void ConsumedStmtVisitor::copyInfo(const Stmt *From, const Stmt *To,
                                   ConsumedState NS) {
  InfoEntry Entry = PropagationMap.find(From);
  if (Entry != PropagationMap.end()) {
    PropagationInfo& PInfo = Entry->second;
    ConsumedState CS = PInfo.getAsState(StateMap);
    if (CS != CS_None)
      PropagationMap.insert(PairType(To, CS));
    if (NS != CS_None && PInfo.isPointerToValue())
      setStateForVarOrTmp(StateMap, PInfo, NS);
  }
}


// Get the ConsumedState for From
ConsumedState ConsumedStmtVisitor::getInfo(const Stmt *From) {
  InfoEntry Entry = PropagationMap.find(From);
  if (Entry != PropagationMap.end()) {
    PropagationInfo& PInfo = Entry->second;
    return PInfo.getAsState(StateMap);
  }
  return CS_None;
}


// If we already have info for To then update it, otherwise create a new entry.
void ConsumedStmtVisitor::setInfo(const Stmt *To, ConsumedState NS) {
  InfoEntry Entry = PropagationMap.find(To);
  if (Entry != PropagationMap.end()) {
    PropagationInfo& PInfo = Entry->second;
    if (PInfo.isPointerToValue())
      setStateForVarOrTmp(StateMap, PInfo, NS);
  } else if (NS != CS_None) {
     PropagationMap.insert(PairType(To, PropagationInfo(NS)));
  }
}



d506 1
a506 1

d510 1
a510 1

d513 1
a513 1

d516 1
a516 1

d520 1
a520 1

d523 1
a523 1

d526 1
a526 1

d532 6
d539 7
a545 75
// Factors out common behavior for function, method, and operator calls.
// Check parameters and set parameter state if necessary.
// Returns true if the state of ObjArg is set, or false otherwise.
bool ConsumedStmtVisitor::handleCall(const CallExpr *Call, const Expr *ObjArg,
                                     const FunctionDecl *FunD) {
  unsigned Offset = 0;
  if (isa<CXXMethodDecl>(FunD))
    Offset = 1;  // First argument to call is 'this' parameter

  // check explicit parameters
  for (unsigned Index = Offset; Index < Call->getNumArgs(); ++Index) {
    // Skip variable argument lists.
    if (Index - Offset >= FunD->getNumParams())
      break;

    const ParmVarDecl *Param = FunD->getParamDecl(Index - Offset);
    QualType ParamType = Param->getType();

    InfoEntry Entry = PropagationMap.find(Call->getArg(Index));

    if (Entry == PropagationMap.end() || Entry->second.isTest())
      continue;
    PropagationInfo PInfo = Entry->second;

    // Check that the parameter is in the correct state.
    if (ParamTypestateAttr *PTA = Param->getAttr<ParamTypestateAttr>()) {
      ConsumedState ParamState = PInfo.getAsState(StateMap);
      ConsumedState ExpectedState = mapParamTypestateAttrState(PTA);

      if (ParamState != ExpectedState)
        Analyzer.WarningsHandler.warnParamTypestateMismatch(
          Call->getArg(Index)->getExprLoc(),
          stateToString(ExpectedState), stateToString(ParamState));
    }

    if (!(Entry->second.isVar() || Entry->second.isTmp()))
      continue;

    // Adjust state on the caller side.
    if (isRValueRefish(ParamType))
      setStateForVarOrTmp(StateMap, PInfo, consumed::CS_Consumed);
    else if (ReturnTypestateAttr *RT = Param->getAttr<ReturnTypestateAttr>())
      setStateForVarOrTmp(StateMap, PInfo, mapReturnTypestateAttrState(RT));
    else if (isPointerOrRef(ParamType)) {
      if (!ParamType->getPointeeType().isConstQualified() ||
          isSetOnReadPtrType(ParamType))
        setStateForVarOrTmp(StateMap, PInfo, consumed::CS_Unknown);
    }
  }

  if (!ObjArg)
    return false;

  // check implicit 'self' parameter, if present
  InfoEntry Entry = PropagationMap.find(ObjArg);
  if (Entry != PropagationMap.end()) {
    PropagationInfo PInfo = Entry->second;
    checkCallability(PInfo, FunD, Call->getExprLoc());

    if (SetTypestateAttr *STA = FunD->getAttr<SetTypestateAttr>()) {
      if (PInfo.isVar()) {
        StateMap->setState(PInfo.getVar(), mapSetTypestateAttrState(STA));
        return true;
      }
      else if (PInfo.isTmp()) {
        StateMap->setState(PInfo.getTmp(), mapSetTypestateAttrState(STA));
        return true;
      }
    }
    else if (isTestingFunction(FunD) && PInfo.isVar()) {
      PropagationMap.insert(PairType(Call,
        PropagationInfo(PInfo.getVar(), testsFor(FunD))));
    }
  }
  return false;
a547 1

d549 4
a552 6
                                              const FunctionDecl *Fun) {
  QualType RetType = Fun->getCallResultType();
  if (RetType->isReferenceType())
    RetType = RetType->getPointeeType();

  if (isConsumableType(RetType)) {
d554 1
d558 1
a558 1
      ReturnState = mapConsumableAttrState(RetType);
a563 1

d617 56
a672 11
  const FunctionDecl *FunDecl = Call->getDirectCallee();
  if (!FunDecl)
    return;

  // Special case for the std::move function.
  // TODO: Make this more specific. (Deferred)
  if (Call->getNumArgs() == 1 &&
      FunDecl->getNameAsString() == "move" &&
      isStdNamespace(FunDecl->getDeclContext())) {
    copyInfo(Call->getArg(0), Call, CS_Consumed);
    return;
a673 3

  handleCall(Call, 0, FunDecl);
  propagateReturnType(Call, FunDecl);
d704 2
a705 2
    PropagationMap.insert(PairType(Call, PropagationInfo(RetState)));
  } else if (Constructor->isDefaultConstructor()) {
d708 26
a733 2
  } else if (Constructor->isMoveConstructor()) {
    copyInfo(Call->getArg(0), Call, CS_Consumed);
d735 2
a736 5
    // Copy state from arg.  If setStateOnRead then set arg to CS_Unknown.
    ConsumedState NS =
      isSetOnReadPtrType(Constructor->getThisType(CurrContext)) ?
      CS_Unknown : CS_None;
    copyInfo(Call->getArg(0), Call, NS);
d739 1
a744 1

d746 22
a767 7
    const CXXMemberCallExpr *Call) {
  CXXMethodDecl* MD = Call->getMethodDecl();
  if (!MD)
    return;

  handleCall(Call, Call->getImplicitObjectArgument(), MD);
  propagateReturnType(Call, MD);
a769 1

d771 2
a772 2
    const CXXOperatorCallExpr *Call) {

d775 1
d777 74
a850 6

  if (Call->getOperator() == OO_Equal) {
    ConsumedState CS = getInfo(Call->getArg(1));
    if (!handleCall(Call, Call->getArg(0), FunDecl))
      setInfo(Call->getArg(0), CS);
    return;
a851 7

  if (const CXXMemberCallExpr *MCall = dyn_cast<CXXMemberCallExpr>(Call))
    handleCall(MCall, MCall->getImplicitObjectArgument(), FunDecl);
  else
    handleCall(Call, Call->getArg(0), FunDecl);

  propagateReturnType(Call, FunDecl);
d1284 2
a1285 6
  } else if (isConsumableType(ReturnType)) {
    if (isAutoCastType(ReturnType))   // We can auto-cast the state to the
      ExpectedReturnState = CS_None;  // expected state.
    else
      ExpectedReturnState = mapConsumableAttrState(ReturnType);
  }
@


1.1.1.4
log
@Import Clang 3.5svn r201163.
@
text
@d186 5
a190 2
static bool isRValueRef(QualType ParamType) {
  return ParamType->isRValueReferenceType();
d610 2
a611 2
  if (isa<CXXOperatorCallExpr>(Call) && isa<CXXMethodDecl>(FunD))
    Offset = 1;  // first argument is 'this'
d643 1
a643 1
    if (isRValueRef(ParamType))
d647 5
a651 4
    else if (isPointerOrRef(ParamType) &&
             (!ParamType->getPointeeType().isConstQualified() ||
              isSetOnReadPtrType(ParamType)))
      setStateForVarOrTmp(StateMap, PInfo, consumed::CS_Unknown);
d888 2
a889 2
  else if (isRValueRef(ParamType) &&
           isConsumableType(ParamType->getPointeeType()))
d892 1
a892 1
           isConsumableType(ParamType->getPointeeType()))
@


1.1.1.4.2.1
log
@Rebase.
@
text
@d30 1
a33 1
#include <memory>
d60 3
a62 2
  for (const auto &B : *Block)
    if (Optional<CFGStmt> CS = B.getAs<CFGStmt>())
d64 1
d118 5
a122 1
  for (const auto &S : CWAttr->callableStates()) {
d124 2
a125 2

    switch (S) {
d474 5
a478 15
  InfoEntry findInfo(const Expr *E) {
    return PropagationMap.find(E->IgnoreParens());
  }
  ConstInfoEntry findInfo(const Expr *E) const {
    return PropagationMap.find(E->IgnoreParens());
  }
  void insertInfo(const Expr *E, const PropagationInfo &PI) {
    PropagationMap.insert(PairType(E->IgnoreParens(), PI));
  }

  void forwardInfo(const Expr *From, const Expr *To);
  void copyInfo(const Expr *From, const Expr *To, ConsumedState CS);
  ConsumedState getInfo(const Expr *From);
  void setInfo(const Expr *To, ConsumedState NS);
  void propagateReturnType(const Expr *Call, const FunctionDecl *Fun);
d507 2
a508 2
  PropagationInfo getInfo(const Expr *StmtNode) const {
    ConstInfoEntry Entry = findInfo(StmtNode);
d522 2
a523 2
void ConsumedStmtVisitor::forwardInfo(const Expr *From, const Expr *To) {
  InfoEntry Entry = findInfo(From);
d525 1
a525 1
    insertInfo(To, Entry->second);
d531 1
a531 1
void ConsumedStmtVisitor::copyInfo(const Expr *From, const Expr *To,
d533 1
a533 1
  InfoEntry Entry = findInfo(From);
d538 1
a538 1
      insertInfo(To, PropagationInfo(CS));
d546 2
a547 2
ConsumedState ConsumedStmtVisitor::getInfo(const Expr *From) {
  InfoEntry Entry = findInfo(From);
d557 2
a558 2
void ConsumedStmtVisitor::setInfo(const Expr *To, ConsumedState NS) {
  InfoEntry Entry = findInfo(To);
d564 1
a564 1
     insertInfo(To, PropagationInfo(NS));
d619 1
a619 1
    InfoEntry Entry = findInfo(Call->getArg(Index));
d654 1
a654 1
  InfoEntry Entry = findInfo(ObjArg);
d678 1
a678 1
void ConsumedStmtVisitor::propagateReturnType(const Expr *Call,
d700 2
a701 2
    InfoEntry LEntry = findInfo(BinOp->getLHS()),
              REntry = findInfo(BinOp->getRHS());
d709 1
a709 1
      LTest.Var      = nullptr;
d717 1
a717 1
      RTest.Var      = nullptr;
d720 2
a721 2

    if (!(LTest.Var == nullptr && RTest.Var == nullptr))
d738 10
d755 3
a757 2
  if (Call->getNumArgs() == 1 && FunDecl->getNameAsString() == "move" &&
      FunDecl->isInStdNamespace()) {
d762 1
a762 1
  handleCall(Call, nullptr, FunDecl);
d773 1
a773 1
  InfoEntry Entry = findInfo(Temp->getSubExpr());
d854 5
a858 3
  for (const auto *DI : DeclS->decls())    
    if (isa<VarDecl>(DI))
      VisitVarDecl(cast<VarDecl>(DI));
d899 1
a899 1
    InfoEntry Entry = findInfo(Ret->getRetValue());
d916 1
a916 1
  InfoEntry Entry = findInfo(UOp->getSubExpr());
d938 2
a939 1
      MapType::iterator VIT = findInfo(Var->getInit()->IgnoreImplicit());
d1074 3
a1076 3

  assert(Block && "Block pointer must not be NULL");

d1098 1
a1098 1
  StateMapsArray[BlockID] = nullptr;
d1108 1
a1108 1
    StateMapsArray[Block->getBlockID()] = nullptr;
d1121 2
a1122 2
  assert(Block && "Block pointer must not be NULL");

d1140 5
a1144 3
  for (const auto &DM : VarMap) {
    if (isa<ParmVarDecl>(DM.first)) {
      const ParmVarDecl *Param = cast<ParmVarDecl>(DM.first);
d1151 1
a1151 1
      if (DM.second != ExpectedState)
d1154 1
a1154 1
          stateToString(DM.second));
d1190 4
a1193 2
  for (const auto &DM : Other->VarMap) {
    LocalState = this->getState(DM.first);
d1198 2
a1199 2
    if (LocalState != DM.second)
     VarMap[DM.first] = CS_Unknown;
d1210 4
a1213 2
  for (const auto &DM : LoopBackStates->VarMap) {    
    LocalState = this->getState(DM.first);
d1218 4
a1221 4
    if (LocalState != DM.second) {
      VarMap[DM.first] = CS_Unknown;
      WarningsHandler.warnLoopStateMismatch(BlameLoc,
                                            DM.first->getNameAsString());
d1241 2
a1242 2
void ConsumedStateMap::remove(const CXXBindTemporaryExpr *Tmp) {
  TmpMap.erase(Tmp);
d1246 7
a1252 3
  for (const auto &DM : Other->VarMap)
    if (this->getState(DM.first) != DM.second)
      return true;  
d1289 2
a1290 3

  std::unique_ptr<ConsumedStateMap> FalseStates(
      new ConsumedStateMap(*CurrStates));
d1296 1
a1296 1
    const Expr *Cond = IfNode->getCond();
d1365 3
a1367 3
    BlockInfo.addInfo(*SI, FalseStates.release());

  CurrStates = nullptr;
d1391 4
a1394 2
  for (const auto *PI : D->params())
    Visitor.VisitParmVarDecl(PI);
d1397 6
a1402 2
  for (const auto *CurrBlock : *SortedGraph) {
    if (!CurrStates)
d1410 1
a1410 1
      CurrStates = nullptr;
d1417 4
a1420 2
    for (const auto &B : *CurrBlock) {
      switch (B.getKind()) {
d1422 1
a1422 1
        Visitor.Visit(B.castAs<CFGStmt>().getStmt());
d1426 1
a1426 1
        const CFGTemporaryDtor &DTor = B.castAs<CFGTemporaryDtor>();
a1431 1
        CurrStates->remove(BTE);
d1436 1
a1436 1
        const CFGAutomaticObjDtor &DTor = B.castAs<CFGAutomaticObjDtor>();
d1451 2
d1456 2
a1457 2
      CurrStates->setSource(nullptr);

d1466 3
a1468 3

          if (*SI == nullptr) continue;

d1483 2
a1484 2

        CurrStates = nullptr;
@


1.1.1.5
log
@Import Clang 3.5svn r209886.
@
text
@d30 1
a33 1
#include <memory>
d60 3
a62 2
  for (const auto &B : *Block)
    if (Optional<CFGStmt> CS = B.getAs<CFGStmt>())
d64 1
d118 5
a122 1
  for (const auto &S : CWAttr->callableStates()) {
d124 2
a125 2

    switch (S) {
d474 5
a478 15
  InfoEntry findInfo(const Expr *E) {
    return PropagationMap.find(E->IgnoreParens());
  }
  ConstInfoEntry findInfo(const Expr *E) const {
    return PropagationMap.find(E->IgnoreParens());
  }
  void insertInfo(const Expr *E, const PropagationInfo &PI) {
    PropagationMap.insert(PairType(E->IgnoreParens(), PI));
  }

  void forwardInfo(const Expr *From, const Expr *To);
  void copyInfo(const Expr *From, const Expr *To, ConsumedState CS);
  ConsumedState getInfo(const Expr *From);
  void setInfo(const Expr *To, ConsumedState NS);
  void propagateReturnType(const Expr *Call, const FunctionDecl *Fun);
d507 2
a508 2
  PropagationInfo getInfo(const Expr *StmtNode) const {
    ConstInfoEntry Entry = findInfo(StmtNode);
d522 2
a523 2
void ConsumedStmtVisitor::forwardInfo(const Expr *From, const Expr *To) {
  InfoEntry Entry = findInfo(From);
d525 1
a525 1
    insertInfo(To, Entry->second);
d531 1
a531 1
void ConsumedStmtVisitor::copyInfo(const Expr *From, const Expr *To,
d533 1
a533 1
  InfoEntry Entry = findInfo(From);
d538 1
a538 1
      insertInfo(To, PropagationInfo(CS));
d546 2
a547 2
ConsumedState ConsumedStmtVisitor::getInfo(const Expr *From) {
  InfoEntry Entry = findInfo(From);
d557 2
a558 2
void ConsumedStmtVisitor::setInfo(const Expr *To, ConsumedState NS) {
  InfoEntry Entry = findInfo(To);
d564 1
a564 1
     insertInfo(To, PropagationInfo(NS));
d619 1
a619 1
    InfoEntry Entry = findInfo(Call->getArg(Index));
d654 1
a654 1
  InfoEntry Entry = findInfo(ObjArg);
d678 1
a678 1
void ConsumedStmtVisitor::propagateReturnType(const Expr *Call,
d700 2
a701 2
    InfoEntry LEntry = findInfo(BinOp->getLHS()),
              REntry = findInfo(BinOp->getRHS());
d709 1
a709 1
      LTest.Var      = nullptr;
d717 1
a717 1
      RTest.Var      = nullptr;
d720 2
a721 2

    if (!(LTest.Var == nullptr && RTest.Var == nullptr))
d738 10
d755 3
a757 2
  if (Call->getNumArgs() == 1 && FunDecl->getNameAsString() == "move" &&
      FunDecl->isInStdNamespace()) {
d762 1
a762 1
  handleCall(Call, nullptr, FunDecl);
d773 1
a773 1
  InfoEntry Entry = findInfo(Temp->getSubExpr());
d854 5
a858 3
  for (const auto *DI : DeclS->decls())    
    if (isa<VarDecl>(DI))
      VisitVarDecl(cast<VarDecl>(DI));
d899 1
a899 1
    InfoEntry Entry = findInfo(Ret->getRetValue());
d916 1
a916 1
  InfoEntry Entry = findInfo(UOp->getSubExpr());
d938 2
a939 1
      MapType::iterator VIT = findInfo(Var->getInit()->IgnoreImplicit());
d1074 3
a1076 3

  assert(Block && "Block pointer must not be NULL");

d1098 1
a1098 1
  StateMapsArray[BlockID] = nullptr;
d1108 1
a1108 1
    StateMapsArray[Block->getBlockID()] = nullptr;
d1121 2
a1122 2
  assert(Block && "Block pointer must not be NULL");

d1140 5
a1144 3
  for (const auto &DM : VarMap) {
    if (isa<ParmVarDecl>(DM.first)) {
      const ParmVarDecl *Param = cast<ParmVarDecl>(DM.first);
d1151 1
a1151 1
      if (DM.second != ExpectedState)
d1154 1
a1154 1
          stateToString(DM.second));
d1190 4
a1193 2
  for (const auto &DM : Other->VarMap) {
    LocalState = this->getState(DM.first);
d1198 2
a1199 2
    if (LocalState != DM.second)
     VarMap[DM.first] = CS_Unknown;
d1210 4
a1213 2
  for (const auto &DM : LoopBackStates->VarMap) {    
    LocalState = this->getState(DM.first);
d1218 4
a1221 4
    if (LocalState != DM.second) {
      VarMap[DM.first] = CS_Unknown;
      WarningsHandler.warnLoopStateMismatch(BlameLoc,
                                            DM.first->getNameAsString());
d1241 2
a1242 2
void ConsumedStateMap::remove(const CXXBindTemporaryExpr *Tmp) {
  TmpMap.erase(Tmp);
d1246 7
a1252 3
  for (const auto &DM : Other->VarMap)
    if (this->getState(DM.first) != DM.second)
      return true;  
d1289 2
a1290 3

  std::unique_ptr<ConsumedStateMap> FalseStates(
      new ConsumedStateMap(*CurrStates));
d1296 1
a1296 1
    const Expr *Cond = IfNode->getCond();
d1365 3
a1367 3
    BlockInfo.addInfo(*SI, FalseStates.release());

  CurrStates = nullptr;
d1391 4
a1394 2
  for (const auto *PI : D->params())
    Visitor.VisitParmVarDecl(PI);
d1397 6
a1402 2
  for (const auto *CurrBlock : *SortedGraph) {
    if (!CurrStates)
d1410 1
a1410 1
      CurrStates = nullptr;
d1417 4
a1420 2
    for (const auto &B : *CurrBlock) {
      switch (B.getKind()) {
d1422 1
a1422 1
        Visitor.Visit(B.castAs<CFGStmt>().getStmt());
d1426 1
a1426 1
        const CFGTemporaryDtor &DTor = B.castAs<CFGTemporaryDtor>();
a1431 1
        CurrStates->remove(BTE);
d1436 1
a1436 1
        const CFGAutomaticObjDtor &DTor = B.castAs<CFGAutomaticObjDtor>();
d1451 2
d1456 2
a1457 2
      CurrStates->setSource(nullptr);

d1466 3
a1468 3

          if (*SI == nullptr) continue;

d1483 2
a1484 2

        CurrStates = nullptr;
@


1.1.1.6
log
@Import Clang 3.8.0rc3 r261930.
@
text
@d949 4
a952 3
static void splitVarStateForIf(const IfStmt *IfNode, const VarTestResult &Test,
                               ConsumedStateMap *ThenStates,
                               ConsumedStateMap *ElseStates) {
d967 3
a969 3
static void splitVarStateForIfBinOp(const PropagationInfo &PInfo,
                                    ConsumedStateMap *ThenStates,
                                    ConsumedStateMap *ElseStates) {
d1042 4
a1045 4
void ConsumedBlockInfo::addInfo(
    const CFGBlock *Block, ConsumedStateMap *StateMap,
    std::unique_ptr<ConsumedStateMap> &OwnedStateMap) {

d1047 3
a1049 3

  auto &Entry = StateMapsArray[Block->getBlockID()];

d1051 9
a1059 5
    Entry->intersect(*StateMap);
  } else if (OwnedStateMap)
    Entry = std::move(OwnedStateMap);
  else
    Entry = llvm::make_unique<ConsumedStateMap>(*StateMap);
d1063 1
a1063 1
                                std::unique_ptr<ConsumedStateMap> StateMap) {
d1067 2
a1068 2
  auto &Entry = StateMapsArray[Block->getBlockID()];

d1070 3
a1072 1
    Entry->intersect(*StateMap);
d1074 1
a1074 1
    Entry = std::move(StateMap);
d1081 2
a1082 2

  return StateMapsArray[Block->getBlockID()].get();
d1086 3
a1088 1
  StateMapsArray[Block->getBlockID()] = nullptr;
d1091 1
a1091 2
std::unique_ptr<ConsumedStateMap>
ConsumedBlockInfo::getInfo(const CFGBlock *Block) {
d1093 8
a1100 4

  auto &Entry = StateMapsArray[Block->getBlockID()];
  return isBackEdgeTarget(Block) ? llvm::make_unique<ConsumedStateMap>(*Entry)
                                 : std::move(Entry);
d1170 1
a1170 1
void ConsumedStateMap::intersect(const ConsumedStateMap &Other) {
d1172 2
a1173 2

  if (this->From && this->From == Other.From && !Other.Reachable) {
d1177 2
a1178 2

  for (const auto &DM : Other.VarMap) {
d1286 1
a1286 1
      splitVarStateForIf(IfNode, PInfo.getVarTest(), CurrStates.get(),
d1288 1
a1288 1

d1292 2
a1293 2
      splitVarStateForIfBinOp(PInfo, CurrStates.get(), FalseStates.get());

d1341 1
a1341 1
    BlockInfo.addInfo(*SI, std::move(CurrStates));
d1343 2
a1344 2
    CurrStates = nullptr;

d1346 1
a1346 1
    BlockInfo.addInfo(*SI, std::move(FalseStates));
d1348 1
d1367 4
a1370 4

  CurrStates = llvm::make_unique<ConsumedStateMap>();
  ConsumedStmtVisitor Visitor(AC, *this, CurrStates.get());

d1384 1
d1388 3
a1390 3

    Visitor.reset(CurrStates.get());

d1433 3
a1435 3

        auto *RawState = CurrStates.get();

d1442 5
a1446 4
            BlockInfo.borrowInfo(*SI)->intersectAtLoopHead(
                *SI, CurrBlock, RawState, WarningsHandler);

            if (BlockInfo.allBackEdgesVisited(CurrBlock, *SI))
d1449 1
a1449 1
            BlockInfo.addInfo(*SI, RawState, CurrStates);
d1452 3
d1467 2
a1468 2
  CurrStates = nullptr;

@


1.1.1.6.2.1
log
@Sync with HEAD
@
text
@a14 1
#include "clang/Analysis/Analyses/Consumed.h"
d23 1
d30 3
a468 3
    if (auto Cleanups = dyn_cast<ExprWithCleanups>(E))
      if (!Cleanups->cleanupsHaveSideEffects())
        E = Cleanups->getSubExpr();
a471 3
    if (auto Cleanups = dyn_cast<ExprWithCleanups>(E))
      if (!Cleanups->cleanupsHaveSideEffects())
        E = Cleanups->getSubExpr();
d1359 1
a1359 1
  for (const auto *PI : D->parameters())
@


1.1.1.7
log
@Import Clang pre-4.0.0 r291444.
@
text
@a14 1
#include "clang/Analysis/Analyses/Consumed.h"
d23 1
d30 3
a468 3
    if (auto Cleanups = dyn_cast<ExprWithCleanups>(E))
      if (!Cleanups->cleanupsHaveSideEffects())
        E = Cleanups->getSubExpr();
a471 3
    if (auto Cleanups = dyn_cast<ExprWithCleanups>(E))
      if (!Cleanups->cleanupsHaveSideEffects())
        E = Cleanups->getSubExpr();
d1359 1
a1359 1
  for (const auto *PI : D->parameters())
@


1.1.1.7.14.1
log
@Sync with HEAD
@
text
@d1 1
a1 1
//===- Consumed.cpp -------------------------------------------------------===//
d16 1
a17 1
#include "clang/AST/Decl.h"
a18 1
#include "clang/AST/Expr.h"
d20 2
a21 1
#include "clang/AST/Stmt.h"
d25 1
a25 1
#include "clang/Analysis/AnalysisDeclContext.h"
a26 1
#include "clang/Basic/LLVM.h"
a29 6
#include "llvm/ADT/Optional.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/Support/Casting.h"
#include "llvm/Support/ErrorHandling.h"
#include <cassert>
a30 1
#include <utility>
d52 1
a52 1
ConsumedWarningsHandlerBase::~ConsumedWarningsHandlerBase() = default;
d66 1
a66 1
  return {};
d112 1
d137 1
d164 1
a272 1

d277 1
a277 2

} // namespace
d295 1
a295 1
  } InfoType = IT_None;
d313 2
a314 1
  PropagationInfo() = default;
d316 2
a317 2
      : InfoType(IT_VarTest), VarTest(VarTest) {}

d319 2
a320 1
      : InfoType(IT_VarTest) {
d327 2
a328 1
      : InfoType(IT_BinTest) {
d338 2
a339 1
      : InfoType(IT_BinTest) {
d349 2
a350 1
      : InfoType(IT_State), State(State) {}
d353 1
a353 1
      : InfoType(IT_Tmp), Tmp(Tmp) {}
d355 1
a355 1
  const ConsumedState &getState() const {
d360 1
a360 1
  const VarTestResult &getVarTest() const {
d365 1
a365 1
  const VarTestResult &getLTest() const {
d370 1
a370 1
  const VarTestResult &getRTest() const {
d375 1
a375 1
  const VarDecl *getVar() const {
d380 1
a380 1
  const CXXBindTemporaryExpr *getTmp() const {
d408 6
a413 6
  bool isValid() const { return InfoType != IT_None; }
  bool isState() const { return InfoType == IT_State; }
  bool isVarTest() const { return InfoType == IT_VarTest; }
  bool isBinTest() const { return InfoType == IT_BinTest; }
  bool isVar() const { return InfoType == IT_Var; }
  bool isTmp() const { return InfoType == IT_Tmp; }
d436 1
a436 1
      return {};
d441 1
a441 4
} // namespace consumed
} // namespace clang

static void
d444 1
a452 3
namespace clang {
namespace consumed {

d454 5
a458 4
  using MapType = llvm::DenseMap<const Stmt *, PropagationInfo>;
  using PairType= std::pair<const Stmt *, PropagationInfo>;
  using InfoEntry = MapType::iterator;
  using ConstInfoEntry = MapType::const_iterator;
d466 1
a466 1
    if (const auto Cleanups = dyn_cast<ExprWithCleanups>(E))
a470 1

d472 1
a472 1
    if (const auto Cleanups = dyn_cast<ExprWithCleanups>(E))
a476 1

d520 1
a520 1
      return {};
a527 2
} // namespace consumed
} // namespace clang
d535 1
d551 1
d562 1
d575 2
d595 1
d607 1
d684 1
d702 1
d714 1
d722 1
d731 1
d752 2
a753 1
  if (Call->isCallToStdMove()) {
d809 1
d820 1
d823 3
a825 1
  const auto *FunDecl = dyn_cast_or_null<FunctionDecl>(Call->getDirectCallee());
d835 1
a835 1
  if (const auto *MCall = dyn_cast<CXXMemberCallExpr>(Call))
d844 1
a844 1
  if (const auto *Var = dyn_cast_or_null<VarDecl>(DeclRef->getDecl()))
d855 1
a855 1
    if (const auto *Var = dyn_cast_or_null<VarDecl>(DeclS->getSingleDecl()))
d861 1
d869 1
d947 4
d960 1
d963 1
d982 1
d985 1
d992 1
d997 1
d1000 1
d1003 1
d1018 1
d1031 1
d1047 1
d1062 1
d1123 1
a1123 1
      const auto *Param = cast<ParmVarDecl>(DM.first);
d1230 1
a1230 1
  if (const auto *Constructor = dyn_cast<CXXConstructorDecl>(D)) {
d1260 1
d1265 3
a1267 2
  if (const auto *IfNode =
          dyn_cast_or_null<IfStmt>(CurrBlock->getTerminator().getStmt())) {
d1279 1
d1284 1
d1288 4
a1291 2
  } else if (const auto *BinOp =
       dyn_cast_or_null<BinaryOperator>(CurrBlock->getTerminator().getStmt())) {
d1299 1
d1324 1
d1343 1
a1343 1
  const auto *D = dyn_cast_or_null<FunctionDecl>(AC.getDecl());
d1372 1
d1427 1
d1456 1
@


1.1.1.7.14.2
log
@Mostly merge changes from HEAD upto 20200411
@
text
@@


1.1.1.7.12.1
log
@Sync with HEAD
@
text
@d1 1
a1 1
//===- Consumed.cpp -------------------------------------------------------===//
d16 1
a17 1
#include "clang/AST/Decl.h"
a18 1
#include "clang/AST/Expr.h"
d20 2
a21 1
#include "clang/AST/Stmt.h"
d25 1
a25 1
#include "clang/Analysis/AnalysisDeclContext.h"
a26 1
#include "clang/Basic/LLVM.h"
a29 6
#include "llvm/ADT/Optional.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/Support/Casting.h"
#include "llvm/Support/ErrorHandling.h"
#include <cassert>
a30 1
#include <utility>
d52 1
a52 1
ConsumedWarningsHandlerBase::~ConsumedWarningsHandlerBase() = default;
d66 1
a66 1
  return {};
d112 1
d137 1
d164 1
a272 1

d277 1
a277 2

} // namespace
d295 1
a295 1
  } InfoType = IT_None;
d313 2
a314 1
  PropagationInfo() = default;
d316 2
a317 2
      : InfoType(IT_VarTest), VarTest(VarTest) {}

d319 2
a320 1
      : InfoType(IT_VarTest) {
d327 2
a328 1
      : InfoType(IT_BinTest) {
d338 2
a339 1
      : InfoType(IT_BinTest) {
d349 2
a350 1
      : InfoType(IT_State), State(State) {}
d353 1
a353 1
      : InfoType(IT_Tmp), Tmp(Tmp) {}
d355 1
a355 1
  const ConsumedState &getState() const {
d360 1
a360 1
  const VarTestResult &getVarTest() const {
d365 1
a365 1
  const VarTestResult &getLTest() const {
d370 1
a370 1
  const VarTestResult &getRTest() const {
d375 1
a375 1
  const VarDecl *getVar() const {
d380 1
a380 1
  const CXXBindTemporaryExpr *getTmp() const {
d408 6
a413 6
  bool isValid() const { return InfoType != IT_None; }
  bool isState() const { return InfoType == IT_State; }
  bool isVarTest() const { return InfoType == IT_VarTest; }
  bool isBinTest() const { return InfoType == IT_BinTest; }
  bool isVar() const { return InfoType == IT_Var; }
  bool isTmp() const { return InfoType == IT_Tmp; }
d436 1
a436 1
      return {};
d441 1
a441 4
} // namespace consumed
} // namespace clang

static void
d444 1
a452 3
namespace clang {
namespace consumed {

d454 5
a458 4
  using MapType = llvm::DenseMap<const Stmt *, PropagationInfo>;
  using PairType= std::pair<const Stmt *, PropagationInfo>;
  using InfoEntry = MapType::iterator;
  using ConstInfoEntry = MapType::const_iterator;
d466 1
a466 1
    if (const auto Cleanups = dyn_cast<ExprWithCleanups>(E))
a470 1

d472 1
a472 1
    if (const auto Cleanups = dyn_cast<ExprWithCleanups>(E))
a476 1

d520 1
a520 1
      return {};
a527 2
} // namespace consumed
} // namespace clang
d535 1
d551 1
d562 1
d575 2
d595 1
d607 1
d684 1
d702 1
d714 1
d722 1
d731 1
d752 2
a753 1
  if (Call->isCallToStdMove()) {
d809 1
d820 1
d823 3
a825 1
  const auto *FunDecl = dyn_cast_or_null<FunctionDecl>(Call->getDirectCallee());
d835 1
a835 1
  if (const auto *MCall = dyn_cast<CXXMemberCallExpr>(Call))
d844 1
a844 1
  if (const auto *Var = dyn_cast_or_null<VarDecl>(DeclRef->getDecl()))
d855 1
a855 1
    if (const auto *Var = dyn_cast_or_null<VarDecl>(DeclS->getSingleDecl()))
d861 1
d869 1
d947 4
d960 1
d963 1
d982 1
d985 1
d992 1
d997 1
d1000 1
d1003 1
d1018 1
d1031 1
d1047 1
d1062 1
d1123 1
a1123 1
      const auto *Param = cast<ParmVarDecl>(DM.first);
d1230 1
a1230 1
  if (const auto *Constructor = dyn_cast<CXXConstructorDecl>(D)) {
d1260 1
d1265 3
a1267 2
  if (const auto *IfNode =
          dyn_cast_or_null<IfStmt>(CurrBlock->getTerminator().getStmt())) {
d1279 1
d1284 1
d1288 4
a1291 2
  } else if (const auto *BinOp =
       dyn_cast_or_null<BinaryOperator>(CurrBlock->getTerminator().getStmt())) {
d1299 1
d1324 1
d1343 1
a1343 1
  const auto *D = dyn_cast_or_null<FunctionDecl>(AC.getDecl());
d1372 1
d1427 1
d1456 1
@


1.1.1.8
log
@Import clang r337282 from trunk
@
text
@d1 1
a1 1
//===- Consumed.cpp -------------------------------------------------------===//
d16 1
a17 1
#include "clang/AST/Decl.h"
a18 1
#include "clang/AST/Expr.h"
d20 2
a21 1
#include "clang/AST/Stmt.h"
d25 1
a25 1
#include "clang/Analysis/AnalysisDeclContext.h"
a26 1
#include "clang/Basic/LLVM.h"
a29 6
#include "llvm/ADT/Optional.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/Support/Casting.h"
#include "llvm/Support/ErrorHandling.h"
#include <cassert>
a30 1
#include <utility>
d52 1
a52 1
ConsumedWarningsHandlerBase::~ConsumedWarningsHandlerBase() = default;
d66 1
a66 1
  return {};
d112 1
d137 1
d164 1
a272 1

d277 1
a277 2

} // namespace
d295 1
a295 1
  } InfoType = IT_None;
d313 2
a314 1
  PropagationInfo() = default;
d316 2
a317 2
      : InfoType(IT_VarTest), VarTest(VarTest) {}

d319 2
a320 1
      : InfoType(IT_VarTest) {
d327 2
a328 1
      : InfoType(IT_BinTest) {
d338 2
a339 1
      : InfoType(IT_BinTest) {
d349 2
a350 1
      : InfoType(IT_State), State(State) {}
d353 1
a353 1
      : InfoType(IT_Tmp), Tmp(Tmp) {}
d355 1
a355 1
  const ConsumedState &getState() const {
d360 1
a360 1
  const VarTestResult &getVarTest() const {
d365 1
a365 1
  const VarTestResult &getLTest() const {
d370 1
a370 1
  const VarTestResult &getRTest() const {
d375 1
a375 1
  const VarDecl *getVar() const {
d380 1
a380 1
  const CXXBindTemporaryExpr *getTmp() const {
d408 6
a413 6
  bool isValid() const { return InfoType != IT_None; }
  bool isState() const { return InfoType == IT_State; }
  bool isVarTest() const { return InfoType == IT_VarTest; }
  bool isBinTest() const { return InfoType == IT_BinTest; }
  bool isVar() const { return InfoType == IT_Var; }
  bool isTmp() const { return InfoType == IT_Tmp; }
d436 1
a436 1
      return {};
d441 1
a441 4
} // namespace consumed
} // namespace clang

static void
d444 1
a452 3
namespace clang {
namespace consumed {

d454 5
a458 4
  using MapType = llvm::DenseMap<const Stmt *, PropagationInfo>;
  using PairType= std::pair<const Stmt *, PropagationInfo>;
  using InfoEntry = MapType::iterator;
  using ConstInfoEntry = MapType::const_iterator;
d466 1
a466 1
    if (const auto Cleanups = dyn_cast<ExprWithCleanups>(E))
a470 1

d472 1
a472 1
    if (const auto Cleanups = dyn_cast<ExprWithCleanups>(E))
a476 1

d520 1
a520 1
      return {};
a527 2
} // namespace consumed
} // namespace clang
d535 1
d551 1
d562 1
d575 2
d595 1
d607 1
d684 1
d702 1
d714 1
d722 1
d731 1
d752 2
a753 1
  if (Call->isCallToStdMove()) {
d809 1
d820 1
d823 3
a825 1
  const auto *FunDecl = dyn_cast_or_null<FunctionDecl>(Call->getDirectCallee());
d835 1
a835 1
  if (const auto *MCall = dyn_cast<CXXMemberCallExpr>(Call))
d844 1
a844 1
  if (const auto *Var = dyn_cast_or_null<VarDecl>(DeclRef->getDecl()))
d855 1
a855 1
    if (const auto *Var = dyn_cast_or_null<VarDecl>(DeclS->getSingleDecl()))
d861 1
d869 1
d947 4
d960 1
d963 1
d982 1
d985 1
d992 1
d997 1
d1000 1
d1003 1
d1018 1
d1031 1
d1047 1
d1062 1
d1123 1
a1123 1
      const auto *Param = cast<ParmVarDecl>(DM.first);
d1230 1
a1230 1
  if (const auto *Constructor = dyn_cast<CXXConstructorDecl>(D)) {
d1260 1
d1265 3
a1267 2
  if (const auto *IfNode =
          dyn_cast_or_null<IfStmt>(CurrBlock->getTerminator().getStmt())) {
d1279 1
d1284 1
d1288 4
a1291 2
  } else if (const auto *BinOp =
       dyn_cast_or_null<BinaryOperator>(CurrBlock->getTerminator().getStmt())) {
d1299 1
d1324 1
d1343 1
a1343 1
  const auto *D = dyn_cast_or_null<FunctionDecl>(AC.getDecl());
d1372 1
d1427 1
d1456 1
@


1.1.1.9
log
@Mark old LLVM instance as dead.
@
text
@@


1.1.1.5.4.1
log
@file Consumed.cpp was added on branch tls-maxphys on 2014-08-19 23:47:26 +0000
@
text
@d1 1471
@


1.1.1.5.4.2
log
@Rebase to HEAD as of a few days ago.
@
text
@a0 1471
//===- 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/StmtCXX.h"
#include "clang/AST/StmtVisitor.h"
#include "clang/AST/Type.h"
#include "clang/Analysis/Analyses/Consumed.h"
#include "clang/Analysis/Analyses/PostOrderCFGView.h"
#include "clang/Analysis/AnalysisContext.h"
#include "clang/Analysis/CFG.h"
#include "clang/Basic/OperatorKinds.h"
#include "clang/Basic/SourceLocation.h"
#include "llvm/ADT/DenseMap.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/Support/Compiler.h"
#include "llvm/Support/raw_ostream.h"
#include <memory>

// TODO: Adjust states of args to constructors in the same way that arguments to
//       function calls are handled.
// TODO: Use information from tests in for- and while-loop conditional.
// TODO: Add notes about the actual and expected state for 
// TODO: Correctly identify unreachable blocks when chaining boolean operators.
// TODO: Adjust the parser and AttributesList class to support lists of
//       identifiers.
// TODO: Warn about unreachable code.
// TODO: Switch to using a bitmap to track unreachable blocks.
// TODO: Handle variable definitions, e.g. bool valid = x.isValid();
//       if (valid) ...; (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)

using namespace clang;
using namespace consumed;

// Key method definition
ConsumedWarningsHandlerBase::~ConsumedWarningsHandlerBase() {}

static SourceLocation getFirstStmtLoc(const CFGBlock *Block) {
  // Find the source location of the first statement in the block, if the block
  // is not empty.
  for (const auto &B : *Block)
    if (Optional<CFGStmt> CS = B.getAs<CFGStmt>())
      return CS->getStmt()->getLocStart();

  // Block is empty.
  // If we have one successor, return the first statement in that block
  if (Block->succ_size() == 1 && *Block->succ_begin())
    return getFirstStmtLoc(*Block->succ_begin());

  return SourceLocation();
}

static SourceLocation getLastStmtLoc(const CFGBlock *Block) {
  // Find the source location of the last statement in the block, if the block
  // is not empty.
  if (const Stmt *StmtNode = Block->getTerminator()) {
    return StmtNode->getLocStart();
  } else {
    for (CFGBlock::const_reverse_iterator BI = Block->rbegin(),
         BE = Block->rend(); BI != BE; ++BI) {
      if (Optional<CFGStmt> CS = BI->getAs<CFGStmt>())
        return CS->getStmt()->getLocStart();
    }
  }

  // If we have one successor, return the first statement in that block
  SourceLocation Loc;
  if (Block->succ_size() == 1 && *Block->succ_begin())
    Loc = getFirstStmtLoc(*Block->succ_begin());
  if (Loc.isValid())
    return Loc;

  // If we have one predecessor, return the last statement in that block
  if (Block->pred_size() == 1 && *Block->pred_begin())
    return getLastStmtLoc(*Block->pred_begin());

  return Loc;
}

static ConsumedState invertConsumedUnconsumed(ConsumedState State) {
  switch (State) {
  case CS_Unconsumed:
    return CS_Consumed;
  case CS_Consumed:
    return CS_Unconsumed;
  case CS_None:
    return CS_None;
  case CS_Unknown:
    return CS_Unknown;
  }
  llvm_unreachable("invalid enum");
}

static bool isCallableInState(const CallableWhenAttr *CWAttr,
                              ConsumedState State) {
  
  for (const auto &S : CWAttr->callableStates()) {
    ConsumedState MappedAttrState = CS_None;

    switch (S) {
    case CallableWhenAttr::Unknown:
      MappedAttrState = CS_Unknown;
      break;
      
    case CallableWhenAttr::Unconsumed:
      MappedAttrState = CS_Unconsumed;
      break;
      
    case CallableWhenAttr::Consumed:
      MappedAttrState = CS_Consumed;
      break;
    }
    
    if (MappedAttrState == State)
      return true;
  }
  
  return false;
}


static bool isConsumableType(const QualType &QT) {
  if (QT->isPointerType() || QT->isReferenceType())
    return false;
  
  if (const CXXRecordDecl *RD = QT->getAsCXXRecordDecl())
    return RD->hasAttr<ConsumableAttr>();
  
  return false;
}

static bool isAutoCastType(const QualType &QT) {
  if (QT->isPointerType() || QT->isReferenceType())
    return false;

  if (const CXXRecordDecl *RD = QT->getAsCXXRecordDecl())
    return RD->hasAttr<ConsumableAutoCastAttr>();

  return false;
}

static bool isSetOnReadPtrType(const QualType &QT) {
  if (const CXXRecordDecl *RD = QT->getPointeeCXXRecordDecl())
    return RD->hasAttr<ConsumableSetOnReadAttr>();
  return false;
}


static bool isKnownState(ConsumedState State) {
  switch (State) {
  case CS_Unconsumed:
  case CS_Consumed:
    return true;
  case CS_None:
  case CS_Unknown:
    return false;
  }
  llvm_unreachable("invalid enum");
}

static bool isRValueRef(QualType ParamType) {
  return ParamType->isRValueReferenceType();
}

static bool isTestingFunction(const FunctionDecl *FunDecl) {
  return FunDecl->hasAttr<TestTypestateAttr>();
}

static bool isPointerOrRef(QualType ParamType) {
  return ParamType->isPointerType() || ParamType->isReferenceType();
}

static ConsumedState mapConsumableAttrState(const QualType QT) {
  assert(isConsumableType(QT));

  const ConsumableAttr *CAttr =
      QT->getAsCXXRecordDecl()->getAttr<ConsumableAttr>();

  switch (CAttr->getDefaultState()) {
  case ConsumableAttr::Unknown:
    return CS_Unknown;
  case ConsumableAttr::Unconsumed:
    return CS_Unconsumed;
  case ConsumableAttr::Consumed:
    return CS_Consumed;
  }
  llvm_unreachable("invalid enum");
}

static ConsumedState
mapParamTypestateAttrState(const ParamTypestateAttr *PTAttr) {
  switch (PTAttr->getParamState()) {
  case ParamTypestateAttr::Unknown:
    return CS_Unknown;
  case ParamTypestateAttr::Unconsumed:
    return CS_Unconsumed;
  case ParamTypestateAttr::Consumed:
    return CS_Consumed;
  }
  llvm_unreachable("invalid_enum");
}

static ConsumedState
mapReturnTypestateAttrState(const ReturnTypestateAttr *RTSAttr) {
  switch (RTSAttr->getState()) {
  case ReturnTypestateAttr::Unknown:
    return CS_Unknown;
  case ReturnTypestateAttr::Unconsumed:
    return CS_Unconsumed;
  case ReturnTypestateAttr::Consumed:
    return CS_Consumed;
  }
  llvm_unreachable("invalid enum");
}

static ConsumedState mapSetTypestateAttrState(const SetTypestateAttr *STAttr) {
  switch (STAttr->getNewState()) {
  case SetTypestateAttr::Unknown:
    return CS_Unknown;
  case SetTypestateAttr::Unconsumed:
    return CS_Unconsumed;
  case SetTypestateAttr::Consumed:
    return CS_Consumed;
  }
  llvm_unreachable("invalid_enum");
}

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");
}

static ConsumedState testsFor(const FunctionDecl *FunDecl) {
  assert(isTestingFunction(FunDecl));
  switch (FunDecl->getAttr<TestTypestateAttr>()->getTestState()) {
  case TestTypestateAttr::Unconsumed:
    return CS_Unconsumed;
  case TestTypestateAttr::Consumed:
    return CS_Consumed;
  }
  llvm_unreachable("invalid enum");
}

namespace {
struct VarTestResult {
  const VarDecl *Var;
  ConsumedState TestsFor;
};
} // end anonymous::VarTestResult

namespace clang {
namespace consumed {

enum EffectiveOp {
  EO_And,
  EO_Or
};

class PropagationInfo {
  enum {
    IT_None,
    IT_State,
    IT_VarTest,
    IT_BinTest,
    IT_Var,
    IT_Tmp
  } InfoType;

  struct BinTestTy {
    const BinaryOperator *Source;
    EffectiveOp EOp;
    VarTestResult LTest;
    VarTestResult RTest;
  };
  
  union {
    ConsumedState State;
    VarTestResult VarTest;
    const VarDecl *Var;
    const CXXBindTemporaryExpr *Tmp;
    BinTestTy BinTest;
  };
  
public:
  PropagationInfo() : InfoType(IT_None) {}
  
  PropagationInfo(const VarTestResult &VarTest)
    : InfoType(IT_VarTest), VarTest(VarTest) {}
  
  PropagationInfo(const VarDecl *Var, ConsumedState TestsFor)
    : InfoType(IT_VarTest) {
    
    VarTest.Var      = Var;
    VarTest.TestsFor = TestsFor;
  }
  
  PropagationInfo(const BinaryOperator *Source, EffectiveOp EOp,
                  const VarTestResult &LTest, const VarTestResult &RTest)
    : InfoType(IT_BinTest) {
    
    BinTest.Source  = Source;
    BinTest.EOp     = EOp;
    BinTest.LTest   = LTest;
    BinTest.RTest   = RTest;
  }
  
  PropagationInfo(const BinaryOperator *Source, EffectiveOp EOp,
                  const VarDecl *LVar, ConsumedState LTestsFor,
                  const VarDecl *RVar, ConsumedState RTestsFor)
    : InfoType(IT_BinTest) {
    
    BinTest.Source         = Source;
    BinTest.EOp            = EOp;
    BinTest.LTest.Var      = LVar;
    BinTest.LTest.TestsFor = LTestsFor;
    BinTest.RTest.Var      = RVar;
    BinTest.RTest.TestsFor = RTestsFor;
  }
  
  PropagationInfo(ConsumedState State)
    : InfoType(IT_State), State(State) {}
  
  PropagationInfo(const VarDecl *Var) : InfoType(IT_Var), Var(Var) {}
  PropagationInfo(const CXXBindTemporaryExpr *Tmp)
    : InfoType(IT_Tmp), Tmp(Tmp) {}
  
  const ConsumedState & getState() const {
    assert(InfoType == IT_State);
    return State;
  }
  
  const VarTestResult & getVarTest() const {
    assert(InfoType == IT_VarTest);
    return VarTest;
  }
  
  const VarTestResult & getLTest() const {
    assert(InfoType == IT_BinTest);
    return BinTest.LTest;
  }
  
  const VarTestResult & getRTest() const {
    assert(InfoType == IT_BinTest);
    return BinTest.RTest;
  }
  
  const VarDecl * getVar() const {
    assert(InfoType == IT_Var);
    return Var;
  }
  
  const CXXBindTemporaryExpr * getTmp() const {
    assert(InfoType == IT_Tmp);
    return Tmp;
  }
  
  ConsumedState getAsState(const ConsumedStateMap *StateMap) const {
    assert(isVar() || isTmp() || isState());
    
    if (isVar())
      return StateMap->getState(Var);
    else if (isTmp())
      return StateMap->getState(Tmp);
    else if (isState())
      return State;
    else
      return CS_None;
  }
  
  EffectiveOp testEffectiveOp() const {
    assert(InfoType == IT_BinTest);
    return BinTest.EOp;
  }
  
  const BinaryOperator * testSourceNode() const {
    assert(InfoType == IT_BinTest);
    return BinTest.Source;
  }
  
  inline bool isValid()   const { return InfoType != IT_None;    }
  inline bool isState()   const { return InfoType == IT_State;   }
  inline bool isVarTest() const { return InfoType == IT_VarTest; }
  inline bool isBinTest() const { return InfoType == IT_BinTest; }
  inline bool isVar()     const { return InfoType == IT_Var;     }
  inline bool isTmp()     const { return InfoType == IT_Tmp;     }
  
  bool isTest() const {
    return InfoType == IT_VarTest || InfoType == IT_BinTest;
  }
  
  bool isPointerToValue() const {
    return InfoType == IT_Var || InfoType == IT_Tmp;
  }
  
  PropagationInfo invertTest() const {
    assert(InfoType == IT_VarTest || InfoType == IT_BinTest);
    
    if (InfoType == IT_VarTest) {
      return PropagationInfo(VarTest.Var,
                             invertConsumedUnconsumed(VarTest.TestsFor));
    
    } else if (InfoType == IT_BinTest) {
      return PropagationInfo(BinTest.Source,
        BinTest.EOp == EO_And ? EO_Or : EO_And,
        BinTest.LTest.Var, invertConsumedUnconsumed(BinTest.LTest.TestsFor),
        BinTest.RTest.Var, invertConsumedUnconsumed(BinTest.RTest.TestsFor));
    } else {
      return PropagationInfo();
    }
  }
};

static inline void
setStateForVarOrTmp(ConsumedStateMap *StateMap, const PropagationInfo &PInfo,
                    ConsumedState State) {

  assert(PInfo.isVar() || PInfo.isTmp());
  
  if (PInfo.isVar())
    StateMap->setState(PInfo.getVar(), State);
  else
    StateMap->setState(PInfo.getTmp(), State);
}

class ConsumedStmtVisitor : public ConstStmtVisitor<ConsumedStmtVisitor> {
  
  typedef llvm::DenseMap<const Stmt *, PropagationInfo> MapType;
  typedef std::pair<const Stmt *, PropagationInfo> PairType;
  typedef MapType::iterator InfoEntry;
  typedef MapType::const_iterator ConstInfoEntry;
  
  AnalysisDeclContext &AC;
  ConsumedAnalyzer &Analyzer;
  ConsumedStateMap *StateMap;
  MapType PropagationMap;

  InfoEntry findInfo(const Expr *E) {
    return PropagationMap.find(E->IgnoreParens());
  }
  ConstInfoEntry findInfo(const Expr *E) const {
    return PropagationMap.find(E->IgnoreParens());
  }
  void insertInfo(const Expr *E, const PropagationInfo &PI) {
    PropagationMap.insert(PairType(E->IgnoreParens(), PI));
  }

  void forwardInfo(const Expr *From, const Expr *To);
  void copyInfo(const Expr *From, const Expr *To, ConsumedState CS);
  ConsumedState getInfo(const Expr *From);
  void setInfo(const Expr *To, ConsumedState NS);
  void propagateReturnType(const Expr *Call, const FunctionDecl *Fun);

public:
  void checkCallability(const PropagationInfo &PInfo,
                        const FunctionDecl *FunDecl,
                        SourceLocation BlameLoc);
  bool handleCall(const CallExpr *Call, const Expr *ObjArg,
                  const FunctionDecl *FunD);
  
  void VisitBinaryOperator(const BinaryOperator *BinOp);
  void VisitCallExpr(const CallExpr *Call);
  void VisitCastExpr(const CastExpr *Cast);
  void VisitCXXBindTemporaryExpr(const CXXBindTemporaryExpr *Temp);
  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 VisitParmVarDecl(const ParmVarDecl *Param);
  void VisitReturnStmt(const ReturnStmt *Ret);
  void VisitUnaryOperator(const UnaryOperator *UOp);
  void VisitVarDecl(const VarDecl *Var);

  ConsumedStmtVisitor(AnalysisDeclContext &AC, ConsumedAnalyzer &Analyzer,
                      ConsumedStateMap *StateMap)
      : AC(AC), Analyzer(Analyzer), StateMap(StateMap) {}
  
  PropagationInfo getInfo(const Expr *StmtNode) const {
    ConstInfoEntry Entry = findInfo(StmtNode);
    
    if (Entry != PropagationMap.end())
      return Entry->second;
    else
      return PropagationInfo();
  }
  
  void reset(ConsumedStateMap *NewStateMap) {
    StateMap = NewStateMap;
  }
};


void ConsumedStmtVisitor::forwardInfo(const Expr *From, const Expr *To) {
  InfoEntry Entry = findInfo(From);
  if (Entry != PropagationMap.end())
    insertInfo(To, Entry->second);
}


// Create a new state for To, which is initialized to the state of From.
// If NS is not CS_None, sets the state of From to NS.
void ConsumedStmtVisitor::copyInfo(const Expr *From, const Expr *To,
                                   ConsumedState NS) {
  InfoEntry Entry = findInfo(From);
  if (Entry != PropagationMap.end()) {
    PropagationInfo& PInfo = Entry->second;
    ConsumedState CS = PInfo.getAsState(StateMap);
    if (CS != CS_None)
      insertInfo(To, PropagationInfo(CS));
    if (NS != CS_None && PInfo.isPointerToValue())
      setStateForVarOrTmp(StateMap, PInfo, NS);
  }
}


// Get the ConsumedState for From
ConsumedState ConsumedStmtVisitor::getInfo(const Expr *From) {
  InfoEntry Entry = findInfo(From);
  if (Entry != PropagationMap.end()) {
    PropagationInfo& PInfo = Entry->second;
    return PInfo.getAsState(StateMap);
  }
  return CS_None;
}


// If we already have info for To then update it, otherwise create a new entry.
void ConsumedStmtVisitor::setInfo(const Expr *To, ConsumedState NS) {
  InfoEntry Entry = findInfo(To);
  if (Entry != PropagationMap.end()) {
    PropagationInfo& PInfo = Entry->second;
    if (PInfo.isPointerToValue())
      setStateForVarOrTmp(StateMap, PInfo, NS);
  } else if (NS != CS_None) {
     insertInfo(To, PropagationInfo(NS));
  }
}



void ConsumedStmtVisitor::checkCallability(const PropagationInfo &PInfo,
                                           const FunctionDecl *FunDecl,
                                           SourceLocation BlameLoc) {
  assert(!PInfo.isTest());

  const CallableWhenAttr *CWAttr = FunDecl->getAttr<CallableWhenAttr>();
  if (!CWAttr)
    return;

  if (PInfo.isVar()) {
    ConsumedState VarState = StateMap->getState(PInfo.getVar());

    if (VarState == CS_None || isCallableInState(CWAttr, VarState))
      return;

    Analyzer.WarningsHandler.warnUseInInvalidState(
      FunDecl->getNameAsString(), PInfo.getVar()->getNameAsString(),
      stateToString(VarState), BlameLoc);

  } else {
    ConsumedState TmpState = PInfo.getAsState(StateMap);

    if (TmpState == CS_None || isCallableInState(CWAttr, TmpState))
      return;

    Analyzer.WarningsHandler.warnUseOfTempInInvalidState(
      FunDecl->getNameAsString(), stateToString(TmpState), BlameLoc);
  }
}


// Factors out common behavior for function, method, and operator calls.
// Check parameters and set parameter state if necessary.
// Returns true if the state of ObjArg is set, or false otherwise.
bool ConsumedStmtVisitor::handleCall(const CallExpr *Call, const Expr *ObjArg,
                                     const FunctionDecl *FunD) {
  unsigned Offset = 0;
  if (isa<CXXOperatorCallExpr>(Call) && isa<CXXMethodDecl>(FunD))
    Offset = 1;  // first argument is 'this'

  // check explicit parameters
  for (unsigned Index = Offset; Index < Call->getNumArgs(); ++Index) {
    // Skip variable argument lists.
    if (Index - Offset >= FunD->getNumParams())
      break;

    const ParmVarDecl *Param = FunD->getParamDecl(Index - Offset);
    QualType ParamType = Param->getType();

    InfoEntry Entry = findInfo(Call->getArg(Index));

    if (Entry == PropagationMap.end() || Entry->second.isTest())
      continue;
    PropagationInfo PInfo = Entry->second;

    // Check that the parameter is in the correct state.
    if (ParamTypestateAttr *PTA = Param->getAttr<ParamTypestateAttr>()) {
      ConsumedState ParamState = PInfo.getAsState(StateMap);
      ConsumedState ExpectedState = mapParamTypestateAttrState(PTA);

      if (ParamState != ExpectedState)
        Analyzer.WarningsHandler.warnParamTypestateMismatch(
          Call->getArg(Index)->getExprLoc(),
          stateToString(ExpectedState), stateToString(ParamState));
    }

    if (!(Entry->second.isVar() || Entry->second.isTmp()))
      continue;

    // Adjust state on the caller side.
    if (isRValueRef(ParamType))
      setStateForVarOrTmp(StateMap, PInfo, consumed::CS_Consumed);
    else if (ReturnTypestateAttr *RT = Param->getAttr<ReturnTypestateAttr>())
      setStateForVarOrTmp(StateMap, PInfo, mapReturnTypestateAttrState(RT));
    else if (isPointerOrRef(ParamType) &&
             (!ParamType->getPointeeType().isConstQualified() ||
              isSetOnReadPtrType(ParamType)))
      setStateForVarOrTmp(StateMap, PInfo, consumed::CS_Unknown);
  }

  if (!ObjArg)
    return false;

  // check implicit 'self' parameter, if present
  InfoEntry Entry = findInfo(ObjArg);
  if (Entry != PropagationMap.end()) {
    PropagationInfo PInfo = Entry->second;
    checkCallability(PInfo, FunD, Call->getExprLoc());

    if (SetTypestateAttr *STA = FunD->getAttr<SetTypestateAttr>()) {
      if (PInfo.isVar()) {
        StateMap->setState(PInfo.getVar(), mapSetTypestateAttrState(STA));
        return true;
      }
      else if (PInfo.isTmp()) {
        StateMap->setState(PInfo.getTmp(), mapSetTypestateAttrState(STA));
        return true;
      }
    }
    else if (isTestingFunction(FunD) && PInfo.isVar()) {
      PropagationMap.insert(PairType(Call,
        PropagationInfo(PInfo.getVar(), testsFor(FunD))));
    }
  }
  return false;
}


void ConsumedStmtVisitor::propagateReturnType(const Expr *Call,
                                              const FunctionDecl *Fun) {
  QualType RetType = Fun->getCallResultType();
  if (RetType->isReferenceType())
    RetType = RetType->getPointeeType();

  if (isConsumableType(RetType)) {
    ConsumedState ReturnState;
    if (ReturnTypestateAttr *RTA = Fun->getAttr<ReturnTypestateAttr>())
      ReturnState = mapReturnTypestateAttrState(RTA);
    else
      ReturnState = mapConsumableAttrState(RetType);
    
    PropagationMap.insert(PairType(Call, PropagationInfo(ReturnState)));
  }
}


void ConsumedStmtVisitor::VisitBinaryOperator(const BinaryOperator *BinOp) {
  switch (BinOp->getOpcode()) {
  case BO_LAnd:
  case BO_LOr : {
    InfoEntry LEntry = findInfo(BinOp->getLHS()),
              REntry = findInfo(BinOp->getRHS());
    
    VarTestResult LTest, RTest;
    
    if (LEntry != PropagationMap.end() && LEntry->second.isVarTest()) {
      LTest = LEntry->second.getVarTest();
      
    } else {
      LTest.Var      = nullptr;
      LTest.TestsFor = CS_None;
    }
    
    if (REntry != PropagationMap.end() && REntry->second.isVarTest()) {
      RTest = REntry->second.getVarTest();
      
    } else {
      RTest.Var      = nullptr;
      RTest.TestsFor = CS_None;
    }

    if (!(LTest.Var == nullptr && RTest.Var == nullptr))
      PropagationMap.insert(PairType(BinOp, PropagationInfo(BinOp,
        static_cast<EffectiveOp>(BinOp->getOpcode() == BO_LOr), LTest, RTest)));
    
    break;
  }
    
  case BO_PtrMemD:
  case BO_PtrMemI:
    forwardInfo(BinOp->getLHS(), BinOp);
    break;
    
  default:
    break;
  }
}

void ConsumedStmtVisitor::VisitCallExpr(const CallExpr *Call) {
  const FunctionDecl *FunDecl = Call->getDirectCallee();
  if (!FunDecl)
    return;

  // Special case for the std::move function.
  // TODO: Make this more specific. (Deferred)
  if (Call->getNumArgs() == 1 && FunDecl->getNameAsString() == "move" &&
      FunDecl->isInStdNamespace()) {
    copyInfo(Call->getArg(0), Call, CS_Consumed);
    return;
  }

  handleCall(Call, nullptr, FunDecl);
  propagateReturnType(Call, FunDecl);
}

void ConsumedStmtVisitor::VisitCastExpr(const CastExpr *Cast) {
  forwardInfo(Cast->getSubExpr(), Cast);
}

void ConsumedStmtVisitor::VisitCXXBindTemporaryExpr(
  const CXXBindTemporaryExpr *Temp) {
  
  InfoEntry Entry = findInfo(Temp->getSubExpr());
  
  if (Entry != PropagationMap.end() && !Entry->second.isTest()) {
    StateMap->setState(Temp, Entry->second.getAsState(StateMap));
    PropagationMap.insert(PairType(Temp, PropagationInfo(Temp)));
  }
}

void ConsumedStmtVisitor::VisitCXXConstructExpr(const CXXConstructExpr *Call) {
  CXXConstructorDecl *Constructor = Call->getConstructor();

  ASTContext &CurrContext = AC.getASTContext();
  QualType ThisType = Constructor->getThisType(CurrContext)->getPointeeType();
  
  if (!isConsumableType(ThisType))
    return;
  
  // FIXME: What should happen if someone annotates the move constructor?
  if (ReturnTypestateAttr *RTA = Constructor->getAttr<ReturnTypestateAttr>()) {
    // TODO: Adjust state of args appropriately.    
    ConsumedState RetState = mapReturnTypestateAttrState(RTA);
    PropagationMap.insert(PairType(Call, PropagationInfo(RetState)));
  } else if (Constructor->isDefaultConstructor()) {
    PropagationMap.insert(PairType(Call,
      PropagationInfo(consumed::CS_Consumed)));
  } else if (Constructor->isMoveConstructor()) {
    copyInfo(Call->getArg(0), Call, CS_Consumed);
  } else if (Constructor->isCopyConstructor()) {
    // Copy state from arg.  If setStateOnRead then set arg to CS_Unknown.
    ConsumedState NS =
      isSetOnReadPtrType(Constructor->getThisType(CurrContext)) ?
      CS_Unknown : CS_None;
    copyInfo(Call->getArg(0), Call, NS);
  } else {
    // TODO: Adjust state of args appropriately.
    ConsumedState RetState = mapConsumableAttrState(ThisType);
    PropagationMap.insert(PairType(Call, PropagationInfo(RetState)));
  }
}


void ConsumedStmtVisitor::VisitCXXMemberCallExpr(
    const CXXMemberCallExpr *Call) {
  CXXMethodDecl* MD = Call->getMethodDecl();
  if (!MD)
    return;

  handleCall(Call, Call->getImplicitObjectArgument(), MD);
  propagateReturnType(Call, MD);
}


void ConsumedStmtVisitor::VisitCXXOperatorCallExpr(
    const CXXOperatorCallExpr *Call) {

  const FunctionDecl *FunDecl =
    dyn_cast_or_null<FunctionDecl>(Call->getDirectCallee());
  if (!FunDecl) return;

  if (Call->getOperator() == OO_Equal) {
    ConsumedState CS = getInfo(Call->getArg(1));
    if (!handleCall(Call, Call->getArg(0), FunDecl))
      setInfo(Call->getArg(0), CS);
    return;
  }

  if (const CXXMemberCallExpr *MCall = dyn_cast<CXXMemberCallExpr>(Call))
    handleCall(MCall, MCall->getImplicitObjectArgument(), FunDecl);
  else
    handleCall(Call, Call->getArg(0), FunDecl);

  propagateReturnType(Call, FunDecl);
}

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 (const auto *DI : DeclS->decls())    
    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) {
  
  forwardInfo(Temp->GetTemporaryExpr(), Temp);
}

void ConsumedStmtVisitor::VisitMemberExpr(const MemberExpr *MExpr) {
  forwardInfo(MExpr->getBase(), MExpr);
}


void ConsumedStmtVisitor::VisitParmVarDecl(const ParmVarDecl *Param) {
  QualType ParamType = Param->getType();
  ConsumedState ParamState = consumed::CS_None;
  
  if (const ParamTypestateAttr *PTA = Param->getAttr<ParamTypestateAttr>())
    ParamState = mapParamTypestateAttrState(PTA);    
  else if (isConsumableType(ParamType))
    ParamState = mapConsumableAttrState(ParamType);    
  else if (isRValueRef(ParamType) &&
           isConsumableType(ParamType->getPointeeType()))
    ParamState = mapConsumableAttrState(ParamType->getPointeeType());    
  else if (ParamType->isReferenceType() &&
           isConsumableType(ParamType->getPointeeType()))
    ParamState = consumed::CS_Unknown;
  
  if (ParamState != CS_None)
    StateMap->setState(Param, ParamState);
}

void ConsumedStmtVisitor::VisitReturnStmt(const ReturnStmt *Ret) {
  ConsumedState ExpectedState = Analyzer.getExpectedReturnState();
  
  if (ExpectedState != CS_None) {
    InfoEntry Entry = findInfo(Ret->getRetValue());
    
    if (Entry != PropagationMap.end()) {
      ConsumedState RetState = Entry->second.getAsState(StateMap);
        
      if (RetState != ExpectedState)
        Analyzer.WarningsHandler.warnReturnTypestateMismatch(
          Ret->getReturnLoc(), stateToString(ExpectedState),
          stateToString(RetState));
    }
  }
  
  StateMap->checkParamsForReturnTypestate(Ret->getLocStart(),
                                          Analyzer.WarningsHandler);
}

void ConsumedStmtVisitor::VisitUnaryOperator(const UnaryOperator *UOp) {
  InfoEntry Entry = findInfo(UOp->getSubExpr());
  if (Entry == PropagationMap.end()) return;
  
  switch (UOp->getOpcode()) {
  case UO_AddrOf:
    PropagationMap.insert(PairType(UOp, Entry->second));
    break;
  
  case UO_LNot:
    if (Entry->second.isTest())
      PropagationMap.insert(PairType(UOp, Entry->second.invertTest()));
    break;
  
  default:
    break;
  }
}

// TODO: See if I need to check for reference types here.
void ConsumedStmtVisitor::VisitVarDecl(const VarDecl *Var) {
  if (isConsumableType(Var->getType())) {
    if (Var->hasInit()) {
      MapType::iterator VIT = findInfo(Var->getInit()->IgnoreImplicit());
      if (VIT != PropagationMap.end()) {
        PropagationInfo PInfo = VIT->second;
        ConsumedState St = PInfo.getAsState(StateMap);
        
        if (St != consumed::CS_None) {
          StateMap->setState(Var, St);
          return;
        }
      }
    }
    // Otherwise
    StateMap->setState(Var, consumed::CS_Unknown);
  }
}
}} // end clang::consumed::ConsumedStmtVisitor

namespace clang {
namespace consumed {

void splitVarStateForIf(const IfStmt * IfNode, const VarTestResult &Test,
                        ConsumedStateMap *ThenStates,
                        ConsumedStateMap *ElseStates) {

  ConsumedState VarState = ThenStates->getState(Test.Var);
  
  if (VarState == CS_Unknown) {
    ThenStates->setState(Test.Var, Test.TestsFor);
    ElseStates->setState(Test.Var, invertConsumedUnconsumed(Test.TestsFor));
  
  } else if (VarState == invertConsumedUnconsumed(Test.TestsFor)) {
    ThenStates->markUnreachable();
    
  } else if (VarState == Test.TestsFor) {
    ElseStates->markUnreachable();
  }
}

void splitVarStateForIfBinOp(const PropagationInfo &PInfo,
  ConsumedStateMap *ThenStates, ConsumedStateMap *ElseStates) {
  
  const VarTestResult &LTest = PInfo.getLTest(),
                      &RTest = PInfo.getRTest();
  
  ConsumedState LState = LTest.Var ? ThenStates->getState(LTest.Var) : CS_None,
                RState = RTest.Var ? ThenStates->getState(RTest.Var) : CS_None;
  
  if (LTest.Var) {
    if (PInfo.testEffectiveOp() == EO_And) {
      if (LState == CS_Unknown) {
        ThenStates->setState(LTest.Var, LTest.TestsFor);
        
      } else if (LState == invertConsumedUnconsumed(LTest.TestsFor)) {
        ThenStates->markUnreachable();
        
      } else if (LState == LTest.TestsFor && isKnownState(RState)) {
        if (RState == RTest.TestsFor)
          ElseStates->markUnreachable();
        else
          ThenStates->markUnreachable();
      }
      
    } else {
      if (LState == CS_Unknown) {
        ElseStates->setState(LTest.Var,
                             invertConsumedUnconsumed(LTest.TestsFor));
      
      } else if (LState == LTest.TestsFor) {
        ElseStates->markUnreachable();
        
      } else if (LState == invertConsumedUnconsumed(LTest.TestsFor) &&
                 isKnownState(RState)) {
        
        if (RState == RTest.TestsFor)
          ElseStates->markUnreachable();
        else
          ThenStates->markUnreachable();
      }
    }
  }
  
  if (RTest.Var) {
    if (PInfo.testEffectiveOp() == EO_And) {
      if (RState == CS_Unknown)
        ThenStates->setState(RTest.Var, RTest.TestsFor);
      else if (RState == invertConsumedUnconsumed(RTest.TestsFor))
        ThenStates->markUnreachable();
      
    } else {
      if (RState == CS_Unknown)
        ElseStates->setState(RTest.Var,
                             invertConsumedUnconsumed(RTest.TestsFor));
      else if (RState == RTest.TestsFor)
        ElseStates->markUnreachable();
    }
  }
}

bool ConsumedBlockInfo::allBackEdgesVisited(const CFGBlock *CurrBlock,
                                            const CFGBlock *TargetBlock) {
  
  assert(CurrBlock && "Block pointer must not be NULL");
  assert(TargetBlock && "TargetBlock pointer must not be NULL");
  
  unsigned int CurrBlockOrder = VisitOrder[CurrBlock->getBlockID()];
  for (CFGBlock::const_pred_iterator PI = TargetBlock->pred_begin(),
       PE = TargetBlock->pred_end(); PI != PE; ++PI) {
    if (*PI && CurrBlockOrder < VisitOrder[(*PI)->getBlockID()] )
      return false;
  }
  return true;
}

void ConsumedBlockInfo::addInfo(const CFGBlock *Block,
                                ConsumedStateMap *StateMap,
                                bool &AlreadyOwned) {
  
  assert(Block && "Block pointer must not be NULL");
  
  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) {

  assert(Block && "Block pointer must not be NULL");

  ConsumedStateMap *Entry = StateMapsArray[Block->getBlockID()];
    
  if (Entry) {
    Entry->intersect(StateMap);
    delete StateMap;
    
  } else {
    StateMapsArray[Block->getBlockID()] = StateMap;
  }
}

ConsumedStateMap* ConsumedBlockInfo::borrowInfo(const CFGBlock *Block) {
  assert(Block && "Block pointer must not be NULL");
  assert(StateMapsArray[Block->getBlockID()] && "Block has no block info");
  
  return StateMapsArray[Block->getBlockID()];
}

void ConsumedBlockInfo::discardInfo(const CFGBlock *Block) {
  unsigned int BlockID = Block->getBlockID();
  delete StateMapsArray[BlockID];
  StateMapsArray[BlockID] = nullptr;
}

ConsumedStateMap* ConsumedBlockInfo::getInfo(const CFGBlock *Block) {
  assert(Block && "Block pointer must not be NULL");
  
  ConsumedStateMap *StateMap = StateMapsArray[Block->getBlockID()];
  if (isBackEdgeTarget(Block)) {
    return new ConsumedStateMap(*StateMap);
  } else {
    StateMapsArray[Block->getBlockID()] = nullptr;
    return StateMap;
  }
}

bool ConsumedBlockInfo::isBackEdge(const CFGBlock *From, const CFGBlock *To) {
  assert(From && "From block must not be NULL");
  assert(To   && "From block must not be NULL");
  
  return VisitOrder[From->getBlockID()] > VisitOrder[To->getBlockID()];
}

bool ConsumedBlockInfo::isBackEdgeTarget(const CFGBlock *Block) {
  assert(Block && "Block pointer must not be NULL");

  // Anything with less than two predecessors can't be the target of a back
  // edge.
  if (Block->pred_size() < 2)
    return false;
  
  unsigned int BlockVisitOrder = VisitOrder[Block->getBlockID()];
  for (CFGBlock::const_pred_iterator PI = Block->pred_begin(),
       PE = Block->pred_end(); PI != PE; ++PI) {
    if (*PI && BlockVisitOrder < VisitOrder[(*PI)->getBlockID()])
      return true;
  }
  return false;
}

void ConsumedStateMap::checkParamsForReturnTypestate(SourceLocation BlameLoc,
  ConsumedWarningsHandlerBase &WarningsHandler) const {
  
  for (const auto &DM : VarMap) {
    if (isa<ParmVarDecl>(DM.first)) {
      const ParmVarDecl *Param = cast<ParmVarDecl>(DM.first);
      const ReturnTypestateAttr *RTA = Param->getAttr<ReturnTypestateAttr>();
      
      if (!RTA)
        continue;
      
      ConsumedState ExpectedState = mapReturnTypestateAttrState(RTA);      
      if (DM.second != ExpectedState)
        WarningsHandler.warnParamReturnTypestateMismatch(BlameLoc,
          Param->getNameAsString(), stateToString(ExpectedState),
          stateToString(DM.second));
    }
  }
}

void ConsumedStateMap::clearTemporaries() {
  TmpMap.clear();
}

ConsumedState ConsumedStateMap::getState(const VarDecl *Var) const {
  VarMapType::const_iterator Entry = VarMap.find(Var);
  
  if (Entry != VarMap.end())
    return Entry->second;
    
  return CS_None;
}

ConsumedState
ConsumedStateMap::getState(const CXXBindTemporaryExpr *Tmp) const {
  TmpMapType::const_iterator Entry = TmpMap.find(Tmp);
  
  if (Entry != TmpMap.end())
    return Entry->second;
  
  return CS_None;
}

void ConsumedStateMap::intersect(const ConsumedStateMap *Other) {
  ConsumedState LocalState;
  
  if (this->From && this->From == Other->From && !Other->Reachable) {
    this->markUnreachable();
    return;
  }
  
  for (const auto &DM : Other->VarMap) {
    LocalState = this->getState(DM.first);
    
    if (LocalState == CS_None)
      continue;
    
    if (LocalState != DM.second)
     VarMap[DM.first] = CS_Unknown;
  }
}

void ConsumedStateMap::intersectAtLoopHead(const CFGBlock *LoopHead,
  const CFGBlock *LoopBack, const ConsumedStateMap *LoopBackStates,
  ConsumedWarningsHandlerBase &WarningsHandler) {
  
  ConsumedState LocalState;
  SourceLocation BlameLoc = getLastStmtLoc(LoopBack);
  
  for (const auto &DM : LoopBackStates->VarMap) {    
    LocalState = this->getState(DM.first);
    
    if (LocalState == CS_None)
      continue;
    
    if (LocalState != DM.second) {
      VarMap[DM.first] = CS_Unknown;
      WarningsHandler.warnLoopStateMismatch(BlameLoc,
                                            DM.first->getNameAsString());
    }
  }
}

void ConsumedStateMap::markUnreachable() {
  this->Reachable = false;
  VarMap.clear();
  TmpMap.clear();
}

void ConsumedStateMap::setState(const VarDecl *Var, ConsumedState State) {
  VarMap[Var] = State;
}

void ConsumedStateMap::setState(const CXXBindTemporaryExpr *Tmp,
                                ConsumedState State) {
  TmpMap[Tmp] = State;
}

void ConsumedStateMap::remove(const CXXBindTemporaryExpr *Tmp) {
  TmpMap.erase(Tmp);
}

bool ConsumedStateMap::operator!=(const ConsumedStateMap *Other) const {
  for (const auto &DM : Other->VarMap)
    if (this->getState(DM.first) != DM.second)
      return true;  
  return false;
}

void ConsumedAnalyzer::determineExpectedReturnState(AnalysisDeclContext &AC,
                                                    const FunctionDecl *D) {
  QualType ReturnType;
  if (const CXXConstructorDecl *Constructor = dyn_cast<CXXConstructorDecl>(D)) {
    ASTContext &CurrContext = AC.getASTContext();
    ReturnType = Constructor->getThisType(CurrContext)->getPointeeType();
  } else
    ReturnType = D->getCallResultType();

  if (const ReturnTypestateAttr *RTSAttr = D->getAttr<ReturnTypestateAttr>()) {
    const CXXRecordDecl *RD = ReturnType->getAsCXXRecordDecl();
    if (!RD || !RD->hasAttr<ConsumableAttr>()) {
      // FIXME: This should be removed when template instantiation propagates
      //        attributes at template specialization definition, not
      //        declaration. When it is removed the test needs to be enabled
      //        in SemaDeclAttr.cpp.
      WarningsHandler.warnReturnTypestateForUnconsumableType(
          RTSAttr->getLocation(), ReturnType.getAsString());
      ExpectedReturnState = CS_None;
    } else
      ExpectedReturnState = mapReturnTypestateAttrState(RTSAttr);
  } else if (isConsumableType(ReturnType)) {
    if (isAutoCastType(ReturnType))   // We can auto-cast the state to the
      ExpectedReturnState = CS_None;  // expected state.
    else
      ExpectedReturnState = mapConsumableAttrState(ReturnType);
  }
  else
    ExpectedReturnState = CS_None;
}

bool ConsumedAnalyzer::splitState(const CFGBlock *CurrBlock,
                                  const ConsumedStmtVisitor &Visitor) {

  std::unique_ptr<ConsumedStateMap> FalseStates(
      new ConsumedStateMap(*CurrStates));
  PropagationInfo PInfo;
  
  if (const IfStmt *IfNode =
    dyn_cast_or_null<IfStmt>(CurrBlock->getTerminator().getStmt())) {
    
    const Expr *Cond = IfNode->getCond();
    
    PInfo = Visitor.getInfo(Cond);
    if (!PInfo.isValid() && isa<BinaryOperator>(Cond))
      PInfo = Visitor.getInfo(cast<BinaryOperator>(Cond)->getRHS());
    
    if (PInfo.isVarTest()) {
      CurrStates->setSource(Cond);
      FalseStates->setSource(Cond);
      splitVarStateForIf(IfNode, PInfo.getVarTest(), CurrStates,
                         FalseStates.get());
      
    } else if (PInfo.isBinTest()) {
      CurrStates->setSource(PInfo.testSourceNode());
      FalseStates->setSource(PInfo.testSourceNode());
      splitVarStateForIfBinOp(PInfo, CurrStates, FalseStates.get());
      
    } else {
      return false;
    }
    
  } else if (const BinaryOperator *BinOp =
    dyn_cast_or_null<BinaryOperator>(CurrBlock->getTerminator().getStmt())) {
    
    PInfo = Visitor.getInfo(BinOp->getLHS());
    if (!PInfo.isVarTest()) {
      if ((BinOp = dyn_cast_or_null<BinaryOperator>(BinOp->getLHS()))) {
        PInfo = Visitor.getInfo(BinOp->getRHS());
        
        if (!PInfo.isVarTest())
          return false;
        
      } else {
        return false;
      }
    }
    
    CurrStates->setSource(BinOp);
    FalseStates->setSource(BinOp);
    
    const VarTestResult &Test = PInfo.getVarTest();
    ConsumedState VarState = CurrStates->getState(Test.Var);
    
    if (BinOp->getOpcode() == BO_LAnd) {
      if (VarState == CS_Unknown)
        CurrStates->setState(Test.Var, Test.TestsFor);
      else if (VarState == invertConsumedUnconsumed(Test.TestsFor))
        CurrStates->markUnreachable();
      
    } else if (BinOp->getOpcode() == BO_LOr) {
      if (VarState == CS_Unknown)
        FalseStates->setState(Test.Var,
                              invertConsumedUnconsumed(Test.TestsFor));
      else if (VarState == Test.TestsFor)
        FalseStates->markUnreachable();
    }
    
  } else {
    return false;
  }
  
  CFGBlock::const_succ_iterator SI = CurrBlock->succ_begin();
  
  if (*SI)
    BlockInfo.addInfo(*SI, CurrStates);
  else
    delete CurrStates;
    
  if (*++SI)
    BlockInfo.addInfo(*SI, FalseStates.release());

  CurrStates = nullptr;
  return true;
}

void ConsumedAnalyzer::run(AnalysisDeclContext &AC) {
  const FunctionDecl *D = dyn_cast_or_null<FunctionDecl>(AC.getDecl());
  if (!D)
    return;
  
  CFG *CFGraph = AC.getCFG();
  if (!CFGraph)
    return;

  determineExpectedReturnState(AC, D);

  PostOrderCFGView *SortedGraph = AC.getAnalysis<PostOrderCFGView>();
  // AC.getCFG()->viewCFG(LangOptions());
  
  BlockInfo = ConsumedBlockInfo(CFGraph->getNumBlockIDs(), SortedGraph);
  
  CurrStates = new ConsumedStateMap();
  ConsumedStmtVisitor Visitor(AC, *this, CurrStates);
  
  // Add all trackable parameters to the state map.
  for (const auto *PI : D->params())
    Visitor.VisitParmVarDecl(PI);
  
  // Visit all of the function's basic blocks.
  for (const auto *CurrBlock : *SortedGraph) {
    if (!CurrStates)
      CurrStates = BlockInfo.getInfo(CurrBlock);
    
    if (!CurrStates) {
      continue;
      
    } else if (!CurrStates->isReachable()) {
      delete CurrStates;
      CurrStates = nullptr;
      continue;
    }
    
    Visitor.reset(CurrStates);
    
    // Visit all of the basic block's statements.
    for (const auto &B : *CurrBlock) {
      switch (B.getKind()) {
      case CFGElement::Statement:
        Visitor.Visit(B.castAs<CFGStmt>().getStmt());
        break;
        
      case CFGElement::TemporaryDtor: {
        const CFGTemporaryDtor &DTor = B.castAs<CFGTemporaryDtor>();
        const CXXBindTemporaryExpr *BTE = DTor.getBindTemporaryExpr();
        
        Visitor.checkCallability(PropagationInfo(BTE),
                                 DTor.getDestructorDecl(AC.getASTContext()),
                                 BTE->getExprLoc());
        CurrStates->remove(BTE);
        break;
      }
      
      case CFGElement::AutomaticObjectDtor: {
        const CFGAutomaticObjDtor &DTor = B.castAs<CFGAutomaticObjDtor>();
        SourceLocation Loc = DTor.getTriggerStmt()->getLocEnd();
        const VarDecl *Var = DTor.getVarDecl();
        
        Visitor.checkCallability(PropagationInfo(Var),
                                 DTor.getDestructorDecl(AC.getASTContext()),
                                 Loc);
        break;
      }
      
      default:
        break;
      }
    }
    
    // TODO: Handle other forms of branching with precision, including while-
    //       and for-loops. (Deferred)
    if (!splitState(CurrBlock, Visitor)) {
      CurrStates->setSource(nullptr);

      if (CurrBlock->succ_size() > 1 ||
          (CurrBlock->succ_size() == 1 &&
           (*CurrBlock->succ_begin())->pred_size() > 1)) {
        
        bool OwnershipTaken = false;
        
        for (CFGBlock::const_succ_iterator SI = CurrBlock->succ_begin(),
             SE = CurrBlock->succ_end(); SI != SE; ++SI) {

          if (*SI == nullptr) continue;

          if (BlockInfo.isBackEdge(CurrBlock, *SI)) {
            BlockInfo.borrowInfo(*SI)->intersectAtLoopHead(*SI, CurrBlock,
                                                           CurrStates,
                                                           WarningsHandler);
            
            if (BlockInfo.allBackEdgesVisited(*SI, CurrBlock))
              BlockInfo.discardInfo(*SI);
          } else {
            BlockInfo.addInfo(*SI, CurrStates, OwnershipTaken);
          }
        }
        
        if (!OwnershipTaken)
          delete CurrStates;

        CurrStates = nullptr;
      }
    }
    
    if (CurrBlock == &AC.getCFG()->getExit() &&
        D->getCallResultType()->isVoidType())
      CurrStates->checkParamsForReturnTypestate(D->getLocation(),
                                                WarningsHandler);
  } // End of block iterator.
  
  // Delete the last existing state map.
  delete CurrStates;
  
  WarningsHandler.emitDiagnostics();
}
}} // end namespace clang::consumed
@


1.1.1.4.4.1
log
@file Consumed.cpp was added on branch yamt-pagecache on 2014-05-22 16:18:26 +0000
@
text
@d1 1499
@


1.1.1.4.4.2
log
@sync with head.

for a reference, the tree before this commit was tagged
as yamt-pagecache-tag8.

this commit was splitted into small chunks to avoid
a limitation of cvs.  ("Protocol error: too many arguments")
@
text
@a0 1499
//===- 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/StmtCXX.h"
#include "clang/AST/StmtVisitor.h"
#include "clang/AST/Type.h"
#include "clang/Analysis/Analyses/Consumed.h"
#include "clang/Analysis/Analyses/PostOrderCFGView.h"
#include "clang/Analysis/AnalysisContext.h"
#include "clang/Analysis/CFG.h"
#include "clang/Basic/OperatorKinds.h"
#include "clang/Basic/SourceLocation.h"
#include "llvm/ADT/DenseMap.h"
#include "llvm/ADT/OwningPtr.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/Support/Compiler.h"
#include "llvm/Support/raw_ostream.h"

// TODO: Adjust states of args to constructors in the same way that arguments to
//       function calls are handled.
// TODO: Use information from tests in for- and while-loop conditional.
// TODO: Add notes about the actual and expected state for 
// TODO: Correctly identify unreachable blocks when chaining boolean operators.
// TODO: Adjust the parser and AttributesList class to support lists of
//       identifiers.
// TODO: Warn about unreachable code.
// TODO: Switch to using a bitmap to track unreachable blocks.
// TODO: Handle variable definitions, e.g. bool valid = x.isValid();
//       if (valid) ...; (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)

using namespace clang;
using namespace consumed;

// Key method definition
ConsumedWarningsHandlerBase::~ConsumedWarningsHandlerBase() {}

static SourceLocation getFirstStmtLoc(const CFGBlock *Block) {
  // Find the source location of the first statement in the block, if the block
  // is not empty.
  for (CFGBlock::const_iterator BI = Block->begin(), BE = Block->end();
       BI != BE; ++BI) {
    if (Optional<CFGStmt> CS = BI->getAs<CFGStmt>())
      return CS->getStmt()->getLocStart();
  }

  // Block is empty.
  // If we have one successor, return the first statement in that block
  if (Block->succ_size() == 1 && *Block->succ_begin())
    return getFirstStmtLoc(*Block->succ_begin());

  return SourceLocation();
}

static SourceLocation getLastStmtLoc(const CFGBlock *Block) {
  // Find the source location of the last statement in the block, if the block
  // is not empty.
  if (const Stmt *StmtNode = Block->getTerminator()) {
    return StmtNode->getLocStart();
  } else {
    for (CFGBlock::const_reverse_iterator BI = Block->rbegin(),
         BE = Block->rend(); BI != BE; ++BI) {
      if (Optional<CFGStmt> CS = BI->getAs<CFGStmt>())
        return CS->getStmt()->getLocStart();
    }
  }

  // If we have one successor, return the first statement in that block
  SourceLocation Loc;
  if (Block->succ_size() == 1 && *Block->succ_begin())
    Loc = getFirstStmtLoc(*Block->succ_begin());
  if (Loc.isValid())
    return Loc;

  // If we have one predecessor, return the last statement in that block
  if (Block->pred_size() == 1 && *Block->pred_begin())
    return getLastStmtLoc(*Block->pred_begin());

  return Loc;
}

static ConsumedState invertConsumedUnconsumed(ConsumedState State) {
  switch (State) {
  case CS_Unconsumed:
    return CS_Consumed;
  case CS_Consumed:
    return CS_Unconsumed;
  case CS_None:
    return CS_None;
  case CS_Unknown:
    return CS_Unknown;
  }
  llvm_unreachable("invalid enum");
}

static bool isCallableInState(const CallableWhenAttr *CWAttr,
                              ConsumedState State) {
  
  CallableWhenAttr::callableState_iterator I = CWAttr->callableState_begin(),
                                           E = CWAttr->callableState_end();
  
  for (; I != E; ++I) {
    
    ConsumedState MappedAttrState = CS_None;
    
    switch (*I) {
    case CallableWhenAttr::Unknown:
      MappedAttrState = CS_Unknown;
      break;
      
    case CallableWhenAttr::Unconsumed:
      MappedAttrState = CS_Unconsumed;
      break;
      
    case CallableWhenAttr::Consumed:
      MappedAttrState = CS_Consumed;
      break;
    }
    
    if (MappedAttrState == State)
      return true;
  }
  
  return false;
}


static bool isConsumableType(const QualType &QT) {
  if (QT->isPointerType() || QT->isReferenceType())
    return false;
  
  if (const CXXRecordDecl *RD = QT->getAsCXXRecordDecl())
    return RD->hasAttr<ConsumableAttr>();
  
  return false;
}

static bool isAutoCastType(const QualType &QT) {
  if (QT->isPointerType() || QT->isReferenceType())
    return false;

  if (const CXXRecordDecl *RD = QT->getAsCXXRecordDecl())
    return RD->hasAttr<ConsumableAutoCastAttr>();

  return false;
}

static bool isSetOnReadPtrType(const QualType &QT) {
  if (const CXXRecordDecl *RD = QT->getPointeeCXXRecordDecl())
    return RD->hasAttr<ConsumableSetOnReadAttr>();
  return false;
}


static bool isKnownState(ConsumedState State) {
  switch (State) {
  case CS_Unconsumed:
  case CS_Consumed:
    return true;
  case CS_None:
  case CS_Unknown:
    return false;
  }
  llvm_unreachable("invalid enum");
}

static bool isRValueRef(QualType ParamType) {
  return ParamType->isRValueReferenceType();
}

static bool isTestingFunction(const FunctionDecl *FunDecl) {
  return FunDecl->hasAttr<TestTypestateAttr>();
}

static bool isPointerOrRef(QualType ParamType) {
  return ParamType->isPointerType() || ParamType->isReferenceType();
}

static ConsumedState mapConsumableAttrState(const QualType QT) {
  assert(isConsumableType(QT));

  const ConsumableAttr *CAttr =
      QT->getAsCXXRecordDecl()->getAttr<ConsumableAttr>();

  switch (CAttr->getDefaultState()) {
  case ConsumableAttr::Unknown:
    return CS_Unknown;
  case ConsumableAttr::Unconsumed:
    return CS_Unconsumed;
  case ConsumableAttr::Consumed:
    return CS_Consumed;
  }
  llvm_unreachable("invalid enum");
}

static ConsumedState
mapParamTypestateAttrState(const ParamTypestateAttr *PTAttr) {
  switch (PTAttr->getParamState()) {
  case ParamTypestateAttr::Unknown:
    return CS_Unknown;
  case ParamTypestateAttr::Unconsumed:
    return CS_Unconsumed;
  case ParamTypestateAttr::Consumed:
    return CS_Consumed;
  }
  llvm_unreachable("invalid_enum");
}

static ConsumedState
mapReturnTypestateAttrState(const ReturnTypestateAttr *RTSAttr) {
  switch (RTSAttr->getState()) {
  case ReturnTypestateAttr::Unknown:
    return CS_Unknown;
  case ReturnTypestateAttr::Unconsumed:
    return CS_Unconsumed;
  case ReturnTypestateAttr::Consumed:
    return CS_Consumed;
  }
  llvm_unreachable("invalid enum");
}

static ConsumedState mapSetTypestateAttrState(const SetTypestateAttr *STAttr) {
  switch (STAttr->getNewState()) {
  case SetTypestateAttr::Unknown:
    return CS_Unknown;
  case SetTypestateAttr::Unconsumed:
    return CS_Unconsumed;
  case SetTypestateAttr::Consumed:
    return CS_Consumed;
  }
  llvm_unreachable("invalid_enum");
}

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");
}

static ConsumedState testsFor(const FunctionDecl *FunDecl) {
  assert(isTestingFunction(FunDecl));
  switch (FunDecl->getAttr<TestTypestateAttr>()->getTestState()) {
  case TestTypestateAttr::Unconsumed:
    return CS_Unconsumed;
  case TestTypestateAttr::Consumed:
    return CS_Consumed;
  }
  llvm_unreachable("invalid enum");
}

namespace {
struct VarTestResult {
  const VarDecl *Var;
  ConsumedState TestsFor;
};
} // end anonymous::VarTestResult

namespace clang {
namespace consumed {

enum EffectiveOp {
  EO_And,
  EO_Or
};

class PropagationInfo {
  enum {
    IT_None,
    IT_State,
    IT_VarTest,
    IT_BinTest,
    IT_Var,
    IT_Tmp
  } InfoType;

  struct BinTestTy {
    const BinaryOperator *Source;
    EffectiveOp EOp;
    VarTestResult LTest;
    VarTestResult RTest;
  };
  
  union {
    ConsumedState State;
    VarTestResult VarTest;
    const VarDecl *Var;
    const CXXBindTemporaryExpr *Tmp;
    BinTestTy BinTest;
  };
  
public:
  PropagationInfo() : InfoType(IT_None) {}
  
  PropagationInfo(const VarTestResult &VarTest)
    : InfoType(IT_VarTest), VarTest(VarTest) {}
  
  PropagationInfo(const VarDecl *Var, ConsumedState TestsFor)
    : InfoType(IT_VarTest) {
    
    VarTest.Var      = Var;
    VarTest.TestsFor = TestsFor;
  }
  
  PropagationInfo(const BinaryOperator *Source, EffectiveOp EOp,
                  const VarTestResult &LTest, const VarTestResult &RTest)
    : InfoType(IT_BinTest) {
    
    BinTest.Source  = Source;
    BinTest.EOp     = EOp;
    BinTest.LTest   = LTest;
    BinTest.RTest   = RTest;
  }
  
  PropagationInfo(const BinaryOperator *Source, EffectiveOp EOp,
                  const VarDecl *LVar, ConsumedState LTestsFor,
                  const VarDecl *RVar, ConsumedState RTestsFor)
    : InfoType(IT_BinTest) {
    
    BinTest.Source         = Source;
    BinTest.EOp            = EOp;
    BinTest.LTest.Var      = LVar;
    BinTest.LTest.TestsFor = LTestsFor;
    BinTest.RTest.Var      = RVar;
    BinTest.RTest.TestsFor = RTestsFor;
  }
  
  PropagationInfo(ConsumedState State)
    : InfoType(IT_State), State(State) {}
  
  PropagationInfo(const VarDecl *Var) : InfoType(IT_Var), Var(Var) {}
  PropagationInfo(const CXXBindTemporaryExpr *Tmp)
    : InfoType(IT_Tmp), Tmp(Tmp) {}
  
  const ConsumedState & getState() const {
    assert(InfoType == IT_State);
    return State;
  }
  
  const VarTestResult & getVarTest() const {
    assert(InfoType == IT_VarTest);
    return VarTest;
  }
  
  const VarTestResult & getLTest() const {
    assert(InfoType == IT_BinTest);
    return BinTest.LTest;
  }
  
  const VarTestResult & getRTest() const {
    assert(InfoType == IT_BinTest);
    return BinTest.RTest;
  }
  
  const VarDecl * getVar() const {
    assert(InfoType == IT_Var);
    return Var;
  }
  
  const CXXBindTemporaryExpr * getTmp() const {
    assert(InfoType == IT_Tmp);
    return Tmp;
  }
  
  ConsumedState getAsState(const ConsumedStateMap *StateMap) const {
    assert(isVar() || isTmp() || isState());
    
    if (isVar())
      return StateMap->getState(Var);
    else if (isTmp())
      return StateMap->getState(Tmp);
    else if (isState())
      return State;
    else
      return CS_None;
  }
  
  EffectiveOp testEffectiveOp() const {
    assert(InfoType == IT_BinTest);
    return BinTest.EOp;
  }
  
  const BinaryOperator * testSourceNode() const {
    assert(InfoType == IT_BinTest);
    return BinTest.Source;
  }
  
  inline bool isValid()   const { return InfoType != IT_None;    }
  inline bool isState()   const { return InfoType == IT_State;   }
  inline bool isVarTest() const { return InfoType == IT_VarTest; }
  inline bool isBinTest() const { return InfoType == IT_BinTest; }
  inline bool isVar()     const { return InfoType == IT_Var;     }
  inline bool isTmp()     const { return InfoType == IT_Tmp;     }
  
  bool isTest() const {
    return InfoType == IT_VarTest || InfoType == IT_BinTest;
  }
  
  bool isPointerToValue() const {
    return InfoType == IT_Var || InfoType == IT_Tmp;
  }
  
  PropagationInfo invertTest() const {
    assert(InfoType == IT_VarTest || InfoType == IT_BinTest);
    
    if (InfoType == IT_VarTest) {
      return PropagationInfo(VarTest.Var,
                             invertConsumedUnconsumed(VarTest.TestsFor));
    
    } else if (InfoType == IT_BinTest) {
      return PropagationInfo(BinTest.Source,
        BinTest.EOp == EO_And ? EO_Or : EO_And,
        BinTest.LTest.Var, invertConsumedUnconsumed(BinTest.LTest.TestsFor),
        BinTest.RTest.Var, invertConsumedUnconsumed(BinTest.RTest.TestsFor));
    } else {
      return PropagationInfo();
    }
  }
};

static inline void
setStateForVarOrTmp(ConsumedStateMap *StateMap, const PropagationInfo &PInfo,
                    ConsumedState State) {

  assert(PInfo.isVar() || PInfo.isTmp());
  
  if (PInfo.isVar())
    StateMap->setState(PInfo.getVar(), State);
  else
    StateMap->setState(PInfo.getTmp(), State);
}

class ConsumedStmtVisitor : public ConstStmtVisitor<ConsumedStmtVisitor> {
  
  typedef llvm::DenseMap<const Stmt *, PropagationInfo> MapType;
  typedef std::pair<const Stmt *, PropagationInfo> PairType;
  typedef MapType::iterator InfoEntry;
  typedef MapType::const_iterator ConstInfoEntry;
  
  AnalysisDeclContext &AC;
  ConsumedAnalyzer &Analyzer;
  ConsumedStateMap *StateMap;
  MapType PropagationMap;

  void forwardInfo(const Stmt *From, const Stmt *To);
  void copyInfo(const Stmt *From, const Stmt *To, ConsumedState CS);
  ConsumedState getInfo(const Stmt *From);
  void setInfo(const Stmt *To, ConsumedState NS);
  void propagateReturnType(const Stmt *Call, const FunctionDecl *Fun);

public:
  void checkCallability(const PropagationInfo &PInfo,
                        const FunctionDecl *FunDecl,
                        SourceLocation BlameLoc);
  bool handleCall(const CallExpr *Call, const Expr *ObjArg,
                  const FunctionDecl *FunD);
  
  void VisitBinaryOperator(const BinaryOperator *BinOp);
  void VisitCallExpr(const CallExpr *Call);
  void VisitCastExpr(const CastExpr *Cast);
  void VisitCXXBindTemporaryExpr(const CXXBindTemporaryExpr *Temp);
  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 VisitParmVarDecl(const ParmVarDecl *Param);
  void VisitReturnStmt(const ReturnStmt *Ret);
  void VisitUnaryOperator(const UnaryOperator *UOp);
  void VisitVarDecl(const VarDecl *Var);

  ConsumedStmtVisitor(AnalysisDeclContext &AC, ConsumedAnalyzer &Analyzer,
                      ConsumedStateMap *StateMap)
      : AC(AC), Analyzer(Analyzer), StateMap(StateMap) {}
  
  PropagationInfo getInfo(const Stmt *StmtNode) const {
    ConstInfoEntry Entry = PropagationMap.find(StmtNode);
    
    if (Entry != PropagationMap.end())
      return Entry->second;
    else
      return PropagationInfo();
  }
  
  void reset(ConsumedStateMap *NewStateMap) {
    StateMap = NewStateMap;
  }
};


void ConsumedStmtVisitor::forwardInfo(const Stmt *From, const Stmt *To) {
  InfoEntry Entry = PropagationMap.find(From);
  if (Entry != PropagationMap.end())
    PropagationMap.insert(PairType(To, Entry->second));
}


// Create a new state for To, which is initialized to the state of From.
// If NS is not CS_None, sets the state of From to NS.
void ConsumedStmtVisitor::copyInfo(const Stmt *From, const Stmt *To,
                                   ConsumedState NS) {
  InfoEntry Entry = PropagationMap.find(From);
  if (Entry != PropagationMap.end()) {
    PropagationInfo& PInfo = Entry->second;
    ConsumedState CS = PInfo.getAsState(StateMap);
    if (CS != CS_None)
      PropagationMap.insert(PairType(To, CS));
    if (NS != CS_None && PInfo.isPointerToValue())
      setStateForVarOrTmp(StateMap, PInfo, NS);
  }
}


// Get the ConsumedState for From
ConsumedState ConsumedStmtVisitor::getInfo(const Stmt *From) {
  InfoEntry Entry = PropagationMap.find(From);
  if (Entry != PropagationMap.end()) {
    PropagationInfo& PInfo = Entry->second;
    return PInfo.getAsState(StateMap);
  }
  return CS_None;
}


// If we already have info for To then update it, otherwise create a new entry.
void ConsumedStmtVisitor::setInfo(const Stmt *To, ConsumedState NS) {
  InfoEntry Entry = PropagationMap.find(To);
  if (Entry != PropagationMap.end()) {
    PropagationInfo& PInfo = Entry->second;
    if (PInfo.isPointerToValue())
      setStateForVarOrTmp(StateMap, PInfo, NS);
  } else if (NS != CS_None) {
     PropagationMap.insert(PairType(To, PropagationInfo(NS)));
  }
}



void ConsumedStmtVisitor::checkCallability(const PropagationInfo &PInfo,
                                           const FunctionDecl *FunDecl,
                                           SourceLocation BlameLoc) {
  assert(!PInfo.isTest());

  const CallableWhenAttr *CWAttr = FunDecl->getAttr<CallableWhenAttr>();
  if (!CWAttr)
    return;

  if (PInfo.isVar()) {
    ConsumedState VarState = StateMap->getState(PInfo.getVar());

    if (VarState == CS_None || isCallableInState(CWAttr, VarState))
      return;

    Analyzer.WarningsHandler.warnUseInInvalidState(
      FunDecl->getNameAsString(), PInfo.getVar()->getNameAsString(),
      stateToString(VarState), BlameLoc);

  } else {
    ConsumedState TmpState = PInfo.getAsState(StateMap);

    if (TmpState == CS_None || isCallableInState(CWAttr, TmpState))
      return;

    Analyzer.WarningsHandler.warnUseOfTempInInvalidState(
      FunDecl->getNameAsString(), stateToString(TmpState), BlameLoc);
  }
}


// Factors out common behavior for function, method, and operator calls.
// Check parameters and set parameter state if necessary.
// Returns true if the state of ObjArg is set, or false otherwise.
bool ConsumedStmtVisitor::handleCall(const CallExpr *Call, const Expr *ObjArg,
                                     const FunctionDecl *FunD) {
  unsigned Offset = 0;
  if (isa<CXXOperatorCallExpr>(Call) && isa<CXXMethodDecl>(FunD))
    Offset = 1;  // first argument is 'this'

  // check explicit parameters
  for (unsigned Index = Offset; Index < Call->getNumArgs(); ++Index) {
    // Skip variable argument lists.
    if (Index - Offset >= FunD->getNumParams())
      break;

    const ParmVarDecl *Param = FunD->getParamDecl(Index - Offset);
    QualType ParamType = Param->getType();

    InfoEntry Entry = PropagationMap.find(Call->getArg(Index));

    if (Entry == PropagationMap.end() || Entry->second.isTest())
      continue;
    PropagationInfo PInfo = Entry->second;

    // Check that the parameter is in the correct state.
    if (ParamTypestateAttr *PTA = Param->getAttr<ParamTypestateAttr>()) {
      ConsumedState ParamState = PInfo.getAsState(StateMap);
      ConsumedState ExpectedState = mapParamTypestateAttrState(PTA);

      if (ParamState != ExpectedState)
        Analyzer.WarningsHandler.warnParamTypestateMismatch(
          Call->getArg(Index)->getExprLoc(),
          stateToString(ExpectedState), stateToString(ParamState));
    }

    if (!(Entry->second.isVar() || Entry->second.isTmp()))
      continue;

    // Adjust state on the caller side.
    if (isRValueRef(ParamType))
      setStateForVarOrTmp(StateMap, PInfo, consumed::CS_Consumed);
    else if (ReturnTypestateAttr *RT = Param->getAttr<ReturnTypestateAttr>())
      setStateForVarOrTmp(StateMap, PInfo, mapReturnTypestateAttrState(RT));
    else if (isPointerOrRef(ParamType) &&
             (!ParamType->getPointeeType().isConstQualified() ||
              isSetOnReadPtrType(ParamType)))
      setStateForVarOrTmp(StateMap, PInfo, consumed::CS_Unknown);
  }

  if (!ObjArg)
    return false;

  // check implicit 'self' parameter, if present
  InfoEntry Entry = PropagationMap.find(ObjArg);
  if (Entry != PropagationMap.end()) {
    PropagationInfo PInfo = Entry->second;
    checkCallability(PInfo, FunD, Call->getExprLoc());

    if (SetTypestateAttr *STA = FunD->getAttr<SetTypestateAttr>()) {
      if (PInfo.isVar()) {
        StateMap->setState(PInfo.getVar(), mapSetTypestateAttrState(STA));
        return true;
      }
      else if (PInfo.isTmp()) {
        StateMap->setState(PInfo.getTmp(), mapSetTypestateAttrState(STA));
        return true;
      }
    }
    else if (isTestingFunction(FunD) && PInfo.isVar()) {
      PropagationMap.insert(PairType(Call,
        PropagationInfo(PInfo.getVar(), testsFor(FunD))));
    }
  }
  return false;
}


void ConsumedStmtVisitor::propagateReturnType(const Stmt *Call,
                                              const FunctionDecl *Fun) {
  QualType RetType = Fun->getCallResultType();
  if (RetType->isReferenceType())
    RetType = RetType->getPointeeType();

  if (isConsumableType(RetType)) {
    ConsumedState ReturnState;
    if (ReturnTypestateAttr *RTA = Fun->getAttr<ReturnTypestateAttr>())
      ReturnState = mapReturnTypestateAttrState(RTA);
    else
      ReturnState = mapConsumableAttrState(RetType);
    
    PropagationMap.insert(PairType(Call, PropagationInfo(ReturnState)));
  }
}


void ConsumedStmtVisitor::VisitBinaryOperator(const BinaryOperator *BinOp) {
  switch (BinOp->getOpcode()) {
  case BO_LAnd:
  case BO_LOr : {
    InfoEntry LEntry = PropagationMap.find(BinOp->getLHS()),
              REntry = PropagationMap.find(BinOp->getRHS());
    
    VarTestResult LTest, RTest;
    
    if (LEntry != PropagationMap.end() && LEntry->second.isVarTest()) {
      LTest = LEntry->second.getVarTest();
      
    } else {
      LTest.Var      = NULL;
      LTest.TestsFor = CS_None;
    }
    
    if (REntry != PropagationMap.end() && REntry->second.isVarTest()) {
      RTest = REntry->second.getVarTest();
      
    } else {
      RTest.Var      = NULL;
      RTest.TestsFor = CS_None;
    }
    
    if (!(LTest.Var == NULL && RTest.Var == NULL))
      PropagationMap.insert(PairType(BinOp, PropagationInfo(BinOp,
        static_cast<EffectiveOp>(BinOp->getOpcode() == BO_LOr), LTest, RTest)));
    
    break;
  }
    
  case BO_PtrMemD:
  case BO_PtrMemI:
    forwardInfo(BinOp->getLHS(), BinOp);
    break;
    
  default:
    break;
  }
}

static bool isStdNamespace(const DeclContext *DC) {
  if (!DC->isNamespace()) return false;
  while (DC->getParent()->isNamespace())
    DC = DC->getParent();
  const NamespaceDecl *ND = dyn_cast<NamespaceDecl>(DC);

  return ND && ND->getName() == "std" &&
         ND->getDeclContext()->isTranslationUnit();
}

void ConsumedStmtVisitor::VisitCallExpr(const CallExpr *Call) {
  const FunctionDecl *FunDecl = Call->getDirectCallee();
  if (!FunDecl)
    return;

  // Special case for the std::move function.
  // TODO: Make this more specific. (Deferred)
  if (Call->getNumArgs() == 1 &&
      FunDecl->getNameAsString() == "move" &&
      isStdNamespace(FunDecl->getDeclContext())) {
    copyInfo(Call->getArg(0), Call, CS_Consumed);
    return;
  }

  handleCall(Call, 0, FunDecl);
  propagateReturnType(Call, FunDecl);
}

void ConsumedStmtVisitor::VisitCastExpr(const CastExpr *Cast) {
  forwardInfo(Cast->getSubExpr(), Cast);
}

void ConsumedStmtVisitor::VisitCXXBindTemporaryExpr(
  const CXXBindTemporaryExpr *Temp) {
  
  InfoEntry Entry = PropagationMap.find(Temp->getSubExpr());
  
  if (Entry != PropagationMap.end() && !Entry->second.isTest()) {
    StateMap->setState(Temp, Entry->second.getAsState(StateMap));
    PropagationMap.insert(PairType(Temp, PropagationInfo(Temp)));
  }
}

void ConsumedStmtVisitor::VisitCXXConstructExpr(const CXXConstructExpr *Call) {
  CXXConstructorDecl *Constructor = Call->getConstructor();

  ASTContext &CurrContext = AC.getASTContext();
  QualType ThisType = Constructor->getThisType(CurrContext)->getPointeeType();
  
  if (!isConsumableType(ThisType))
    return;
  
  // FIXME: What should happen if someone annotates the move constructor?
  if (ReturnTypestateAttr *RTA = Constructor->getAttr<ReturnTypestateAttr>()) {
    // TODO: Adjust state of args appropriately.    
    ConsumedState RetState = mapReturnTypestateAttrState(RTA);
    PropagationMap.insert(PairType(Call, PropagationInfo(RetState)));
  } else if (Constructor->isDefaultConstructor()) {
    PropagationMap.insert(PairType(Call,
      PropagationInfo(consumed::CS_Consumed)));
  } else if (Constructor->isMoveConstructor()) {
    copyInfo(Call->getArg(0), Call, CS_Consumed);
  } else if (Constructor->isCopyConstructor()) {
    // Copy state from arg.  If setStateOnRead then set arg to CS_Unknown.
    ConsumedState NS =
      isSetOnReadPtrType(Constructor->getThisType(CurrContext)) ?
      CS_Unknown : CS_None;
    copyInfo(Call->getArg(0), Call, NS);
  } else {
    // TODO: Adjust state of args appropriately.
    ConsumedState RetState = mapConsumableAttrState(ThisType);
    PropagationMap.insert(PairType(Call, PropagationInfo(RetState)));
  }
}


void ConsumedStmtVisitor::VisitCXXMemberCallExpr(
    const CXXMemberCallExpr *Call) {
  CXXMethodDecl* MD = Call->getMethodDecl();
  if (!MD)
    return;

  handleCall(Call, Call->getImplicitObjectArgument(), MD);
  propagateReturnType(Call, MD);
}


void ConsumedStmtVisitor::VisitCXXOperatorCallExpr(
    const CXXOperatorCallExpr *Call) {

  const FunctionDecl *FunDecl =
    dyn_cast_or_null<FunctionDecl>(Call->getDirectCallee());
  if (!FunDecl) return;

  if (Call->getOperator() == OO_Equal) {
    ConsumedState CS = getInfo(Call->getArg(1));
    if (!handleCall(Call, Call->getArg(0), FunDecl))
      setInfo(Call->getArg(0), CS);
    return;
  }

  if (const CXXMemberCallExpr *MCall = dyn_cast<CXXMemberCallExpr>(Call))
    handleCall(MCall, MCall->getImplicitObjectArgument(), FunDecl);
  else
    handleCall(Call, Call->getArg(0), FunDecl);

  propagateReturnType(Call, FunDecl);
}

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) {
  
  forwardInfo(Temp->GetTemporaryExpr(), Temp);
}

void ConsumedStmtVisitor::VisitMemberExpr(const MemberExpr *MExpr) {
  forwardInfo(MExpr->getBase(), MExpr);
}


void ConsumedStmtVisitor::VisitParmVarDecl(const ParmVarDecl *Param) {
  QualType ParamType = Param->getType();
  ConsumedState ParamState = consumed::CS_None;
  
  if (const ParamTypestateAttr *PTA = Param->getAttr<ParamTypestateAttr>())
    ParamState = mapParamTypestateAttrState(PTA);    
  else if (isConsumableType(ParamType))
    ParamState = mapConsumableAttrState(ParamType);    
  else if (isRValueRef(ParamType) &&
           isConsumableType(ParamType->getPointeeType()))
    ParamState = mapConsumableAttrState(ParamType->getPointeeType());    
  else if (ParamType->isReferenceType() &&
           isConsumableType(ParamType->getPointeeType()))
    ParamState = consumed::CS_Unknown;
  
  if (ParamState != CS_None)
    StateMap->setState(Param, ParamState);
}

void ConsumedStmtVisitor::VisitReturnStmt(const ReturnStmt *Ret) {
  ConsumedState ExpectedState = Analyzer.getExpectedReturnState();
  
  if (ExpectedState != CS_None) {
    InfoEntry Entry = PropagationMap.find(Ret->getRetValue());
    
    if (Entry != PropagationMap.end()) {
      ConsumedState RetState = Entry->second.getAsState(StateMap);
        
      if (RetState != ExpectedState)
        Analyzer.WarningsHandler.warnReturnTypestateMismatch(
          Ret->getReturnLoc(), stateToString(ExpectedState),
          stateToString(RetState));
    }
  }
  
  StateMap->checkParamsForReturnTypestate(Ret->getLocStart(),
                                          Analyzer.WarningsHandler);
}

void ConsumedStmtVisitor::VisitUnaryOperator(const UnaryOperator *UOp) {
  InfoEntry Entry = PropagationMap.find(UOp->getSubExpr()->IgnoreParens());
  if (Entry == PropagationMap.end()) return;
  
  switch (UOp->getOpcode()) {
  case UO_AddrOf:
    PropagationMap.insert(PairType(UOp, Entry->second));
    break;
  
  case UO_LNot:
    if (Entry->second.isTest())
      PropagationMap.insert(PairType(UOp, Entry->second.invertTest()));
    break;
  
  default:
    break;
  }
}

// TODO: See if I need to check for reference types here.
void ConsumedStmtVisitor::VisitVarDecl(const VarDecl *Var) {
  if (isConsumableType(Var->getType())) {
    if (Var->hasInit()) {
      MapType::iterator VIT = PropagationMap.find(
        Var->getInit()->IgnoreImplicit());
      if (VIT != PropagationMap.end()) {
        PropagationInfo PInfo = VIT->second;
        ConsumedState St = PInfo.getAsState(StateMap);
        
        if (St != consumed::CS_None) {
          StateMap->setState(Var, St);
          return;
        }
      }
    }
    // Otherwise
    StateMap->setState(Var, consumed::CS_Unknown);
  }
}
}} // end clang::consumed::ConsumedStmtVisitor

namespace clang {
namespace consumed {

void splitVarStateForIf(const IfStmt * IfNode, const VarTestResult &Test,
                        ConsumedStateMap *ThenStates,
                        ConsumedStateMap *ElseStates) {

  ConsumedState VarState = ThenStates->getState(Test.Var);
  
  if (VarState == CS_Unknown) {
    ThenStates->setState(Test.Var, Test.TestsFor);
    ElseStates->setState(Test.Var, invertConsumedUnconsumed(Test.TestsFor));
  
  } else if (VarState == invertConsumedUnconsumed(Test.TestsFor)) {
    ThenStates->markUnreachable();
    
  } else if (VarState == Test.TestsFor) {
    ElseStates->markUnreachable();
  }
}

void splitVarStateForIfBinOp(const PropagationInfo &PInfo,
  ConsumedStateMap *ThenStates, ConsumedStateMap *ElseStates) {
  
  const VarTestResult &LTest = PInfo.getLTest(),
                      &RTest = PInfo.getRTest();
  
  ConsumedState LState = LTest.Var ? ThenStates->getState(LTest.Var) : CS_None,
                RState = RTest.Var ? ThenStates->getState(RTest.Var) : CS_None;
  
  if (LTest.Var) {
    if (PInfo.testEffectiveOp() == EO_And) {
      if (LState == CS_Unknown) {
        ThenStates->setState(LTest.Var, LTest.TestsFor);
        
      } else if (LState == invertConsumedUnconsumed(LTest.TestsFor)) {
        ThenStates->markUnreachable();
        
      } else if (LState == LTest.TestsFor && isKnownState(RState)) {
        if (RState == RTest.TestsFor)
          ElseStates->markUnreachable();
        else
          ThenStates->markUnreachable();
      }
      
    } else {
      if (LState == CS_Unknown) {
        ElseStates->setState(LTest.Var,
                             invertConsumedUnconsumed(LTest.TestsFor));
      
      } else if (LState == LTest.TestsFor) {
        ElseStates->markUnreachable();
        
      } else if (LState == invertConsumedUnconsumed(LTest.TestsFor) &&
                 isKnownState(RState)) {
        
        if (RState == RTest.TestsFor)
          ElseStates->markUnreachable();
        else
          ThenStates->markUnreachable();
      }
    }
  }
  
  if (RTest.Var) {
    if (PInfo.testEffectiveOp() == EO_And) {
      if (RState == CS_Unknown)
        ThenStates->setState(RTest.Var, RTest.TestsFor);
      else if (RState == invertConsumedUnconsumed(RTest.TestsFor))
        ThenStates->markUnreachable();
      
    } else {
      if (RState == CS_Unknown)
        ElseStates->setState(RTest.Var,
                             invertConsumedUnconsumed(RTest.TestsFor));
      else if (RState == RTest.TestsFor)
        ElseStates->markUnreachable();
    }
  }
}

bool ConsumedBlockInfo::allBackEdgesVisited(const CFGBlock *CurrBlock,
                                            const CFGBlock *TargetBlock) {
  
  assert(CurrBlock && "Block pointer must not be NULL");
  assert(TargetBlock && "TargetBlock pointer must not be NULL");
  
  unsigned int CurrBlockOrder = VisitOrder[CurrBlock->getBlockID()];
  for (CFGBlock::const_pred_iterator PI = TargetBlock->pred_begin(),
       PE = TargetBlock->pred_end(); PI != PE; ++PI) {
    if (*PI && CurrBlockOrder < VisitOrder[(*PI)->getBlockID()] )
      return false;
  }
  return true;
}

void ConsumedBlockInfo::addInfo(const CFGBlock *Block,
                                ConsumedStateMap *StateMap,
                                bool &AlreadyOwned) {
  
  assert(Block && "Block pointer must not be NULL");
  
  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) {
  
  assert(Block != NULL && "Block pointer must not be NULL");
  
  ConsumedStateMap *Entry = StateMapsArray[Block->getBlockID()];
    
  if (Entry) {
    Entry->intersect(StateMap);
    delete StateMap;
    
  } else {
    StateMapsArray[Block->getBlockID()] = StateMap;
  }
}

ConsumedStateMap* ConsumedBlockInfo::borrowInfo(const CFGBlock *Block) {
  assert(Block && "Block pointer must not be NULL");
  assert(StateMapsArray[Block->getBlockID()] && "Block has no block info");
  
  return StateMapsArray[Block->getBlockID()];
}

void ConsumedBlockInfo::discardInfo(const CFGBlock *Block) {
  unsigned int BlockID = Block->getBlockID();
  delete StateMapsArray[BlockID];
  StateMapsArray[BlockID] = NULL;
}

ConsumedStateMap* ConsumedBlockInfo::getInfo(const CFGBlock *Block) {
  assert(Block && "Block pointer must not be NULL");
  
  ConsumedStateMap *StateMap = StateMapsArray[Block->getBlockID()];
  if (isBackEdgeTarget(Block)) {
    return new ConsumedStateMap(*StateMap);
  } else {
    StateMapsArray[Block->getBlockID()] = NULL;
    return StateMap;
  }
}

bool ConsumedBlockInfo::isBackEdge(const CFGBlock *From, const CFGBlock *To) {
  assert(From && "From block must not be NULL");
  assert(To   && "From block must not be NULL");
  
  return VisitOrder[From->getBlockID()] > VisitOrder[To->getBlockID()];
}

bool ConsumedBlockInfo::isBackEdgeTarget(const CFGBlock *Block) {
  assert(Block != NULL && "Block pointer must not be NULL");
  
  // Anything with less than two predecessors can't be the target of a back
  // edge.
  if (Block->pred_size() < 2)
    return false;
  
  unsigned int BlockVisitOrder = VisitOrder[Block->getBlockID()];
  for (CFGBlock::const_pred_iterator PI = Block->pred_begin(),
       PE = Block->pred_end(); PI != PE; ++PI) {
    if (*PI && BlockVisitOrder < VisitOrder[(*PI)->getBlockID()])
      return true;
  }
  return false;
}

void ConsumedStateMap::checkParamsForReturnTypestate(SourceLocation BlameLoc,
  ConsumedWarningsHandlerBase &WarningsHandler) const {
  
  for (VarMapType::const_iterator DMI = VarMap.begin(), DME = VarMap.end();
       DMI != DME; ++DMI) {
    
    if (isa<ParmVarDecl>(DMI->first)) {
      const ParmVarDecl *Param = cast<ParmVarDecl>(DMI->first);
      const ReturnTypestateAttr *RTA = Param->getAttr<ReturnTypestateAttr>();
      
      if (!RTA)
        continue;
      
      ConsumedState ExpectedState = mapReturnTypestateAttrState(RTA);      
      if (DMI->second != ExpectedState)
        WarningsHandler.warnParamReturnTypestateMismatch(BlameLoc,
          Param->getNameAsString(), stateToString(ExpectedState),
          stateToString(DMI->second));
    }
  }
}

void ConsumedStateMap::clearTemporaries() {
  TmpMap.clear();
}

ConsumedState ConsumedStateMap::getState(const VarDecl *Var) const {
  VarMapType::const_iterator Entry = VarMap.find(Var);
  
  if (Entry != VarMap.end())
    return Entry->second;
    
  return CS_None;
}

ConsumedState
ConsumedStateMap::getState(const CXXBindTemporaryExpr *Tmp) const {
  TmpMapType::const_iterator Entry = TmpMap.find(Tmp);
  
  if (Entry != TmpMap.end())
    return Entry->second;
  
  return CS_None;
}

void ConsumedStateMap::intersect(const ConsumedStateMap *Other) {
  ConsumedState LocalState;
  
  if (this->From && this->From == Other->From && !Other->Reachable) {
    this->markUnreachable();
    return;
  }
  
  for (VarMapType::const_iterator DMI = Other->VarMap.begin(),
       DME = Other->VarMap.end(); DMI != DME; ++DMI) {
    
    LocalState = this->getState(DMI->first);
    
    if (LocalState == CS_None)
      continue;
    
    if (LocalState != DMI->second)
       VarMap[DMI->first] = CS_Unknown;
  }
}

void ConsumedStateMap::intersectAtLoopHead(const CFGBlock *LoopHead,
  const CFGBlock *LoopBack, const ConsumedStateMap *LoopBackStates,
  ConsumedWarningsHandlerBase &WarningsHandler) {
  
  ConsumedState LocalState;
  SourceLocation BlameLoc = getLastStmtLoc(LoopBack);
  
  for (VarMapType::const_iterator DMI = LoopBackStates->VarMap.begin(),
       DME = LoopBackStates->VarMap.end(); DMI != DME; ++DMI) {
    
    LocalState = this->getState(DMI->first);
    
    if (LocalState == CS_None)
      continue;
    
    if (LocalState != DMI->second) {
      VarMap[DMI->first] = CS_Unknown;
      WarningsHandler.warnLoopStateMismatch(
        BlameLoc, DMI->first->getNameAsString());
    }
  }
}

void ConsumedStateMap::markUnreachable() {
  this->Reachable = false;
  VarMap.clear();
  TmpMap.clear();
}

void ConsumedStateMap::setState(const VarDecl *Var, ConsumedState State) {
  VarMap[Var] = State;
}

void ConsumedStateMap::setState(const CXXBindTemporaryExpr *Tmp,
                                ConsumedState State) {
  TmpMap[Tmp] = State;
}

void ConsumedStateMap::remove(const VarDecl *Var) {
  VarMap.erase(Var);
}

bool ConsumedStateMap::operator!=(const ConsumedStateMap *Other) const {
  for (VarMapType::const_iterator DMI = Other->VarMap.begin(),
       DME = Other->VarMap.end(); DMI != DME; ++DMI) {
    
    if (this->getState(DMI->first) != DMI->second)
      return true;
  }
  
  return false;
}

void ConsumedAnalyzer::determineExpectedReturnState(AnalysisDeclContext &AC,
                                                    const FunctionDecl *D) {
  QualType ReturnType;
  if (const CXXConstructorDecl *Constructor = dyn_cast<CXXConstructorDecl>(D)) {
    ASTContext &CurrContext = AC.getASTContext();
    ReturnType = Constructor->getThisType(CurrContext)->getPointeeType();
  } else
    ReturnType = D->getCallResultType();

  if (const ReturnTypestateAttr *RTSAttr = D->getAttr<ReturnTypestateAttr>()) {
    const CXXRecordDecl *RD = ReturnType->getAsCXXRecordDecl();
    if (!RD || !RD->hasAttr<ConsumableAttr>()) {
      // FIXME: This should be removed when template instantiation propagates
      //        attributes at template specialization definition, not
      //        declaration. When it is removed the test needs to be enabled
      //        in SemaDeclAttr.cpp.
      WarningsHandler.warnReturnTypestateForUnconsumableType(
          RTSAttr->getLocation(), ReturnType.getAsString());
      ExpectedReturnState = CS_None;
    } else
      ExpectedReturnState = mapReturnTypestateAttrState(RTSAttr);
  } else if (isConsumableType(ReturnType)) {
    if (isAutoCastType(ReturnType))   // We can auto-cast the state to the
      ExpectedReturnState = CS_None;  // expected state.
    else
      ExpectedReturnState = mapConsumableAttrState(ReturnType);
  }
  else
    ExpectedReturnState = CS_None;
}

bool ConsumedAnalyzer::splitState(const CFGBlock *CurrBlock,
                                  const ConsumedStmtVisitor &Visitor) {
  
  OwningPtr<ConsumedStateMap> FalseStates(new ConsumedStateMap(*CurrStates));
  PropagationInfo PInfo;
  
  if (const IfStmt *IfNode =
    dyn_cast_or_null<IfStmt>(CurrBlock->getTerminator().getStmt())) {
    
    const Stmt *Cond = IfNode->getCond();
    
    PInfo = Visitor.getInfo(Cond);
    if (!PInfo.isValid() && isa<BinaryOperator>(Cond))
      PInfo = Visitor.getInfo(cast<BinaryOperator>(Cond)->getRHS());
    
    if (PInfo.isVarTest()) {
      CurrStates->setSource(Cond);
      FalseStates->setSource(Cond);
      splitVarStateForIf(IfNode, PInfo.getVarTest(), CurrStates,
                         FalseStates.get());
      
    } else if (PInfo.isBinTest()) {
      CurrStates->setSource(PInfo.testSourceNode());
      FalseStates->setSource(PInfo.testSourceNode());
      splitVarStateForIfBinOp(PInfo, CurrStates, FalseStates.get());
      
    } else {
      return false;
    }
    
  } else if (const BinaryOperator *BinOp =
    dyn_cast_or_null<BinaryOperator>(CurrBlock->getTerminator().getStmt())) {
    
    PInfo = Visitor.getInfo(BinOp->getLHS());
    if (!PInfo.isVarTest()) {
      if ((BinOp = dyn_cast_or_null<BinaryOperator>(BinOp->getLHS()))) {
        PInfo = Visitor.getInfo(BinOp->getRHS());
        
        if (!PInfo.isVarTest())
          return false;
        
      } else {
        return false;
      }
    }
    
    CurrStates->setSource(BinOp);
    FalseStates->setSource(BinOp);
    
    const VarTestResult &Test = PInfo.getVarTest();
    ConsumedState VarState = CurrStates->getState(Test.Var);
    
    if (BinOp->getOpcode() == BO_LAnd) {
      if (VarState == CS_Unknown)
        CurrStates->setState(Test.Var, Test.TestsFor);
      else if (VarState == invertConsumedUnconsumed(Test.TestsFor))
        CurrStates->markUnreachable();
      
    } else if (BinOp->getOpcode() == BO_LOr) {
      if (VarState == CS_Unknown)
        FalseStates->setState(Test.Var,
                              invertConsumedUnconsumed(Test.TestsFor));
      else if (VarState == Test.TestsFor)
        FalseStates->markUnreachable();
    }
    
  } else {
    return false;
  }
  
  CFGBlock::const_succ_iterator SI = CurrBlock->succ_begin();
  
  if (*SI)
    BlockInfo.addInfo(*SI, CurrStates);
  else
    delete CurrStates;
    
  if (*++SI)
    BlockInfo.addInfo(*SI, FalseStates.take());
  
  CurrStates = NULL;
  return true;
}

void ConsumedAnalyzer::run(AnalysisDeclContext &AC) {
  const FunctionDecl *D = dyn_cast_or_null<FunctionDecl>(AC.getDecl());
  if (!D)
    return;
  
  CFG *CFGraph = AC.getCFG();
  if (!CFGraph)
    return;

  determineExpectedReturnState(AC, D);

  PostOrderCFGView *SortedGraph = AC.getAnalysis<PostOrderCFGView>();
  // AC.getCFG()->viewCFG(LangOptions());
  
  BlockInfo = ConsumedBlockInfo(CFGraph->getNumBlockIDs(), SortedGraph);
  
  CurrStates = new ConsumedStateMap();
  ConsumedStmtVisitor Visitor(AC, *this, CurrStates);
  
  // Add all trackable parameters to the state map.
  for (FunctionDecl::param_const_iterator PI = D->param_begin(),
       PE = D->param_end(); PI != PE; ++PI) {
    Visitor.VisitParmVarDecl(*PI);
  }
  
  // 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);
    
    if (!CurrStates) {
      continue;
      
    } else if (!CurrStates->isReachable()) {
      delete CurrStates;
      CurrStates = NULL;
      continue;
    }
    
    Visitor.reset(CurrStates);
    
    // Visit all of the basic block's statements.
    for (CFGBlock::const_iterator BI = CurrBlock->begin(),
         BE = CurrBlock->end(); BI != BE; ++BI) {
      
      switch (BI->getKind()) {
      case CFGElement::Statement:
        Visitor.Visit(BI->castAs<CFGStmt>().getStmt());
        break;
        
      case CFGElement::TemporaryDtor: {
        const CFGTemporaryDtor DTor = BI->castAs<CFGTemporaryDtor>();
        const CXXBindTemporaryExpr *BTE = DTor.getBindTemporaryExpr();
        
        Visitor.checkCallability(PropagationInfo(BTE),
                                 DTor.getDestructorDecl(AC.getASTContext()),
                                 BTE->getExprLoc());
        break;
      }
      
      case CFGElement::AutomaticObjectDtor: {
        const CFGAutomaticObjDtor DTor = BI->castAs<CFGAutomaticObjDtor>();
        SourceLocation Loc = DTor.getTriggerStmt()->getLocEnd();
        const VarDecl *Var = DTor.getVarDecl();
        
        Visitor.checkCallability(PropagationInfo(Var),
                                 DTor.getDestructorDecl(AC.getASTContext()),
                                 Loc);
        break;
      }
      
      default:
        break;
      }
    }
    
    CurrStates->clearTemporaries();
    
    // TODO: Handle other forms of branching with precision, including while-
    //       and for-loops. (Deferred)
    if (!splitState(CurrBlock, Visitor)) {
      CurrStates->setSource(NULL);
      
      if (CurrBlock->succ_size() > 1 ||
          (CurrBlock->succ_size() == 1 &&
           (*CurrBlock->succ_begin())->pred_size() > 1)) {
        
        bool OwnershipTaken = false;
        
        for (CFGBlock::const_succ_iterator SI = CurrBlock->succ_begin(),
             SE = CurrBlock->succ_end(); SI != SE; ++SI) {
          
          if (*SI == NULL) continue;
          
          if (BlockInfo.isBackEdge(CurrBlock, *SI)) {
            BlockInfo.borrowInfo(*SI)->intersectAtLoopHead(*SI, CurrBlock,
                                                           CurrStates,
                                                           WarningsHandler);
            
            if (BlockInfo.allBackEdgesVisited(*SI, CurrBlock))
              BlockInfo.discardInfo(*SI);
          } else {
            BlockInfo.addInfo(*SI, CurrStates, OwnershipTaken);
          }
        }
        
        if (!OwnershipTaken)
          delete CurrStates;
        
        CurrStates = NULL;
      }
    }
    
    if (CurrBlock == &AC.getCFG()->getExit() &&
        D->getCallResultType()->isVoidType())
      CurrStates->checkParamsForReturnTypestate(D->getLocation(),
                                                WarningsHandler);
  } // End of block iterator.
  
  // Delete the last existing state map.
  delete CurrStates;
  
  WarningsHandler.emitDiagnostics();
}
}} // end namespace clang::consumed
@


