brintos

brintos / llvm-project-archived public Read only

0
0
Text · 43.0 KiB · efc7098 Raw
1413 lines · cpp
1//===- Consumed.cpp -------------------------------------------------------===//2//3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.4// See https://llvm.org/LICENSE.txt for license information.5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception6//7//===----------------------------------------------------------------------===//8//9// A intra-procedural analysis for checking consumed properties.  This is based,10// in part, on research on linear types.11//12//===----------------------------------------------------------------------===//13 14#include "clang/Analysis/Analyses/Consumed.h"15#include "clang/AST/Attr.h"16#include "clang/AST/Decl.h"17#include "clang/AST/DeclCXX.h"18#include "clang/AST/Expr.h"19#include "clang/AST/ExprCXX.h"20#include "clang/AST/Stmt.h"21#include "clang/AST/StmtVisitor.h"22#include "clang/AST/Type.h"23#include "clang/Analysis/Analyses/PostOrderCFGView.h"24#include "clang/Analysis/AnalysisDeclContext.h"25#include "clang/Analysis/CFG.h"26#include "clang/Basic/LLVM.h"27#include "clang/Basic/OperatorKinds.h"28#include "clang/Basic/SourceLocation.h"29#include "llvm/ADT/DenseMap.h"30#include "llvm/Support/ErrorHandling.h"31#include <cassert>32#include <memory>33#include <optional>34#include <utility>35 36// TODO: Adjust states of args to constructors in the same way that arguments to37//       function calls are handled.38// TODO: Use information from tests in for- and while-loop conditional.39// TODO: Add notes about the actual and expected state for40// TODO: Correctly identify unreachable blocks when chaining boolean operators.41// TODO: Adjust the parser and AttributesList class to support lists of42//       identifiers.43// TODO: Warn about unreachable code.44// TODO: Switch to using a bitmap to track unreachable blocks.45// TODO: Handle variable definitions, e.g. bool valid = x.isValid();46//       if (valid) ...; (Deferred)47// TODO: Take notes on state transitions to provide better warning messages.48//       (Deferred)49// TODO: Test nested conditionals: A) Checking the same value multiple times,50//       and 2) Checking different values. (Deferred)51 52using namespace clang;53using namespace consumed;54 55// Key method definition56ConsumedWarningsHandlerBase::~ConsumedWarningsHandlerBase() = default;57 58static SourceLocation getFirstStmtLoc(const CFGBlock *Block) {59  // Find the source location of the first statement in the block, if the block60  // is not empty.61  for (const auto &B : *Block)62    if (std::optional<CFGStmt> CS = B.getAs<CFGStmt>())63      return CS->getStmt()->getBeginLoc();64 65  // Block is empty.66  // If we have one successor, return the first statement in that block67  if (Block->succ_size() == 1 && *Block->succ_begin())68    return getFirstStmtLoc(*Block->succ_begin());69 70  return {};71}72 73static SourceLocation getLastStmtLoc(const CFGBlock *Block) {74  // Find the source location of the last statement in the block, if the block75  // is not empty.76  if (const Stmt *StmtNode = Block->getTerminatorStmt()) {77    return StmtNode->getBeginLoc();78  } else {79    for (CFGBlock::const_reverse_iterator BI = Block->rbegin(),80         BE = Block->rend(); BI != BE; ++BI) {81      if (std::optional<CFGStmt> CS = BI->getAs<CFGStmt>())82        return CS->getStmt()->getBeginLoc();83    }84  }85 86  // If we have one successor, return the first statement in that block87  SourceLocation Loc;88  if (Block->succ_size() == 1 && *Block->succ_begin())89    Loc = getFirstStmtLoc(*Block->succ_begin());90  if (Loc.isValid())91    return Loc;92 93  // If we have one predecessor, return the last statement in that block94  if (Block->pred_size() == 1 && *Block->pred_begin())95    return getLastStmtLoc(*Block->pred_begin());96 97  return Loc;98}99 100static ConsumedState invertConsumedUnconsumed(ConsumedState State) {101  switch (State) {102  case CS_Unconsumed:103    return CS_Consumed;104  case CS_Consumed:105    return CS_Unconsumed;106  case CS_None:107    return CS_None;108  case CS_Unknown:109    return CS_Unknown;110  }111  llvm_unreachable("invalid enum");112}113 114static bool isCallableInState(const CallableWhenAttr *CWAttr,115                              ConsumedState State) {116  for (const auto &S : CWAttr->callableStates()) {117    ConsumedState MappedAttrState = CS_None;118 119    switch (S) {120    case CallableWhenAttr::Unknown:121      MappedAttrState = CS_Unknown;122      break;123 124    case CallableWhenAttr::Unconsumed:125      MappedAttrState = CS_Unconsumed;126      break;127 128    case CallableWhenAttr::Consumed:129      MappedAttrState = CS_Consumed;130      break;131    }132 133    if (MappedAttrState == State)134      return true;135  }136 137  return false;138}139 140static bool isConsumableType(const QualType &QT) {141  if (QT->isPointerOrReferenceType())142    return false;143 144  if (const CXXRecordDecl *RD = QT->getAsCXXRecordDecl())145    return RD->hasAttr<ConsumableAttr>();146 147  return false;148}149 150static bool isAutoCastType(const QualType &QT) {151  if (QT->isPointerOrReferenceType())152    return false;153 154  if (const CXXRecordDecl *RD = QT->getAsCXXRecordDecl())155    return RD->hasAttr<ConsumableAutoCastAttr>();156 157  return false;158}159 160static bool isSetOnReadPtrType(const QualType &QT) {161  if (const CXXRecordDecl *RD = QT->getPointeeCXXRecordDecl())162    return RD->hasAttr<ConsumableSetOnReadAttr>();163  return false;164}165 166static bool isKnownState(ConsumedState State) {167  switch (State) {168  case CS_Unconsumed:169  case CS_Consumed:170    return true;171  case CS_None:172  case CS_Unknown:173    return false;174  }175  llvm_unreachable("invalid enum");176}177 178static bool isRValueRef(QualType ParamType) {179  return ParamType->isRValueReferenceType();180}181 182static bool isTestingFunction(const FunctionDecl *FunDecl) {183  return FunDecl->hasAttr<TestTypestateAttr>();184}185 186static ConsumedState mapConsumableAttrState(const QualType QT) {187  assert(isConsumableType(QT));188 189  const ConsumableAttr *CAttr =190      QT->getAsCXXRecordDecl()->getAttr<ConsumableAttr>();191 192  switch (CAttr->getDefaultState()) {193  case ConsumableAttr::Unknown:194    return CS_Unknown;195  case ConsumableAttr::Unconsumed:196    return CS_Unconsumed;197  case ConsumableAttr::Consumed:198    return CS_Consumed;199  }200  llvm_unreachable("invalid enum");201}202 203static ConsumedState204mapParamTypestateAttrState(const ParamTypestateAttr *PTAttr) {205  switch (PTAttr->getParamState()) {206  case ParamTypestateAttr::Unknown:207    return CS_Unknown;208  case ParamTypestateAttr::Unconsumed:209    return CS_Unconsumed;210  case ParamTypestateAttr::Consumed:211    return CS_Consumed;212  }213  llvm_unreachable("invalid_enum");214}215 216static ConsumedState217mapReturnTypestateAttrState(const ReturnTypestateAttr *RTSAttr) {218  switch (RTSAttr->getState()) {219  case ReturnTypestateAttr::Unknown:220    return CS_Unknown;221  case ReturnTypestateAttr::Unconsumed:222    return CS_Unconsumed;223  case ReturnTypestateAttr::Consumed:224    return CS_Consumed;225  }226  llvm_unreachable("invalid enum");227}228 229static ConsumedState mapSetTypestateAttrState(const SetTypestateAttr *STAttr) {230  switch (STAttr->getNewState()) {231  case SetTypestateAttr::Unknown:232    return CS_Unknown;233  case SetTypestateAttr::Unconsumed:234    return CS_Unconsumed;235  case SetTypestateAttr::Consumed:236    return CS_Consumed;237  }238  llvm_unreachable("invalid_enum");239}240 241static StringRef stateToString(ConsumedState State) {242  switch (State) {243  case consumed::CS_None:244    return "none";245 246  case consumed::CS_Unknown:247    return "unknown";248 249  case consumed::CS_Unconsumed:250    return "unconsumed";251 252  case consumed::CS_Consumed:253    return "consumed";254  }255  llvm_unreachable("invalid enum");256}257 258static ConsumedState testsFor(const FunctionDecl *FunDecl) {259  assert(isTestingFunction(FunDecl));260  switch (FunDecl->getAttr<TestTypestateAttr>()->getTestState()) {261  case TestTypestateAttr::Unconsumed:262    return CS_Unconsumed;263  case TestTypestateAttr::Consumed:264    return CS_Consumed;265  }266  llvm_unreachable("invalid enum");267}268 269namespace {270 271struct VarTestResult {272  const VarDecl *Var;273  ConsumedState TestsFor;274};275 276} // namespace277 278namespace clang {279namespace consumed {280 281enum EffectiveOp {282  EO_And,283  EO_Or284};285 286class PropagationInfo {287  enum {288    IT_None,289    IT_State,290    IT_VarTest,291    IT_BinTest,292    IT_Var,293    IT_Tmp294  } InfoType = IT_None;295 296  struct BinTestTy {297    const BinaryOperator *Source;298    EffectiveOp EOp;299    VarTestResult LTest;300    VarTestResult RTest;301  };302 303  union {304    ConsumedState State;305    VarTestResult VarTest;306    const VarDecl *Var;307    const CXXBindTemporaryExpr *Tmp;308    BinTestTy BinTest;309  };310 311public:312  PropagationInfo() = default;313  PropagationInfo(const VarTestResult &VarTest)314      : InfoType(IT_VarTest), VarTest(VarTest) {}315 316  PropagationInfo(const VarDecl *Var, ConsumedState TestsFor)317      : InfoType(IT_VarTest) {318    VarTest.Var      = Var;319    VarTest.TestsFor = TestsFor;320  }321 322  PropagationInfo(const BinaryOperator *Source, EffectiveOp EOp,323                  const VarTestResult &LTest, const VarTestResult &RTest)324      : InfoType(IT_BinTest) {325    BinTest.Source  = Source;326    BinTest.EOp     = EOp;327    BinTest.LTest   = LTest;328    BinTest.RTest   = RTest;329  }330 331  PropagationInfo(const BinaryOperator *Source, EffectiveOp EOp,332                  const VarDecl *LVar, ConsumedState LTestsFor,333                  const VarDecl *RVar, ConsumedState RTestsFor)334      : InfoType(IT_BinTest) {335    BinTest.Source         = Source;336    BinTest.EOp            = EOp;337    BinTest.LTest.Var      = LVar;338    BinTest.LTest.TestsFor = LTestsFor;339    BinTest.RTest.Var      = RVar;340    BinTest.RTest.TestsFor = RTestsFor;341  }342 343  PropagationInfo(ConsumedState State)344      : InfoType(IT_State), State(State) {}345  PropagationInfo(const VarDecl *Var) : InfoType(IT_Var), Var(Var) {}346  PropagationInfo(const CXXBindTemporaryExpr *Tmp)347      : InfoType(IT_Tmp), Tmp(Tmp) {}348 349  const ConsumedState &getState() const {350    assert(InfoType == IT_State);351    return State;352  }353 354  const VarTestResult &getVarTest() const {355    assert(InfoType == IT_VarTest);356    return VarTest;357  }358 359  const VarTestResult &getLTest() const {360    assert(InfoType == IT_BinTest);361    return BinTest.LTest;362  }363 364  const VarTestResult &getRTest() const {365    assert(InfoType == IT_BinTest);366    return BinTest.RTest;367  }368 369  const VarDecl *getVar() const {370    assert(InfoType == IT_Var);371    return Var;372  }373 374  const CXXBindTemporaryExpr *getTmp() const {375    assert(InfoType == IT_Tmp);376    return Tmp;377  }378 379  ConsumedState getAsState(const ConsumedStateMap *StateMap) const {380    assert(isVar() || isTmp() || isState());381 382    if (isVar())383      return StateMap->getState(Var);384    else if (isTmp())385      return StateMap->getState(Tmp);386    else if (isState())387      return State;388    else389      return CS_None;390  }391 392  EffectiveOp testEffectiveOp() const {393    assert(InfoType == IT_BinTest);394    return BinTest.EOp;395  }396 397  const BinaryOperator * testSourceNode() const {398    assert(InfoType == IT_BinTest);399    return BinTest.Source;400  }401 402  bool isValid() const { return InfoType != IT_None; }403  bool isState() const { return InfoType == IT_State; }404  bool isVarTest() const { return InfoType == IT_VarTest; }405  bool isBinTest() const { return InfoType == IT_BinTest; }406  bool isVar() const { return InfoType == IT_Var; }407  bool isTmp() const { return InfoType == IT_Tmp; }408 409  bool isTest() const {410    return InfoType == IT_VarTest || InfoType == IT_BinTest;411  }412 413  bool isPointerToValue() const {414    return InfoType == IT_Var || InfoType == IT_Tmp;415  }416 417  PropagationInfo invertTest() const {418    assert(InfoType == IT_VarTest || InfoType == IT_BinTest);419 420    if (InfoType == IT_VarTest) {421      return PropagationInfo(VarTest.Var,422                             invertConsumedUnconsumed(VarTest.TestsFor));423 424    } else if (InfoType == IT_BinTest) {425      return PropagationInfo(BinTest.Source,426        BinTest.EOp == EO_And ? EO_Or : EO_And,427        BinTest.LTest.Var, invertConsumedUnconsumed(BinTest.LTest.TestsFor),428        BinTest.RTest.Var, invertConsumedUnconsumed(BinTest.RTest.TestsFor));429    } else {430      return {};431    }432  }433};434 435} // namespace consumed436} // namespace clang437 438static void439setStateForVarOrTmp(ConsumedStateMap *StateMap, const PropagationInfo &PInfo,440                    ConsumedState State) {441  assert(PInfo.isVar() || PInfo.isTmp());442 443  if (PInfo.isVar())444    StateMap->setState(PInfo.getVar(), State);445  else446    StateMap->setState(PInfo.getTmp(), State);447}448 449namespace clang {450namespace consumed {451 452class ConsumedStmtVisitor : public ConstStmtVisitor<ConsumedStmtVisitor> {453  using MapType = llvm::DenseMap<const Stmt *, PropagationInfo>;454  using PairType= std::pair<const Stmt *, PropagationInfo>;455  using InfoEntry = MapType::iterator;456  using ConstInfoEntry = MapType::const_iterator;457 458  ConsumedAnalyzer &Analyzer;459  ConsumedStateMap *StateMap;460  MapType PropagationMap;461 462  InfoEntry findInfo(const Expr *E) {463    if (const auto Cleanups = dyn_cast<ExprWithCleanups>(E))464      if (!Cleanups->cleanupsHaveSideEffects())465        E = Cleanups->getSubExpr();466    return PropagationMap.find(E->IgnoreParens());467  }468 469  ConstInfoEntry findInfo(const Expr *E) const {470    if (const auto Cleanups = dyn_cast<ExprWithCleanups>(E))471      if (!Cleanups->cleanupsHaveSideEffects())472        E = Cleanups->getSubExpr();473    return PropagationMap.find(E->IgnoreParens());474  }475 476  void insertInfo(const Expr *E, const PropagationInfo &PI) {477    PropagationMap.insert(PairType(E->IgnoreParens(), PI));478  }479 480  void forwardInfo(const Expr *From, const Expr *To);481  void copyInfo(const Expr *From, const Expr *To, ConsumedState CS);482  ConsumedState getInfo(const Expr *From);483  void setInfo(const Expr *To, ConsumedState NS);484  void propagateReturnType(const Expr *Call, const FunctionDecl *Fun);485 486public:487  void checkCallability(const PropagationInfo &PInfo,488                        const FunctionDecl *FunDecl,489                        SourceLocation BlameLoc);490  bool handleCall(const CallExpr *Call, const Expr *ObjArg,491                  const FunctionDecl *FunD);492 493  void VisitBinaryOperator(const BinaryOperator *BinOp);494  void VisitCallExpr(const CallExpr *Call);495  void VisitCastExpr(const CastExpr *Cast);496  void VisitCXXBindTemporaryExpr(const CXXBindTemporaryExpr *Temp);497  void VisitCXXConstructExpr(const CXXConstructExpr *Call);498  void VisitCXXMemberCallExpr(const CXXMemberCallExpr *Call);499  void VisitCXXOperatorCallExpr(const CXXOperatorCallExpr *Call);500  void VisitDeclRefExpr(const DeclRefExpr *DeclRef);501  void VisitDeclStmt(const DeclStmt *DelcS);502  void VisitMaterializeTemporaryExpr(const MaterializeTemporaryExpr *Temp);503  void VisitMemberExpr(const MemberExpr *MExpr);504  void VisitParmVarDecl(const ParmVarDecl *Param);505  void VisitReturnStmt(const ReturnStmt *Ret);506  void VisitUnaryOperator(const UnaryOperator *UOp);507  void VisitVarDecl(const VarDecl *Var);508 509  ConsumedStmtVisitor(ConsumedAnalyzer &Analyzer, ConsumedStateMap *StateMap)510      : Analyzer(Analyzer), StateMap(StateMap) {}511 512  PropagationInfo getInfo(const Expr *StmtNode) const {513    ConstInfoEntry Entry = findInfo(StmtNode);514 515    if (Entry != PropagationMap.end())516      return Entry->second;517    else518      return {};519  }520 521  void reset(ConsumedStateMap *NewStateMap) {522    StateMap = NewStateMap;523  }524};525 526} // namespace consumed527} // namespace clang528 529void ConsumedStmtVisitor::forwardInfo(const Expr *From, const Expr *To) {530  InfoEntry Entry = findInfo(From);531  if (Entry != PropagationMap.end())532    insertInfo(To, Entry->second);533}534 535// Create a new state for To, which is initialized to the state of From.536// If NS is not CS_None, sets the state of From to NS.537void ConsumedStmtVisitor::copyInfo(const Expr *From, const Expr *To,538                                   ConsumedState NS) {539  InfoEntry Entry = findInfo(From);540  if (Entry != PropagationMap.end()) {541    PropagationInfo& PInfo = Entry->second;542    ConsumedState CS = PInfo.getAsState(StateMap);543    if (CS != CS_None)544      insertInfo(To, PropagationInfo(CS));545    if (NS != CS_None && PInfo.isPointerToValue())546      setStateForVarOrTmp(StateMap, PInfo, NS);547  }548}549 550// Get the ConsumedState for From551ConsumedState ConsumedStmtVisitor::getInfo(const Expr *From) {552  InfoEntry Entry = findInfo(From);553  if (Entry != PropagationMap.end()) {554    PropagationInfo& PInfo = Entry->second;555    return PInfo.getAsState(StateMap);556  }557  return CS_None;558}559 560// If we already have info for To then update it, otherwise create a new entry.561void ConsumedStmtVisitor::setInfo(const Expr *To, ConsumedState NS) {562  InfoEntry Entry = findInfo(To);563  if (Entry != PropagationMap.end()) {564    PropagationInfo& PInfo = Entry->second;565    if (PInfo.isPointerToValue())566      setStateForVarOrTmp(StateMap, PInfo, NS);567  } else if (NS != CS_None) {568     insertInfo(To, PropagationInfo(NS));569  }570}571 572void ConsumedStmtVisitor::checkCallability(const PropagationInfo &PInfo,573                                           const FunctionDecl *FunDecl,574                                           SourceLocation BlameLoc) {575  assert(!PInfo.isTest());576 577  const CallableWhenAttr *CWAttr = FunDecl->getAttr<CallableWhenAttr>();578  if (!CWAttr)579    return;580 581  if (PInfo.isVar()) {582    ConsumedState VarState = StateMap->getState(PInfo.getVar());583 584    if (VarState == CS_None || isCallableInState(CWAttr, VarState))585      return;586 587    Analyzer.WarningsHandler.warnUseInInvalidState(588      FunDecl->getNameAsString(), PInfo.getVar()->getNameAsString(),589      stateToString(VarState), BlameLoc);590  } else {591    ConsumedState TmpState = PInfo.getAsState(StateMap);592 593    if (TmpState == CS_None || isCallableInState(CWAttr, TmpState))594      return;595 596    Analyzer.WarningsHandler.warnUseOfTempInInvalidState(597      FunDecl->getNameAsString(), stateToString(TmpState), BlameLoc);598  }599}600 601// Factors out common behavior for function, method, and operator calls.602// Check parameters and set parameter state if necessary.603// Returns true if the state of ObjArg is set, or false otherwise.604bool ConsumedStmtVisitor::handleCall(const CallExpr *Call, const Expr *ObjArg,605                                     const FunctionDecl *FunD) {606  unsigned Offset = 0;607  if (isa<CXXOperatorCallExpr>(Call) && isa<CXXMethodDecl>(FunD))608    Offset = 1;  // first argument is 'this'609 610  // check explicit parameters611  for (unsigned Index = Offset; Index < Call->getNumArgs(); ++Index) {612    // Skip variable argument lists.613    if (Index - Offset >= FunD->getNumParams())614      break;615 616    const ParmVarDecl *Param = FunD->getParamDecl(Index - Offset);617    QualType ParamType = Param->getType();618 619    InfoEntry Entry = findInfo(Call->getArg(Index));620 621    if (Entry == PropagationMap.end() || Entry->second.isTest())622      continue;623    PropagationInfo PInfo = Entry->second;624 625    // Check that the parameter is in the correct state.626    if (ParamTypestateAttr *PTA = Param->getAttr<ParamTypestateAttr>()) {627      ConsumedState ParamState = PInfo.getAsState(StateMap);628      ConsumedState ExpectedState = mapParamTypestateAttrState(PTA);629 630      if (ParamState != ExpectedState)631        Analyzer.WarningsHandler.warnParamTypestateMismatch(632          Call->getArg(Index)->getExprLoc(),633          stateToString(ExpectedState), stateToString(ParamState));634    }635 636    if (!(Entry->second.isVar() || Entry->second.isTmp()))637      continue;638 639    // Adjust state on the caller side.640    if (ReturnTypestateAttr *RT = Param->getAttr<ReturnTypestateAttr>())641      setStateForVarOrTmp(StateMap, PInfo, mapReturnTypestateAttrState(RT));642    else if (isRValueRef(ParamType) || isConsumableType(ParamType))643      setStateForVarOrTmp(StateMap, PInfo, consumed::CS_Consumed);644    else if (ParamType->isPointerOrReferenceType() &&645             (!ParamType->getPointeeType().isConstQualified() ||646              isSetOnReadPtrType(ParamType)))647      setStateForVarOrTmp(StateMap, PInfo, consumed::CS_Unknown);648  }649 650  if (!ObjArg)651    return false;652 653  // check implicit 'self' parameter, if present654  InfoEntry Entry = findInfo(ObjArg);655  if (Entry != PropagationMap.end()) {656    PropagationInfo PInfo = Entry->second;657    checkCallability(PInfo, FunD, Call->getExprLoc());658 659    if (SetTypestateAttr *STA = FunD->getAttr<SetTypestateAttr>()) {660      if (PInfo.isVar()) {661        StateMap->setState(PInfo.getVar(), mapSetTypestateAttrState(STA));662        return true;663      }664      else if (PInfo.isTmp()) {665        StateMap->setState(PInfo.getTmp(), mapSetTypestateAttrState(STA));666        return true;667      }668    }669    else if (isTestingFunction(FunD) && PInfo.isVar()) {670      PropagationMap.insert(PairType(Call,671        PropagationInfo(PInfo.getVar(), testsFor(FunD))));672    }673  }674  return false;675}676 677void ConsumedStmtVisitor::propagateReturnType(const Expr *Call,678                                              const FunctionDecl *Fun) {679  QualType RetType = Fun->getCallResultType();680  if (RetType->isReferenceType())681    RetType = RetType->getPointeeType();682 683  if (isConsumableType(RetType)) {684    ConsumedState ReturnState;685    if (ReturnTypestateAttr *RTA = Fun->getAttr<ReturnTypestateAttr>())686      ReturnState = mapReturnTypestateAttrState(RTA);687    else688      ReturnState = mapConsumableAttrState(RetType);689 690    PropagationMap.insert(PairType(Call, PropagationInfo(ReturnState)));691  }692}693 694void ConsumedStmtVisitor::VisitBinaryOperator(const BinaryOperator *BinOp) {695  switch (BinOp->getOpcode()) {696  case BO_LAnd:697  case BO_LOr : {698    InfoEntry LEntry = findInfo(BinOp->getLHS()),699              REntry = findInfo(BinOp->getRHS());700 701    VarTestResult LTest, RTest;702 703    if (LEntry != PropagationMap.end() && LEntry->second.isVarTest()) {704      LTest = LEntry->second.getVarTest();705    } else {706      LTest.Var      = nullptr;707      LTest.TestsFor = CS_None;708    }709 710    if (REntry != PropagationMap.end() && REntry->second.isVarTest()) {711      RTest = REntry->second.getVarTest();712    } else {713      RTest.Var      = nullptr;714      RTest.TestsFor = CS_None;715    }716 717    if (!(LTest.Var == nullptr && RTest.Var == nullptr))718      PropagationMap.insert(PairType(BinOp, PropagationInfo(BinOp,719        static_cast<EffectiveOp>(BinOp->getOpcode() == BO_LOr), LTest, RTest)));720    break;721  }722 723  case BO_PtrMemD:724  case BO_PtrMemI:725    forwardInfo(BinOp->getLHS(), BinOp);726    break;727 728  default:729    break;730  }731}732 733void ConsumedStmtVisitor::VisitCallExpr(const CallExpr *Call) {734  const FunctionDecl *FunDecl = Call->getDirectCallee();735  if (!FunDecl)736    return;737 738  // Special case for the std::move function.739  // TODO: Make this more specific. (Deferred)740  if (Call->isCallToStdMove()) {741    copyInfo(Call->getArg(0), Call, CS_Consumed);742    return;743  }744 745  handleCall(Call, nullptr, FunDecl);746  propagateReturnType(Call, FunDecl);747}748 749void ConsumedStmtVisitor::VisitCastExpr(const CastExpr *Cast) {750  forwardInfo(Cast->getSubExpr(), Cast);751}752 753void ConsumedStmtVisitor::VisitCXXBindTemporaryExpr(754  const CXXBindTemporaryExpr *Temp) {755 756  InfoEntry Entry = findInfo(Temp->getSubExpr());757 758  if (Entry != PropagationMap.end() && !Entry->second.isTest()) {759    StateMap->setState(Temp, Entry->second.getAsState(StateMap));760    PropagationMap.insert(PairType(Temp, PropagationInfo(Temp)));761  }762}763 764void ConsumedStmtVisitor::VisitCXXConstructExpr(const CXXConstructExpr *Call) {765  CXXConstructorDecl *Constructor = Call->getConstructor();766 767  QualType ThisType = Constructor->getFunctionObjectParameterType();768 769  if (!isConsumableType(ThisType))770    return;771 772  // FIXME: What should happen if someone annotates the move constructor?773  if (ReturnTypestateAttr *RTA = Constructor->getAttr<ReturnTypestateAttr>()) {774    // TODO: Adjust state of args appropriately.775    ConsumedState RetState = mapReturnTypestateAttrState(RTA);776    PropagationMap.insert(PairType(Call, PropagationInfo(RetState)));777  } else if (Constructor->isDefaultConstructor()) {778    PropagationMap.insert(PairType(Call,779      PropagationInfo(consumed::CS_Consumed)));780  } else if (Constructor->isMoveConstructor()) {781    copyInfo(Call->getArg(0), Call, CS_Consumed);782  } else if (Constructor->isCopyConstructor()) {783    // Copy state from arg.  If setStateOnRead then set arg to CS_Unknown.784    ConsumedState NS =785      isSetOnReadPtrType(Constructor->getThisType()) ?786      CS_Unknown : CS_None;787    copyInfo(Call->getArg(0), Call, NS);788  } else {789    // TODO: Adjust state of args appropriately.790    ConsumedState RetState = mapConsumableAttrState(ThisType);791    PropagationMap.insert(PairType(Call, PropagationInfo(RetState)));792  }793}794 795void ConsumedStmtVisitor::VisitCXXMemberCallExpr(796    const CXXMemberCallExpr *Call) {797  CXXMethodDecl* MD = Call->getMethodDecl();798  if (!MD)799    return;800 801  handleCall(Call, Call->getImplicitObjectArgument(), MD);802  propagateReturnType(Call, MD);803}804 805void ConsumedStmtVisitor::VisitCXXOperatorCallExpr(806    const CXXOperatorCallExpr *Call) {807  const auto *FunDecl = dyn_cast_or_null<FunctionDecl>(Call->getDirectCallee());808  if (!FunDecl) return;809 810  if (Call->getOperator() == OO_Equal) {811    ConsumedState CS = getInfo(Call->getArg(1));812    if (!handleCall(Call, Call->getArg(0), FunDecl))813      setInfo(Call->getArg(0), CS);814    return;815  }816 817  if (const auto *MCall = dyn_cast<CXXMemberCallExpr>(Call))818    handleCall(MCall, MCall->getImplicitObjectArgument(), FunDecl);819  else820    handleCall(Call, Call->getArg(0), FunDecl);821 822  propagateReturnType(Call, FunDecl);823}824 825void ConsumedStmtVisitor::VisitDeclRefExpr(const DeclRefExpr *DeclRef) {826  if (const auto *Var = dyn_cast_or_null<VarDecl>(DeclRef->getDecl()))827    if (StateMap->getState(Var) != consumed::CS_None)828      PropagationMap.insert(PairType(DeclRef, PropagationInfo(Var)));829}830 831void ConsumedStmtVisitor::VisitDeclStmt(const DeclStmt *DeclS) {832  for (const auto *DI : DeclS->decls())833    if (isa<VarDecl>(DI))834      VisitVarDecl(cast<VarDecl>(DI));835 836  if (DeclS->isSingleDecl())837    if (const auto *Var = dyn_cast_or_null<VarDecl>(DeclS->getSingleDecl()))838      PropagationMap.insert(PairType(DeclS, PropagationInfo(Var)));839}840 841void ConsumedStmtVisitor::VisitMaterializeTemporaryExpr(842  const MaterializeTemporaryExpr *Temp) {843  forwardInfo(Temp->getSubExpr(), Temp);844}845 846void ConsumedStmtVisitor::VisitMemberExpr(const MemberExpr *MExpr) {847  forwardInfo(MExpr->getBase(), MExpr);848}849 850void ConsumedStmtVisitor::VisitParmVarDecl(const ParmVarDecl *Param) {851  QualType ParamType = Param->getType();852  ConsumedState ParamState = consumed::CS_None;853 854  if (const ParamTypestateAttr *PTA = Param->getAttr<ParamTypestateAttr>())855    ParamState = mapParamTypestateAttrState(PTA);856  else if (isConsumableType(ParamType))857    ParamState = mapConsumableAttrState(ParamType);858  else if (isRValueRef(ParamType) &&859           isConsumableType(ParamType->getPointeeType()))860    ParamState = mapConsumableAttrState(ParamType->getPointeeType());861  else if (ParamType->isReferenceType() &&862           isConsumableType(ParamType->getPointeeType()))863    ParamState = consumed::CS_Unknown;864 865  if (ParamState != CS_None)866    StateMap->setState(Param, ParamState);867}868 869void ConsumedStmtVisitor::VisitReturnStmt(const ReturnStmt *Ret) {870  ConsumedState ExpectedState = Analyzer.getExpectedReturnState();871 872  if (ExpectedState != CS_None) {873    InfoEntry Entry = findInfo(Ret->getRetValue());874 875    if (Entry != PropagationMap.end()) {876      ConsumedState RetState = Entry->second.getAsState(StateMap);877 878      if (RetState != ExpectedState)879        Analyzer.WarningsHandler.warnReturnTypestateMismatch(880          Ret->getReturnLoc(), stateToString(ExpectedState),881          stateToString(RetState));882    }883  }884 885  StateMap->checkParamsForReturnTypestate(Ret->getBeginLoc(),886                                          Analyzer.WarningsHandler);887}888 889void ConsumedStmtVisitor::VisitUnaryOperator(const UnaryOperator *UOp) {890  InfoEntry Entry = findInfo(UOp->getSubExpr());891  if (Entry == PropagationMap.end()) return;892 893  switch (UOp->getOpcode()) {894  case UO_AddrOf:895    PropagationMap.insert(PairType(UOp, Entry->second));896    break;897 898  case UO_LNot:899    if (Entry->second.isTest())900      PropagationMap.insert(PairType(UOp, Entry->second.invertTest()));901    break;902 903  default:904    break;905  }906}907 908// TODO: See if I need to check for reference types here.909void ConsumedStmtVisitor::VisitVarDecl(const VarDecl *Var) {910  if (isConsumableType(Var->getType())) {911    if (Var->hasInit()) {912      MapType::iterator VIT = findInfo(Var->getInit()->IgnoreImplicit());913      if (VIT != PropagationMap.end()) {914        PropagationInfo PInfo = VIT->second;915        ConsumedState St = PInfo.getAsState(StateMap);916 917        if (St != consumed::CS_None) {918          StateMap->setState(Var, St);919          return;920        }921      }922    }923    // Otherwise924    StateMap->setState(Var, consumed::CS_Unknown);925  }926}927 928static void splitVarStateForIf(const IfStmt *IfNode, const VarTestResult &Test,929                               ConsumedStateMap *ThenStates,930                               ConsumedStateMap *ElseStates) {931  ConsumedState VarState = ThenStates->getState(Test.Var);932 933  if (VarState == CS_Unknown) {934    ThenStates->setState(Test.Var, Test.TestsFor);935    ElseStates->setState(Test.Var, invertConsumedUnconsumed(Test.TestsFor));936  } else if (VarState == invertConsumedUnconsumed(Test.TestsFor)) {937    ThenStates->markUnreachable();938  } else if (VarState == Test.TestsFor) {939    ElseStates->markUnreachable();940  }941}942 943static void splitVarStateForIfBinOp(const PropagationInfo &PInfo,944                                    ConsumedStateMap *ThenStates,945                                    ConsumedStateMap *ElseStates) {946  const VarTestResult &LTest = PInfo.getLTest(),947                      &RTest = PInfo.getRTest();948 949  ConsumedState LState = LTest.Var ? ThenStates->getState(LTest.Var) : CS_None,950                RState = RTest.Var ? ThenStates->getState(RTest.Var) : CS_None;951 952  if (LTest.Var) {953    if (PInfo.testEffectiveOp() == EO_And) {954      if (LState == CS_Unknown) {955        ThenStates->setState(LTest.Var, LTest.TestsFor);956      } else if (LState == invertConsumedUnconsumed(LTest.TestsFor)) {957        ThenStates->markUnreachable();958      } else if (LState == LTest.TestsFor && isKnownState(RState)) {959        if (RState == RTest.TestsFor)960          ElseStates->markUnreachable();961        else962          ThenStates->markUnreachable();963      }964    } else {965      if (LState == CS_Unknown) {966        ElseStates->setState(LTest.Var,967                             invertConsumedUnconsumed(LTest.TestsFor));968      } else if (LState == LTest.TestsFor) {969        ElseStates->markUnreachable();970      } else if (LState == invertConsumedUnconsumed(LTest.TestsFor) &&971                 isKnownState(RState)) {972        if (RState == RTest.TestsFor)973          ElseStates->markUnreachable();974        else975          ThenStates->markUnreachable();976      }977    }978  }979 980  if (RTest.Var) {981    if (PInfo.testEffectiveOp() == EO_And) {982      if (RState == CS_Unknown)983        ThenStates->setState(RTest.Var, RTest.TestsFor);984      else if (RState == invertConsumedUnconsumed(RTest.TestsFor))985        ThenStates->markUnreachable();986    } else {987      if (RState == CS_Unknown)988        ElseStates->setState(RTest.Var,989                             invertConsumedUnconsumed(RTest.TestsFor));990      else if (RState == RTest.TestsFor)991        ElseStates->markUnreachable();992    }993  }994}995 996bool ConsumedBlockInfo::allBackEdgesVisited(const CFGBlock *CurrBlock,997                                            const CFGBlock *TargetBlock) {998  assert(CurrBlock && "Block pointer must not be NULL");999  assert(TargetBlock && "TargetBlock pointer must not be NULL");1000 1001  unsigned int CurrBlockOrder = VisitOrder[CurrBlock->getBlockID()];1002  for (CFGBlock::const_pred_iterator PI = TargetBlock->pred_begin(),1003       PE = TargetBlock->pred_end(); PI != PE; ++PI) {1004    if (*PI && CurrBlockOrder < VisitOrder[(*PI)->getBlockID()] )1005      return false;1006  }1007  return true;1008}1009 1010void ConsumedBlockInfo::addInfo(1011    const CFGBlock *Block, ConsumedStateMap *StateMap,1012    std::unique_ptr<ConsumedStateMap> &OwnedStateMap) {1013  assert(Block && "Block pointer must not be NULL");1014 1015  auto &Entry = StateMapsArray[Block->getBlockID()];1016 1017  if (Entry) {1018    Entry->intersect(*StateMap);1019  } else if (OwnedStateMap)1020    Entry = std::move(OwnedStateMap);1021  else1022    Entry = std::make_unique<ConsumedStateMap>(*StateMap);1023}1024 1025void ConsumedBlockInfo::addInfo(const CFGBlock *Block,1026                                std::unique_ptr<ConsumedStateMap> StateMap) {1027  assert(Block && "Block pointer must not be NULL");1028 1029  auto &Entry = StateMapsArray[Block->getBlockID()];1030 1031  if (Entry) {1032    Entry->intersect(*StateMap);1033  } else {1034    Entry = std::move(StateMap);1035  }1036}1037 1038ConsumedStateMap* ConsumedBlockInfo::borrowInfo(const CFGBlock *Block) {1039  assert(Block && "Block pointer must not be NULL");1040  assert(StateMapsArray[Block->getBlockID()] && "Block has no block info");1041 1042  return StateMapsArray[Block->getBlockID()].get();1043}1044 1045void ConsumedBlockInfo::discardInfo(const CFGBlock *Block) {1046  StateMapsArray[Block->getBlockID()] = nullptr;1047}1048 1049std::unique_ptr<ConsumedStateMap>1050ConsumedBlockInfo::getInfo(const CFGBlock *Block) {1051  assert(Block && "Block pointer must not be NULL");1052 1053  auto &Entry = StateMapsArray[Block->getBlockID()];1054  return isBackEdgeTarget(Block) ? std::make_unique<ConsumedStateMap>(*Entry)1055                                 : std::move(Entry);1056}1057 1058bool ConsumedBlockInfo::isBackEdge(const CFGBlock *From, const CFGBlock *To) {1059  assert(From && "From block must not be NULL");1060  assert(To   && "From block must not be NULL");1061 1062  return VisitOrder[From->getBlockID()] > VisitOrder[To->getBlockID()];1063}1064 1065bool ConsumedBlockInfo::isBackEdgeTarget(const CFGBlock *Block) {1066  assert(Block && "Block pointer must not be NULL");1067 1068  // Anything with less than two predecessors can't be the target of a back1069  // edge.1070  if (Block->pred_size() < 2)1071    return false;1072 1073  unsigned int BlockVisitOrder = VisitOrder[Block->getBlockID()];1074  for (CFGBlock::const_pred_iterator PI = Block->pred_begin(),1075       PE = Block->pred_end(); PI != PE; ++PI) {1076    if (*PI && BlockVisitOrder < VisitOrder[(*PI)->getBlockID()])1077      return true;1078  }1079  return false;1080}1081 1082void ConsumedStateMap::checkParamsForReturnTypestate(SourceLocation BlameLoc,1083  ConsumedWarningsHandlerBase &WarningsHandler) const {1084 1085  for (const auto &DM : VarMap) {1086    if (isa<ParmVarDecl>(DM.first)) {1087      const auto *Param = cast<ParmVarDecl>(DM.first);1088      const ReturnTypestateAttr *RTA = Param->getAttr<ReturnTypestateAttr>();1089 1090      if (!RTA)1091        continue;1092 1093      ConsumedState ExpectedState = mapReturnTypestateAttrState(RTA);1094      if (DM.second != ExpectedState)1095        WarningsHandler.warnParamReturnTypestateMismatch(BlameLoc,1096          Param->getNameAsString(), stateToString(ExpectedState),1097          stateToString(DM.second));1098    }1099  }1100}1101 1102void ConsumedStateMap::clearTemporaries() {1103  TmpMap.clear();1104}1105 1106ConsumedState ConsumedStateMap::getState(const VarDecl *Var) const {1107  VarMapType::const_iterator Entry = VarMap.find(Var);1108 1109  if (Entry != VarMap.end())1110    return Entry->second;1111 1112  return CS_None;1113}1114 1115ConsumedState1116ConsumedStateMap::getState(const CXXBindTemporaryExpr *Tmp) const {1117  TmpMapType::const_iterator Entry = TmpMap.find(Tmp);1118 1119  if (Entry != TmpMap.end())1120    return Entry->second;1121 1122  return CS_None;1123}1124 1125void ConsumedStateMap::intersect(const ConsumedStateMap &Other) {1126  ConsumedState LocalState;1127 1128  if (this->From && this->From == Other.From && !Other.Reachable) {1129    this->markUnreachable();1130    return;1131  }1132 1133  for (const auto &DM : Other.VarMap) {1134    LocalState = this->getState(DM.first);1135 1136    if (LocalState == CS_None)1137      continue;1138 1139    if (LocalState != DM.second)1140     VarMap[DM.first] = CS_Unknown;1141  }1142}1143 1144void ConsumedStateMap::intersectAtLoopHead(const CFGBlock *LoopHead,1145  const CFGBlock *LoopBack, const ConsumedStateMap *LoopBackStates,1146  ConsumedWarningsHandlerBase &WarningsHandler) {1147 1148  ConsumedState LocalState;1149  SourceLocation BlameLoc = getLastStmtLoc(LoopBack);1150 1151  for (const auto &DM : LoopBackStates->VarMap) {1152    LocalState = this->getState(DM.first);1153 1154    if (LocalState == CS_None)1155      continue;1156 1157    if (LocalState != DM.second) {1158      VarMap[DM.first] = CS_Unknown;1159      WarningsHandler.warnLoopStateMismatch(BlameLoc,1160                                            DM.first->getNameAsString());1161    }1162  }1163}1164 1165void ConsumedStateMap::markUnreachable() {1166  this->Reachable = false;1167  VarMap.clear();1168  TmpMap.clear();1169}1170 1171void ConsumedStateMap::setState(const VarDecl *Var, ConsumedState State) {1172  VarMap[Var] = State;1173}1174 1175void ConsumedStateMap::setState(const CXXBindTemporaryExpr *Tmp,1176                                ConsumedState State) {1177  TmpMap[Tmp] = State;1178}1179 1180void ConsumedStateMap::remove(const CXXBindTemporaryExpr *Tmp) {1181  TmpMap.erase(Tmp);1182}1183 1184bool ConsumedStateMap::operator!=(const ConsumedStateMap *Other) const {1185  for (const auto &DM : Other->VarMap)1186    if (this->getState(DM.first) != DM.second)1187      return true;1188  return false;1189}1190 1191void ConsumedAnalyzer::determineExpectedReturnState(AnalysisDeclContext &AC,1192                                                    const FunctionDecl *D) {1193  QualType ReturnType;1194  if (const auto *Constructor = dyn_cast<CXXConstructorDecl>(D)) {1195    ReturnType = Constructor->getFunctionObjectParameterType();1196  } else1197    ReturnType = D->getCallResultType();1198 1199  if (const ReturnTypestateAttr *RTSAttr = D->getAttr<ReturnTypestateAttr>()) {1200    const CXXRecordDecl *RD = ReturnType->getAsCXXRecordDecl();1201    if (!RD || !RD->hasAttr<ConsumableAttr>()) {1202      // FIXME: This should be removed when template instantiation propagates1203      //        attributes at template specialization definition, not1204      //        declaration. When it is removed the test needs to be enabled1205      //        in SemaDeclAttr.cpp.1206      WarningsHandler.warnReturnTypestateForUnconsumableType(1207          RTSAttr->getLocation(), ReturnType.getAsString());1208      ExpectedReturnState = CS_None;1209    } else1210      ExpectedReturnState = mapReturnTypestateAttrState(RTSAttr);1211  } else if (isConsumableType(ReturnType)) {1212    if (isAutoCastType(ReturnType))   // We can auto-cast the state to the1213      ExpectedReturnState = CS_None;  // expected state.1214    else1215      ExpectedReturnState = mapConsumableAttrState(ReturnType);1216  }1217  else1218    ExpectedReturnState = CS_None;1219}1220 1221bool ConsumedAnalyzer::splitState(const CFGBlock *CurrBlock,1222                                  const ConsumedStmtVisitor &Visitor) {1223  std::unique_ptr<ConsumedStateMap> FalseStates(1224      new ConsumedStateMap(*CurrStates));1225  PropagationInfo PInfo;1226 1227  if (const auto *IfNode =1228          dyn_cast_or_null<IfStmt>(CurrBlock->getTerminator().getStmt())) {1229    if (IfNode->isConsteval())1230      return false;1231 1232    const Expr *Cond = IfNode->getCond();1233 1234    PInfo = Visitor.getInfo(Cond);1235    if (!PInfo.isValid() && isa<BinaryOperator>(Cond))1236      PInfo = Visitor.getInfo(cast<BinaryOperator>(Cond)->getRHS());1237 1238    if (PInfo.isVarTest()) {1239      CurrStates->setSource(Cond);1240      FalseStates->setSource(Cond);1241      splitVarStateForIf(IfNode, PInfo.getVarTest(), CurrStates.get(),1242                         FalseStates.get());1243    } else if (PInfo.isBinTest()) {1244      CurrStates->setSource(PInfo.testSourceNode());1245      FalseStates->setSource(PInfo.testSourceNode());1246      splitVarStateForIfBinOp(PInfo, CurrStates.get(), FalseStates.get());1247    } else {1248      return false;1249    }1250  } else if (const auto *BinOp =1251       dyn_cast_or_null<BinaryOperator>(CurrBlock->getTerminator().getStmt())) {1252    PInfo = Visitor.getInfo(BinOp->getLHS());1253    if (!PInfo.isVarTest()) {1254      if ((BinOp = dyn_cast_or_null<BinaryOperator>(BinOp->getLHS()))) {1255        PInfo = Visitor.getInfo(BinOp->getRHS());1256 1257        if (!PInfo.isVarTest())1258          return false;1259      } else {1260        return false;1261      }1262    }1263 1264    CurrStates->setSource(BinOp);1265    FalseStates->setSource(BinOp);1266 1267    const VarTestResult &Test = PInfo.getVarTest();1268    ConsumedState VarState = CurrStates->getState(Test.Var);1269 1270    if (BinOp->getOpcode() == BO_LAnd) {1271      if (VarState == CS_Unknown)1272        CurrStates->setState(Test.Var, Test.TestsFor);1273      else if (VarState == invertConsumedUnconsumed(Test.TestsFor))1274        CurrStates->markUnreachable();1275 1276    } else if (BinOp->getOpcode() == BO_LOr) {1277      if (VarState == CS_Unknown)1278        FalseStates->setState(Test.Var,1279                              invertConsumedUnconsumed(Test.TestsFor));1280      else if (VarState == Test.TestsFor)1281        FalseStates->markUnreachable();1282    }1283  } else {1284    return false;1285  }1286 1287  CFGBlock::const_succ_iterator SI = CurrBlock->succ_begin();1288 1289  if (*SI)1290    BlockInfo.addInfo(*SI, std::move(CurrStates));1291  else1292    CurrStates = nullptr;1293 1294  if (*++SI)1295    BlockInfo.addInfo(*SI, std::move(FalseStates));1296 1297  return true;1298}1299 1300void ConsumedAnalyzer::run(AnalysisDeclContext &AC) {1301  const auto *D = dyn_cast_or_null<FunctionDecl>(AC.getDecl());1302  if (!D)1303    return;1304 1305  CFG *CFGraph = AC.getCFG();1306  if (!CFGraph)1307    return;1308 1309  determineExpectedReturnState(AC, D);1310 1311  PostOrderCFGView *SortedGraph = AC.getAnalysis<PostOrderCFGView>();1312  // AC.getCFG()->viewCFG(LangOptions());1313 1314  BlockInfo = ConsumedBlockInfo(CFGraph->getNumBlockIDs(), SortedGraph);1315 1316  CurrStates = std::make_unique<ConsumedStateMap>();1317  ConsumedStmtVisitor Visitor(*this, CurrStates.get());1318 1319  // Add all trackable parameters to the state map.1320  for (const auto *PI : D->parameters())1321    Visitor.VisitParmVarDecl(PI);1322 1323  // Visit all of the function's basic blocks.1324  for (const auto *CurrBlock : *SortedGraph) {1325    if (!CurrStates)1326      CurrStates = BlockInfo.getInfo(CurrBlock);1327 1328    if (!CurrStates) {1329      continue;1330    } else if (!CurrStates->isReachable()) {1331      CurrStates = nullptr;1332      continue;1333    }1334 1335    Visitor.reset(CurrStates.get());1336 1337    // Visit all of the basic block's statements.1338    for (const auto &B : *CurrBlock) {1339      switch (B.getKind()) {1340      case CFGElement::Statement:1341        Visitor.Visit(B.castAs<CFGStmt>().getStmt());1342        break;1343 1344      case CFGElement::TemporaryDtor: {1345        const CFGTemporaryDtor &DTor = B.castAs<CFGTemporaryDtor>();1346        const CXXBindTemporaryExpr *BTE = DTor.getBindTemporaryExpr();1347 1348        Visitor.checkCallability(PropagationInfo(BTE),1349                                 DTor.getDestructorDecl(AC.getASTContext()),1350                                 BTE->getExprLoc());1351        CurrStates->remove(BTE);1352        break;1353      }1354 1355      case CFGElement::AutomaticObjectDtor: {1356        const CFGAutomaticObjDtor &DTor = B.castAs<CFGAutomaticObjDtor>();1357        const auto *DD = DTor.getDestructorDecl(AC.getASTContext());1358        if (!DD)1359          break;1360 1361        SourceLocation Loc = DTor.getTriggerStmt()->getEndLoc();1362        const VarDecl *Var = DTor.getVarDecl();1363        Visitor.checkCallability(PropagationInfo(Var), DD, Loc);1364        break;1365      }1366 1367      default:1368        break;1369      }1370    }1371 1372    // TODO: Handle other forms of branching with precision, including while-1373    //       and for-loops. (Deferred)1374    if (!splitState(CurrBlock, Visitor)) {1375      CurrStates->setSource(nullptr);1376 1377      if (CurrBlock->succ_size() > 1 ||1378          (CurrBlock->succ_size() == 1 &&1379           (*CurrBlock->succ_begin())->pred_size() > 1)) {1380 1381        auto *RawState = CurrStates.get();1382 1383        for (CFGBlock::const_succ_iterator SI = CurrBlock->succ_begin(),1384             SE = CurrBlock->succ_end(); SI != SE; ++SI) {1385          if (*SI == nullptr) continue;1386 1387          if (BlockInfo.isBackEdge(CurrBlock, *SI)) {1388            BlockInfo.borrowInfo(*SI)->intersectAtLoopHead(1389                *SI, CurrBlock, RawState, WarningsHandler);1390 1391            if (BlockInfo.allBackEdgesVisited(CurrBlock, *SI))1392              BlockInfo.discardInfo(*SI);1393          } else {1394            BlockInfo.addInfo(*SI, RawState, CurrStates);1395          }1396        }1397 1398        CurrStates = nullptr;1399      }1400    }1401 1402    if (CurrBlock == &AC.getCFG()->getExit() &&1403        D->getCallResultType()->isVoidType())1404      CurrStates->checkParamsForReturnTypestate(D->getLocation(),1405                                                WarningsHandler);1406  } // End of block iterator.1407 1408  // Delete the last existing state map.1409  CurrStates = nullptr;1410 1411  WarningsHandler.emitDiagnostics();1412}1413