diff options
Diffstat (limited to 'lib/Analysis/Consumed.cpp')
| -rw-r--r-- | lib/Analysis/Consumed.cpp | 1415 | 
1 files changed, 0 insertions, 1415 deletions
diff --git a/lib/Analysis/Consumed.cpp b/lib/Analysis/Consumed.cpp deleted file mode 100644 index cde753e8ec576..0000000000000 --- a/lib/Analysis/Consumed.cpp +++ /dev/null @@ -1,1415 +0,0 @@ -//===- Consumed.cpp -------------------------------------------------------===// -// -// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -//===----------------------------------------------------------------------===// -// -// A intra-procedural analysis for checking consumed properties.  This is based, -// in part, on research on linear types. -// -//===----------------------------------------------------------------------===// - -#include "clang/Analysis/Analyses/Consumed.h" -#include "clang/AST/Attr.h" -#include "clang/AST/Decl.h" -#include "clang/AST/DeclCXX.h" -#include "clang/AST/Expr.h" -#include "clang/AST/ExprCXX.h" -#include "clang/AST/Stmt.h" -#include "clang/AST/StmtVisitor.h" -#include "clang/AST/Type.h" -#include "clang/Analysis/Analyses/PostOrderCFGView.h" -#include "clang/Analysis/AnalysisDeclContext.h" -#include "clang/Analysis/CFG.h" -#include "clang/Basic/LLVM.h" -#include "clang/Basic/OperatorKinds.h" -#include "clang/Basic/SourceLocation.h" -#include "llvm/ADT/DenseMap.h" -#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> -#include <memory> -#include <utility> - -// 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() = default; - -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()->getBeginLoc(); - -  // 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 {}; -} - -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->getTerminatorStmt()) { -    return StmtNode->getBeginLoc(); -  } 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()->getBeginLoc(); -    } -  } - -  // 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; -}; - -} // namespace - -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 = IT_None; - -  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() = default; -  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 <est, 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; -  } - -  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; } - -  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 {}; -    } -  } -}; - -} // namespace consumed -} // namespace clang - -static 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); -} - -namespace clang { -namespace consumed { - -class ConsumedStmtVisitor : public ConstStmtVisitor<ConsumedStmtVisitor> { -  using MapType = llvm::DenseMap<const Stmt *, PropagationInfo>; -  using PairType= std::pair<const Stmt *, PropagationInfo>; -  using InfoEntry = MapType::iterator; -  using ConstInfoEntry = MapType::const_iterator; - -  ConsumedAnalyzer &Analyzer; -  ConsumedStateMap *StateMap; -  MapType PropagationMap; - -  InfoEntry findInfo(const Expr *E) { -    if (const auto Cleanups = dyn_cast<ExprWithCleanups>(E)) -      if (!Cleanups->cleanupsHaveSideEffects()) -        E = Cleanups->getSubExpr(); -    return PropagationMap.find(E->IgnoreParens()); -  } - -  ConstInfoEntry findInfo(const Expr *E) const { -    if (const auto Cleanups = dyn_cast<ExprWithCleanups>(E)) -      if (!Cleanups->cleanupsHaveSideEffects()) -        E = Cleanups->getSubExpr(); -    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(ConsumedAnalyzer &Analyzer, ConsumedStateMap *StateMap) -      : Analyzer(Analyzer), StateMap(StateMap) {} - -  PropagationInfo getInfo(const Expr *StmtNode) const { -    ConstInfoEntry Entry = findInfo(StmtNode); - -    if (Entry != PropagationMap.end()) -      return Entry->second; -    else -      return {}; -  } - -  void reset(ConsumedStateMap *NewStateMap) { -    StateMap = NewStateMap; -  } -}; - -} // namespace consumed -} // namespace clang - -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 (ReturnTypestateAttr *RT = Param->getAttr<ReturnTypestateAttr>()) -      setStateForVarOrTmp(StateMap, PInfo, mapReturnTypestateAttrState(RT)); -    else if (isRValueRef(ParamType) || isConsumableType(ParamType)) -      setStateForVarOrTmp(StateMap, PInfo, consumed::CS_Consumed); -    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->isCallToStdMove()) { -    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(); - -  QualType ThisType = Constructor->getThisType()->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()) ? -      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 auto *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 auto *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 auto *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 auto *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->getBeginLoc(), -                                          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); -  } -} - -static 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(); -  } -} - -static void splitVarStateForIfBinOp(const PropagationInfo &PInfo, -                                    ConsumedStateMap *ThenStates, -                                    ConsumedStateMap *ElseStates) { -  const VarTestResult <est = 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, -    std::unique_ptr<ConsumedStateMap> &OwnedStateMap) { -  assert(Block && "Block pointer must not be NULL"); - -  auto &Entry = StateMapsArray[Block->getBlockID()]; - -  if (Entry) { -    Entry->intersect(*StateMap); -  } else if (OwnedStateMap) -    Entry = std::move(OwnedStateMap); -  else -    Entry = std::make_unique<ConsumedStateMap>(*StateMap); -} - -void ConsumedBlockInfo::addInfo(const CFGBlock *Block, -                                std::unique_ptr<ConsumedStateMap> StateMap) { -  assert(Block && "Block pointer must not be NULL"); - -  auto &Entry = StateMapsArray[Block->getBlockID()]; - -  if (Entry) { -    Entry->intersect(*StateMap); -  } else { -    Entry = std::move(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()].get(); -} - -void ConsumedBlockInfo::discardInfo(const CFGBlock *Block) { -  StateMapsArray[Block->getBlockID()] = nullptr; -} - -std::unique_ptr<ConsumedStateMap> -ConsumedBlockInfo::getInfo(const CFGBlock *Block) { -  assert(Block && "Block pointer must not be NULL"); - -  auto &Entry = StateMapsArray[Block->getBlockID()]; -  return isBackEdgeTarget(Block) ? std::make_unique<ConsumedStateMap>(*Entry) -                                 : std::move(Entry); -} - -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 auto *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 auto *Constructor = dyn_cast<CXXConstructorDecl>(D)) { -    ReturnType = Constructor->getThisType()->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 auto *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.get(), -                         FalseStates.get()); -    } else if (PInfo.isBinTest()) { -      CurrStates->setSource(PInfo.testSourceNode()); -      FalseStates->setSource(PInfo.testSourceNode()); -      splitVarStateForIfBinOp(PInfo, CurrStates.get(), FalseStates.get()); -    } else { -      return false; -    } -  } else if (const auto *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, std::move(CurrStates)); -  else -    CurrStates = nullptr; - -  if (*++SI) -    BlockInfo.addInfo(*SI, std::move(FalseStates)); - -  return true; -} - -void ConsumedAnalyzer::run(AnalysisDeclContext &AC) { -  const auto *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 = std::make_unique<ConsumedStateMap>(); -  ConsumedStmtVisitor Visitor(*this, CurrStates.get()); - -  // Add all trackable parameters to the state map. -  for (const auto *PI : D->parameters()) -    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()) { -      CurrStates = nullptr; -      continue; -    } - -    Visitor.reset(CurrStates.get()); - -    // 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()->getEndLoc(); -        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)) { - -        auto *RawState = CurrStates.get(); - -        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, RawState, WarningsHandler); - -            if (BlockInfo.allBackEdgesVisited(CurrBlock, *SI)) -              BlockInfo.discardInfo(*SI); -          } else { -            BlockInfo.addInfo(*SI, RawState, 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. -  CurrStates = nullptr; - -  WarningsHandler.emitDiagnostics(); -}  | 
