|  | //===- Consumed.cpp --------------------------------------------*- C++ --*-===// | 
|  | // | 
|  | //                     The LLVM Compiler Infrastructure | 
|  | // | 
|  | // This file is distributed under the University of Illinois Open Source | 
|  | // License. See LICENSE.TXT for details. | 
|  | // | 
|  | //===----------------------------------------------------------------------===// | 
|  | // | 
|  | // A intra-procedural analysis for checking consumed properties.  This is based, | 
|  | // in part, on research on linear types. | 
|  | // | 
|  | //===----------------------------------------------------------------------===// | 
|  |  | 
|  | #include "clang/Analysis/Analyses/Consumed.h" | 
|  | #include "clang/AST/ASTContext.h" | 
|  | #include "clang/AST/Attr.h" | 
|  | #include "clang/AST/DeclCXX.h" | 
|  | #include "clang/AST/ExprCXX.h" | 
|  | #include "clang/AST/RecursiveASTVisitor.h" | 
|  | #include "clang/AST/StmtCXX.h" | 
|  | #include "clang/AST/StmtVisitor.h" | 
|  | #include "clang/AST/Type.h" | 
|  | #include "clang/Analysis/Analyses/PostOrderCFGView.h" | 
|  | #include "clang/Analysis/AnalysisContext.h" | 
|  | #include "clang/Analysis/CFG.h" | 
|  | #include "clang/Basic/OperatorKinds.h" | 
|  | #include "clang/Basic/SourceLocation.h" | 
|  | #include "llvm/ADT/DenseMap.h" | 
|  | #include <memory> | 
|  |  | 
|  | // TODO: Adjust states of args to constructors in the same way that arguments to | 
|  | //       function calls are handled. | 
|  | // TODO: Use information from tests in for- and while-loop conditional. | 
|  | // TODO: Add notes about the actual and expected state for | 
|  | // TODO: Correctly identify unreachable blocks when chaining boolean operators. | 
|  | // TODO: Adjust the parser and AttributesList class to support lists of | 
|  | //       identifiers. | 
|  | // TODO: Warn about unreachable code. | 
|  | // TODO: Switch to using a bitmap to track unreachable blocks. | 
|  | // TODO: Handle variable definitions, e.g. bool valid = x.isValid(); | 
|  | //       if (valid) ...; (Deferred) | 
|  | // TODO: Take notes on state transitions to provide better warning messages. | 
|  | //       (Deferred) | 
|  | // TODO: Test nested conditionals: A) Checking the same value multiple times, | 
|  | //       and 2) Checking different values. (Deferred) | 
|  |  | 
|  | using namespace clang; | 
|  | using namespace consumed; | 
|  |  | 
|  | // Key method definition | 
|  | ConsumedWarningsHandlerBase::~ConsumedWarningsHandlerBase() {} | 
|  |  | 
|  | static SourceLocation getFirstStmtLoc(const CFGBlock *Block) { | 
|  | // Find the source location of the first statement in the block, if the block | 
|  | // is not empty. | 
|  | for (const auto &B : *Block) | 
|  | if (Optional<CFGStmt> CS = B.getAs<CFGStmt>()) | 
|  | return CS->getStmt()->getLocStart(); | 
|  |  | 
|  | // Block is empty. | 
|  | // If we have one successor, return the first statement in that block | 
|  | if (Block->succ_size() == 1 && *Block->succ_begin()) | 
|  | return getFirstStmtLoc(*Block->succ_begin()); | 
|  |  | 
|  | return SourceLocation(); | 
|  | } | 
|  |  | 
|  | static SourceLocation getLastStmtLoc(const CFGBlock *Block) { | 
|  | // Find the source location of the last statement in the block, if the block | 
|  | // is not empty. | 
|  | if (const Stmt *StmtNode = Block->getTerminator()) { | 
|  | return StmtNode->getLocStart(); | 
|  | } else { | 
|  | for (CFGBlock::const_reverse_iterator BI = Block->rbegin(), | 
|  | BE = Block->rend(); BI != BE; ++BI) { | 
|  | if (Optional<CFGStmt> CS = BI->getAs<CFGStmt>()) | 
|  | return CS->getStmt()->getLocStart(); | 
|  | } | 
|  | } | 
|  |  | 
|  | // If we have one successor, return the first statement in that block | 
|  | SourceLocation Loc; | 
|  | if (Block->succ_size() == 1 && *Block->succ_begin()) | 
|  | Loc = getFirstStmtLoc(*Block->succ_begin()); | 
|  | if (Loc.isValid()) | 
|  | return Loc; | 
|  |  | 
|  | // If we have one predecessor, return the last statement in that block | 
|  | if (Block->pred_size() == 1 && *Block->pred_begin()) | 
|  | return getLastStmtLoc(*Block->pred_begin()); | 
|  |  | 
|  | return Loc; | 
|  | } | 
|  |  | 
|  | static ConsumedState invertConsumedUnconsumed(ConsumedState State) { | 
|  | switch (State) { | 
|  | case CS_Unconsumed: | 
|  | return CS_Consumed; | 
|  | case CS_Consumed: | 
|  | return CS_Unconsumed; | 
|  | case CS_None: | 
|  | return CS_None; | 
|  | case CS_Unknown: | 
|  | return CS_Unknown; | 
|  | } | 
|  | llvm_unreachable("invalid enum"); | 
|  | } | 
|  |  | 
|  | static bool isCallableInState(const CallableWhenAttr *CWAttr, | 
|  | ConsumedState State) { | 
|  |  | 
|  | for (const auto &S : CWAttr->callableStates()) { | 
|  | ConsumedState MappedAttrState = CS_None; | 
|  |  | 
|  | switch (S) { | 
|  | case CallableWhenAttr::Unknown: | 
|  | MappedAttrState = CS_Unknown; | 
|  | break; | 
|  |  | 
|  | case CallableWhenAttr::Unconsumed: | 
|  | MappedAttrState = CS_Unconsumed; | 
|  | break; | 
|  |  | 
|  | case CallableWhenAttr::Consumed: | 
|  | MappedAttrState = CS_Consumed; | 
|  | break; | 
|  | } | 
|  |  | 
|  | if (MappedAttrState == State) | 
|  | return true; | 
|  | } | 
|  |  | 
|  | return false; | 
|  | } | 
|  |  | 
|  |  | 
|  | static bool isConsumableType(const QualType &QT) { | 
|  | if (QT->isPointerType() || QT->isReferenceType()) | 
|  | return false; | 
|  |  | 
|  | if (const CXXRecordDecl *RD = QT->getAsCXXRecordDecl()) | 
|  | return RD->hasAttr<ConsumableAttr>(); | 
|  |  | 
|  | return false; | 
|  | } | 
|  |  | 
|  | static bool isAutoCastType(const QualType &QT) { | 
|  | if (QT->isPointerType() || QT->isReferenceType()) | 
|  | return false; | 
|  |  | 
|  | if (const CXXRecordDecl *RD = QT->getAsCXXRecordDecl()) | 
|  | return RD->hasAttr<ConsumableAutoCastAttr>(); | 
|  |  | 
|  | return false; | 
|  | } | 
|  |  | 
|  | static bool isSetOnReadPtrType(const QualType &QT) { | 
|  | if (const CXXRecordDecl *RD = QT->getPointeeCXXRecordDecl()) | 
|  | return RD->hasAttr<ConsumableSetOnReadAttr>(); | 
|  | return false; | 
|  | } | 
|  |  | 
|  |  | 
|  | static bool isKnownState(ConsumedState State) { | 
|  | switch (State) { | 
|  | case CS_Unconsumed: | 
|  | case CS_Consumed: | 
|  | return true; | 
|  | case CS_None: | 
|  | case CS_Unknown: | 
|  | return false; | 
|  | } | 
|  | llvm_unreachable("invalid enum"); | 
|  | } | 
|  |  | 
|  | static bool isRValueRef(QualType ParamType) { | 
|  | return ParamType->isRValueReferenceType(); | 
|  | } | 
|  |  | 
|  | static bool isTestingFunction(const FunctionDecl *FunDecl) { | 
|  | return FunDecl->hasAttr<TestTypestateAttr>(); | 
|  | } | 
|  |  | 
|  | static bool isPointerOrRef(QualType ParamType) { | 
|  | return ParamType->isPointerType() || ParamType->isReferenceType(); | 
|  | } | 
|  |  | 
|  | static ConsumedState mapConsumableAttrState(const QualType QT) { | 
|  | assert(isConsumableType(QT)); | 
|  |  | 
|  | const ConsumableAttr *CAttr = | 
|  | QT->getAsCXXRecordDecl()->getAttr<ConsumableAttr>(); | 
|  |  | 
|  | switch (CAttr->getDefaultState()) { | 
|  | case ConsumableAttr::Unknown: | 
|  | return CS_Unknown; | 
|  | case ConsumableAttr::Unconsumed: | 
|  | return CS_Unconsumed; | 
|  | case ConsumableAttr::Consumed: | 
|  | return CS_Consumed; | 
|  | } | 
|  | llvm_unreachable("invalid enum"); | 
|  | } | 
|  |  | 
|  | static ConsumedState | 
|  | mapParamTypestateAttrState(const ParamTypestateAttr *PTAttr) { | 
|  | switch (PTAttr->getParamState()) { | 
|  | case ParamTypestateAttr::Unknown: | 
|  | return CS_Unknown; | 
|  | case ParamTypestateAttr::Unconsumed: | 
|  | return CS_Unconsumed; | 
|  | case ParamTypestateAttr::Consumed: | 
|  | return CS_Consumed; | 
|  | } | 
|  | llvm_unreachable("invalid_enum"); | 
|  | } | 
|  |  | 
|  | static ConsumedState | 
|  | mapReturnTypestateAttrState(const ReturnTypestateAttr *RTSAttr) { | 
|  | switch (RTSAttr->getState()) { | 
|  | case ReturnTypestateAttr::Unknown: | 
|  | return CS_Unknown; | 
|  | case ReturnTypestateAttr::Unconsumed: | 
|  | return CS_Unconsumed; | 
|  | case ReturnTypestateAttr::Consumed: | 
|  | return CS_Consumed; | 
|  | } | 
|  | llvm_unreachable("invalid enum"); | 
|  | } | 
|  |  | 
|  | static ConsumedState mapSetTypestateAttrState(const SetTypestateAttr *STAttr) { | 
|  | switch (STAttr->getNewState()) { | 
|  | case SetTypestateAttr::Unknown: | 
|  | return CS_Unknown; | 
|  | case SetTypestateAttr::Unconsumed: | 
|  | return CS_Unconsumed; | 
|  | case SetTypestateAttr::Consumed: | 
|  | return CS_Consumed; | 
|  | } | 
|  | llvm_unreachable("invalid_enum"); | 
|  | } | 
|  |  | 
|  | static StringRef stateToString(ConsumedState State) { | 
|  | switch (State) { | 
|  | case consumed::CS_None: | 
|  | return "none"; | 
|  |  | 
|  | case consumed::CS_Unknown: | 
|  | return "unknown"; | 
|  |  | 
|  | case consumed::CS_Unconsumed: | 
|  | return "unconsumed"; | 
|  |  | 
|  | case consumed::CS_Consumed: | 
|  | return "consumed"; | 
|  | } | 
|  | llvm_unreachable("invalid enum"); | 
|  | } | 
|  |  | 
|  | static ConsumedState testsFor(const FunctionDecl *FunDecl) { | 
|  | assert(isTestingFunction(FunDecl)); | 
|  | switch (FunDecl->getAttr<TestTypestateAttr>()->getTestState()) { | 
|  | case TestTypestateAttr::Unconsumed: | 
|  | return CS_Unconsumed; | 
|  | case TestTypestateAttr::Consumed: | 
|  | return CS_Consumed; | 
|  | } | 
|  | llvm_unreachable("invalid enum"); | 
|  | } | 
|  |  | 
|  | namespace { | 
|  | struct VarTestResult { | 
|  | const VarDecl *Var; | 
|  | ConsumedState TestsFor; | 
|  | }; | 
|  | } // end anonymous::VarTestResult | 
|  |  | 
|  | namespace clang { | 
|  | namespace consumed { | 
|  |  | 
|  | enum EffectiveOp { | 
|  | EO_And, | 
|  | EO_Or | 
|  | }; | 
|  |  | 
|  | class PropagationInfo { | 
|  | enum { | 
|  | IT_None, | 
|  | IT_State, | 
|  | IT_VarTest, | 
|  | IT_BinTest, | 
|  | IT_Var, | 
|  | IT_Tmp | 
|  | } InfoType; | 
|  |  | 
|  | struct BinTestTy { | 
|  | const BinaryOperator *Source; | 
|  | EffectiveOp EOp; | 
|  | VarTestResult LTest; | 
|  | VarTestResult RTest; | 
|  | }; | 
|  |  | 
|  | union { | 
|  | ConsumedState State; | 
|  | VarTestResult VarTest; | 
|  | const VarDecl *Var; | 
|  | const CXXBindTemporaryExpr *Tmp; | 
|  | BinTestTy BinTest; | 
|  | }; | 
|  |  | 
|  | public: | 
|  | PropagationInfo() : InfoType(IT_None) {} | 
|  |  | 
|  | PropagationInfo(const VarTestResult &VarTest) | 
|  | : InfoType(IT_VarTest), VarTest(VarTest) {} | 
|  |  | 
|  | PropagationInfo(const VarDecl *Var, ConsumedState TestsFor) | 
|  | : InfoType(IT_VarTest) { | 
|  |  | 
|  | VarTest.Var      = Var; | 
|  | VarTest.TestsFor = TestsFor; | 
|  | } | 
|  |  | 
|  | PropagationInfo(const BinaryOperator *Source, EffectiveOp EOp, | 
|  | const VarTestResult <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; | 
|  | } | 
|  |  | 
|  | inline bool isValid()   const { return InfoType != IT_None;    } | 
|  | inline bool isState()   const { return InfoType == IT_State;   } | 
|  | inline bool isVarTest() const { return InfoType == IT_VarTest; } | 
|  | inline bool isBinTest() const { return InfoType == IT_BinTest; } | 
|  | inline bool isVar()     const { return InfoType == IT_Var;     } | 
|  | inline bool isTmp()     const { return InfoType == IT_Tmp;     } | 
|  |  | 
|  | bool isTest() const { | 
|  | return InfoType == IT_VarTest || InfoType == IT_BinTest; | 
|  | } | 
|  |  | 
|  | bool isPointerToValue() const { | 
|  | return InfoType == IT_Var || InfoType == IT_Tmp; | 
|  | } | 
|  |  | 
|  | PropagationInfo invertTest() const { | 
|  | assert(InfoType == IT_VarTest || InfoType == IT_BinTest); | 
|  |  | 
|  | if (InfoType == IT_VarTest) { | 
|  | return PropagationInfo(VarTest.Var, | 
|  | invertConsumedUnconsumed(VarTest.TestsFor)); | 
|  |  | 
|  | } else if (InfoType == IT_BinTest) { | 
|  | return PropagationInfo(BinTest.Source, | 
|  | BinTest.EOp == EO_And ? EO_Or : EO_And, | 
|  | BinTest.LTest.Var, invertConsumedUnconsumed(BinTest.LTest.TestsFor), | 
|  | BinTest.RTest.Var, invertConsumedUnconsumed(BinTest.RTest.TestsFor)); | 
|  | } else { | 
|  | return PropagationInfo(); | 
|  | } | 
|  | } | 
|  | }; | 
|  |  | 
|  | static inline void | 
|  | setStateForVarOrTmp(ConsumedStateMap *StateMap, const PropagationInfo &PInfo, | 
|  | ConsumedState State) { | 
|  |  | 
|  | assert(PInfo.isVar() || PInfo.isTmp()); | 
|  |  | 
|  | if (PInfo.isVar()) | 
|  | StateMap->setState(PInfo.getVar(), State); | 
|  | else | 
|  | StateMap->setState(PInfo.getTmp(), State); | 
|  | } | 
|  |  | 
|  | class ConsumedStmtVisitor : public ConstStmtVisitor<ConsumedStmtVisitor> { | 
|  |  | 
|  | typedef llvm::DenseMap<const Stmt *, PropagationInfo> MapType; | 
|  | typedef std::pair<const Stmt *, PropagationInfo> PairType; | 
|  | typedef MapType::iterator InfoEntry; | 
|  | typedef MapType::const_iterator ConstInfoEntry; | 
|  |  | 
|  | AnalysisDeclContext &AC; | 
|  | ConsumedAnalyzer &Analyzer; | 
|  | ConsumedStateMap *StateMap; | 
|  | MapType PropagationMap; | 
|  |  | 
|  | InfoEntry findInfo(const Expr *E) { | 
|  | if (auto Cleanups = dyn_cast<ExprWithCleanups>(E)) | 
|  | if (!Cleanups->cleanupsHaveSideEffects()) | 
|  | E = Cleanups->getSubExpr(); | 
|  | return PropagationMap.find(E->IgnoreParens()); | 
|  | } | 
|  | ConstInfoEntry findInfo(const Expr *E) const { | 
|  | if (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(AnalysisDeclContext &AC, ConsumedAnalyzer &Analyzer, | 
|  | ConsumedStateMap *StateMap) | 
|  | : AC(AC), Analyzer(Analyzer), StateMap(StateMap) {} | 
|  |  | 
|  | PropagationInfo getInfo(const Expr *StmtNode) const { | 
|  | ConstInfoEntry Entry = findInfo(StmtNode); | 
|  |  | 
|  | if (Entry != PropagationMap.end()) | 
|  | return Entry->second; | 
|  | else | 
|  | return PropagationInfo(); | 
|  | } | 
|  |  | 
|  | void reset(ConsumedStateMap *NewStateMap) { | 
|  | StateMap = NewStateMap; | 
|  | } | 
|  | }; | 
|  |  | 
|  |  | 
|  | void ConsumedStmtVisitor::forwardInfo(const Expr *From, const Expr *To) { | 
|  | InfoEntry Entry = findInfo(From); | 
|  | if (Entry != PropagationMap.end()) | 
|  | insertInfo(To, Entry->second); | 
|  | } | 
|  |  | 
|  |  | 
|  | // Create a new state for To, which is initialized to the state of From. | 
|  | // If NS is not CS_None, sets the state of From to NS. | 
|  | void ConsumedStmtVisitor::copyInfo(const Expr *From, const Expr *To, | 
|  | ConsumedState NS) { | 
|  | InfoEntry Entry = findInfo(From); | 
|  | if (Entry != PropagationMap.end()) { | 
|  | PropagationInfo& PInfo = Entry->second; | 
|  | ConsumedState CS = PInfo.getAsState(StateMap); | 
|  | if (CS != CS_None) | 
|  | insertInfo(To, PropagationInfo(CS)); | 
|  | if (NS != CS_None && PInfo.isPointerToValue()) | 
|  | setStateForVarOrTmp(StateMap, PInfo, NS); | 
|  | } | 
|  | } | 
|  |  | 
|  |  | 
|  | // Get the ConsumedState for From | 
|  | ConsumedState ConsumedStmtVisitor::getInfo(const Expr *From) { | 
|  | InfoEntry Entry = findInfo(From); | 
|  | if (Entry != PropagationMap.end()) { | 
|  | PropagationInfo& PInfo = Entry->second; | 
|  | return PInfo.getAsState(StateMap); | 
|  | } | 
|  | return CS_None; | 
|  | } | 
|  |  | 
|  |  | 
|  | // If we already have info for To then update it, otherwise create a new entry. | 
|  | void ConsumedStmtVisitor::setInfo(const Expr *To, ConsumedState NS) { | 
|  | InfoEntry Entry = findInfo(To); | 
|  | if (Entry != PropagationMap.end()) { | 
|  | PropagationInfo& PInfo = Entry->second; | 
|  | if (PInfo.isPointerToValue()) | 
|  | setStateForVarOrTmp(StateMap, PInfo, NS); | 
|  | } else if (NS != CS_None) { | 
|  | insertInfo(To, PropagationInfo(NS)); | 
|  | } | 
|  | } | 
|  |  | 
|  |  | 
|  |  | 
|  | void ConsumedStmtVisitor::checkCallability(const PropagationInfo &PInfo, | 
|  | const FunctionDecl *FunDecl, | 
|  | SourceLocation BlameLoc) { | 
|  | assert(!PInfo.isTest()); | 
|  |  | 
|  | const CallableWhenAttr *CWAttr = FunDecl->getAttr<CallableWhenAttr>(); | 
|  | if (!CWAttr) | 
|  | return; | 
|  |  | 
|  | if (PInfo.isVar()) { | 
|  | ConsumedState VarState = StateMap->getState(PInfo.getVar()); | 
|  |  | 
|  | if (VarState == CS_None || isCallableInState(CWAttr, VarState)) | 
|  | return; | 
|  |  | 
|  | Analyzer.WarningsHandler.warnUseInInvalidState( | 
|  | FunDecl->getNameAsString(), PInfo.getVar()->getNameAsString(), | 
|  | stateToString(VarState), BlameLoc); | 
|  |  | 
|  | } else { | 
|  | ConsumedState TmpState = PInfo.getAsState(StateMap); | 
|  |  | 
|  | if (TmpState == CS_None || isCallableInState(CWAttr, TmpState)) | 
|  | return; | 
|  |  | 
|  | Analyzer.WarningsHandler.warnUseOfTempInInvalidState( | 
|  | FunDecl->getNameAsString(), stateToString(TmpState), BlameLoc); | 
|  | } | 
|  | } | 
|  |  | 
|  |  | 
|  | // Factors out common behavior for function, method, and operator calls. | 
|  | // Check parameters and set parameter state if necessary. | 
|  | // Returns true if the state of ObjArg is set, or false otherwise. | 
|  | bool ConsumedStmtVisitor::handleCall(const CallExpr *Call, const Expr *ObjArg, | 
|  | const FunctionDecl *FunD) { | 
|  | unsigned Offset = 0; | 
|  | if (isa<CXXOperatorCallExpr>(Call) && isa<CXXMethodDecl>(FunD)) | 
|  | Offset = 1;  // first argument is 'this' | 
|  |  | 
|  | // check explicit parameters | 
|  | for (unsigned Index = Offset; Index < Call->getNumArgs(); ++Index) { | 
|  | // Skip variable argument lists. | 
|  | if (Index - Offset >= FunD->getNumParams()) | 
|  | break; | 
|  |  | 
|  | const ParmVarDecl *Param = FunD->getParamDecl(Index - Offset); | 
|  | QualType ParamType = Param->getType(); | 
|  |  | 
|  | InfoEntry Entry = findInfo(Call->getArg(Index)); | 
|  |  | 
|  | if (Entry == PropagationMap.end() || Entry->second.isTest()) | 
|  | continue; | 
|  | PropagationInfo PInfo = Entry->second; | 
|  |  | 
|  | // Check that the parameter is in the correct state. | 
|  | if (ParamTypestateAttr *PTA = Param->getAttr<ParamTypestateAttr>()) { | 
|  | ConsumedState ParamState = PInfo.getAsState(StateMap); | 
|  | ConsumedState ExpectedState = mapParamTypestateAttrState(PTA); | 
|  |  | 
|  | if (ParamState != ExpectedState) | 
|  | Analyzer.WarningsHandler.warnParamTypestateMismatch( | 
|  | Call->getArg(Index)->getExprLoc(), | 
|  | stateToString(ExpectedState), stateToString(ParamState)); | 
|  | } | 
|  |  | 
|  | if (!(Entry->second.isVar() || Entry->second.isTmp())) | 
|  | continue; | 
|  |  | 
|  | // Adjust state on the caller side. | 
|  | if (isRValueRef(ParamType)) | 
|  | setStateForVarOrTmp(StateMap, PInfo, consumed::CS_Consumed); | 
|  | else if (ReturnTypestateAttr *RT = Param->getAttr<ReturnTypestateAttr>()) | 
|  | setStateForVarOrTmp(StateMap, PInfo, mapReturnTypestateAttrState(RT)); | 
|  | else if (isPointerOrRef(ParamType) && | 
|  | (!ParamType->getPointeeType().isConstQualified() || | 
|  | isSetOnReadPtrType(ParamType))) | 
|  | setStateForVarOrTmp(StateMap, PInfo, consumed::CS_Unknown); | 
|  | } | 
|  |  | 
|  | if (!ObjArg) | 
|  | return false; | 
|  |  | 
|  | // check implicit 'self' parameter, if present | 
|  | InfoEntry Entry = findInfo(ObjArg); | 
|  | if (Entry != PropagationMap.end()) { | 
|  | PropagationInfo PInfo = Entry->second; | 
|  | checkCallability(PInfo, FunD, Call->getExprLoc()); | 
|  |  | 
|  | if (SetTypestateAttr *STA = FunD->getAttr<SetTypestateAttr>()) { | 
|  | if (PInfo.isVar()) { | 
|  | StateMap->setState(PInfo.getVar(), mapSetTypestateAttrState(STA)); | 
|  | return true; | 
|  | } | 
|  | else if (PInfo.isTmp()) { | 
|  | StateMap->setState(PInfo.getTmp(), mapSetTypestateAttrState(STA)); | 
|  | return true; | 
|  | } | 
|  | } | 
|  | else if (isTestingFunction(FunD) && PInfo.isVar()) { | 
|  | PropagationMap.insert(PairType(Call, | 
|  | PropagationInfo(PInfo.getVar(), testsFor(FunD)))); | 
|  | } | 
|  | } | 
|  | return false; | 
|  | } | 
|  |  | 
|  |  | 
|  | void ConsumedStmtVisitor::propagateReturnType(const Expr *Call, | 
|  | const FunctionDecl *Fun) { | 
|  | QualType RetType = Fun->getCallResultType(); | 
|  | if (RetType->isReferenceType()) | 
|  | RetType = RetType->getPointeeType(); | 
|  |  | 
|  | if (isConsumableType(RetType)) { | 
|  | ConsumedState ReturnState; | 
|  | if (ReturnTypestateAttr *RTA = Fun->getAttr<ReturnTypestateAttr>()) | 
|  | ReturnState = mapReturnTypestateAttrState(RTA); | 
|  | else | 
|  | ReturnState = mapConsumableAttrState(RetType); | 
|  |  | 
|  | PropagationMap.insert(PairType(Call, PropagationInfo(ReturnState))); | 
|  | } | 
|  | } | 
|  |  | 
|  |  | 
|  | void ConsumedStmtVisitor::VisitBinaryOperator(const BinaryOperator *BinOp) { | 
|  | switch (BinOp->getOpcode()) { | 
|  | case BO_LAnd: | 
|  | case BO_LOr : { | 
|  | InfoEntry LEntry = findInfo(BinOp->getLHS()), | 
|  | REntry = findInfo(BinOp->getRHS()); | 
|  |  | 
|  | VarTestResult LTest, RTest; | 
|  |  | 
|  | if (LEntry != PropagationMap.end() && LEntry->second.isVarTest()) { | 
|  | LTest = LEntry->second.getVarTest(); | 
|  |  | 
|  | } else { | 
|  | LTest.Var      = nullptr; | 
|  | LTest.TestsFor = CS_None; | 
|  | } | 
|  |  | 
|  | if (REntry != PropagationMap.end() && REntry->second.isVarTest()) { | 
|  | RTest = REntry->second.getVarTest(); | 
|  |  | 
|  | } else { | 
|  | RTest.Var      = nullptr; | 
|  | RTest.TestsFor = CS_None; | 
|  | } | 
|  |  | 
|  | if (!(LTest.Var == nullptr && RTest.Var == nullptr)) | 
|  | PropagationMap.insert(PairType(BinOp, PropagationInfo(BinOp, | 
|  | static_cast<EffectiveOp>(BinOp->getOpcode() == BO_LOr), LTest, RTest))); | 
|  |  | 
|  | break; | 
|  | } | 
|  |  | 
|  | case BO_PtrMemD: | 
|  | case BO_PtrMemI: | 
|  | forwardInfo(BinOp->getLHS(), BinOp); | 
|  | break; | 
|  |  | 
|  | default: | 
|  | break; | 
|  | } | 
|  | } | 
|  |  | 
|  | void ConsumedStmtVisitor::VisitCallExpr(const CallExpr *Call) { | 
|  | const FunctionDecl *FunDecl = Call->getDirectCallee(); | 
|  | if (!FunDecl) | 
|  | return; | 
|  |  | 
|  | // Special case for the std::move function. | 
|  | // TODO: Make this more specific. (Deferred) | 
|  | if (Call->getNumArgs() == 1 && FunDecl->getNameAsString() == "move" && | 
|  | FunDecl->isInStdNamespace()) { | 
|  | copyInfo(Call->getArg(0), Call, CS_Consumed); | 
|  | return; | 
|  | } | 
|  |  | 
|  | handleCall(Call, nullptr, FunDecl); | 
|  | propagateReturnType(Call, FunDecl); | 
|  | } | 
|  |  | 
|  | void ConsumedStmtVisitor::VisitCastExpr(const CastExpr *Cast) { | 
|  | forwardInfo(Cast->getSubExpr(), Cast); | 
|  | } | 
|  |  | 
|  | void ConsumedStmtVisitor::VisitCXXBindTemporaryExpr( | 
|  | const CXXBindTemporaryExpr *Temp) { | 
|  |  | 
|  | InfoEntry Entry = findInfo(Temp->getSubExpr()); | 
|  |  | 
|  | if (Entry != PropagationMap.end() && !Entry->second.isTest()) { | 
|  | StateMap->setState(Temp, Entry->second.getAsState(StateMap)); | 
|  | PropagationMap.insert(PairType(Temp, PropagationInfo(Temp))); | 
|  | } | 
|  | } | 
|  |  | 
|  | void ConsumedStmtVisitor::VisitCXXConstructExpr(const CXXConstructExpr *Call) { | 
|  | CXXConstructorDecl *Constructor = Call->getConstructor(); | 
|  |  | 
|  | ASTContext &CurrContext = AC.getASTContext(); | 
|  | QualType ThisType = Constructor->getThisType(CurrContext)->getPointeeType(); | 
|  |  | 
|  | if (!isConsumableType(ThisType)) | 
|  | return; | 
|  |  | 
|  | // FIXME: What should happen if someone annotates the move constructor? | 
|  | if (ReturnTypestateAttr *RTA = Constructor->getAttr<ReturnTypestateAttr>()) { | 
|  | // TODO: Adjust state of args appropriately. | 
|  | ConsumedState RetState = mapReturnTypestateAttrState(RTA); | 
|  | PropagationMap.insert(PairType(Call, PropagationInfo(RetState))); | 
|  | } else if (Constructor->isDefaultConstructor()) { | 
|  | PropagationMap.insert(PairType(Call, | 
|  | PropagationInfo(consumed::CS_Consumed))); | 
|  | } else if (Constructor->isMoveConstructor()) { | 
|  | copyInfo(Call->getArg(0), Call, CS_Consumed); | 
|  | } else if (Constructor->isCopyConstructor()) { | 
|  | // Copy state from arg.  If setStateOnRead then set arg to CS_Unknown. | 
|  | ConsumedState NS = | 
|  | isSetOnReadPtrType(Constructor->getThisType(CurrContext)) ? | 
|  | CS_Unknown : CS_None; | 
|  | copyInfo(Call->getArg(0), Call, NS); | 
|  | } else { | 
|  | // TODO: Adjust state of args appropriately. | 
|  | ConsumedState RetState = mapConsumableAttrState(ThisType); | 
|  | PropagationMap.insert(PairType(Call, PropagationInfo(RetState))); | 
|  | } | 
|  | } | 
|  |  | 
|  |  | 
|  | void ConsumedStmtVisitor::VisitCXXMemberCallExpr( | 
|  | const CXXMemberCallExpr *Call) { | 
|  | CXXMethodDecl* MD = Call->getMethodDecl(); | 
|  | if (!MD) | 
|  | return; | 
|  |  | 
|  | handleCall(Call, Call->getImplicitObjectArgument(), MD); | 
|  | propagateReturnType(Call, MD); | 
|  | } | 
|  |  | 
|  |  | 
|  | void ConsumedStmtVisitor::VisitCXXOperatorCallExpr( | 
|  | const CXXOperatorCallExpr *Call) { | 
|  |  | 
|  | const FunctionDecl *FunDecl = | 
|  | dyn_cast_or_null<FunctionDecl>(Call->getDirectCallee()); | 
|  | if (!FunDecl) return; | 
|  |  | 
|  | if (Call->getOperator() == OO_Equal) { | 
|  | ConsumedState CS = getInfo(Call->getArg(1)); | 
|  | if (!handleCall(Call, Call->getArg(0), FunDecl)) | 
|  | setInfo(Call->getArg(0), CS); | 
|  | return; | 
|  | } | 
|  |  | 
|  | if (const CXXMemberCallExpr *MCall = dyn_cast<CXXMemberCallExpr>(Call)) | 
|  | handleCall(MCall, MCall->getImplicitObjectArgument(), FunDecl); | 
|  | else | 
|  | handleCall(Call, Call->getArg(0), FunDecl); | 
|  |  | 
|  | propagateReturnType(Call, FunDecl); | 
|  | } | 
|  |  | 
|  | void ConsumedStmtVisitor::VisitDeclRefExpr(const DeclRefExpr *DeclRef) { | 
|  | if (const VarDecl *Var = dyn_cast_or_null<VarDecl>(DeclRef->getDecl())) | 
|  | if (StateMap->getState(Var) != consumed::CS_None) | 
|  | PropagationMap.insert(PairType(DeclRef, PropagationInfo(Var))); | 
|  | } | 
|  |  | 
|  | void ConsumedStmtVisitor::VisitDeclStmt(const DeclStmt *DeclS) { | 
|  | for (const auto *DI : DeclS->decls()) | 
|  | if (isa<VarDecl>(DI)) | 
|  | VisitVarDecl(cast<VarDecl>(DI)); | 
|  |  | 
|  | if (DeclS->isSingleDecl()) | 
|  | if (const VarDecl *Var = dyn_cast_or_null<VarDecl>(DeclS->getSingleDecl())) | 
|  | PropagationMap.insert(PairType(DeclS, PropagationInfo(Var))); | 
|  | } | 
|  |  | 
|  | void ConsumedStmtVisitor::VisitMaterializeTemporaryExpr( | 
|  | const MaterializeTemporaryExpr *Temp) { | 
|  |  | 
|  | forwardInfo(Temp->GetTemporaryExpr(), Temp); | 
|  | } | 
|  |  | 
|  | void ConsumedStmtVisitor::VisitMemberExpr(const MemberExpr *MExpr) { | 
|  | forwardInfo(MExpr->getBase(), MExpr); | 
|  | } | 
|  |  | 
|  |  | 
|  | void ConsumedStmtVisitor::VisitParmVarDecl(const ParmVarDecl *Param) { | 
|  | QualType ParamType = Param->getType(); | 
|  | ConsumedState ParamState = consumed::CS_None; | 
|  |  | 
|  | if (const ParamTypestateAttr *PTA = Param->getAttr<ParamTypestateAttr>()) | 
|  | ParamState = mapParamTypestateAttrState(PTA); | 
|  | else if (isConsumableType(ParamType)) | 
|  | ParamState = mapConsumableAttrState(ParamType); | 
|  | else if (isRValueRef(ParamType) && | 
|  | isConsumableType(ParamType->getPointeeType())) | 
|  | ParamState = mapConsumableAttrState(ParamType->getPointeeType()); | 
|  | else if (ParamType->isReferenceType() && | 
|  | isConsumableType(ParamType->getPointeeType())) | 
|  | ParamState = consumed::CS_Unknown; | 
|  |  | 
|  | if (ParamState != CS_None) | 
|  | StateMap->setState(Param, ParamState); | 
|  | } | 
|  |  | 
|  | void ConsumedStmtVisitor::VisitReturnStmt(const ReturnStmt *Ret) { | 
|  | ConsumedState ExpectedState = Analyzer.getExpectedReturnState(); | 
|  |  | 
|  | if (ExpectedState != CS_None) { | 
|  | InfoEntry Entry = findInfo(Ret->getRetValue()); | 
|  |  | 
|  | if (Entry != PropagationMap.end()) { | 
|  | ConsumedState RetState = Entry->second.getAsState(StateMap); | 
|  |  | 
|  | if (RetState != ExpectedState) | 
|  | Analyzer.WarningsHandler.warnReturnTypestateMismatch( | 
|  | Ret->getReturnLoc(), stateToString(ExpectedState), | 
|  | stateToString(RetState)); | 
|  | } | 
|  | } | 
|  |  | 
|  | StateMap->checkParamsForReturnTypestate(Ret->getLocStart(), | 
|  | Analyzer.WarningsHandler); | 
|  | } | 
|  |  | 
|  | void ConsumedStmtVisitor::VisitUnaryOperator(const UnaryOperator *UOp) { | 
|  | InfoEntry Entry = findInfo(UOp->getSubExpr()); | 
|  | if (Entry == PropagationMap.end()) return; | 
|  |  | 
|  | switch (UOp->getOpcode()) { | 
|  | case UO_AddrOf: | 
|  | PropagationMap.insert(PairType(UOp, Entry->second)); | 
|  | break; | 
|  |  | 
|  | case UO_LNot: | 
|  | if (Entry->second.isTest()) | 
|  | PropagationMap.insert(PairType(UOp, Entry->second.invertTest())); | 
|  | break; | 
|  |  | 
|  | default: | 
|  | break; | 
|  | } | 
|  | } | 
|  |  | 
|  | // TODO: See if I need to check for reference types here. | 
|  | void ConsumedStmtVisitor::VisitVarDecl(const VarDecl *Var) { | 
|  | if (isConsumableType(Var->getType())) { | 
|  | if (Var->hasInit()) { | 
|  | MapType::iterator VIT = findInfo(Var->getInit()->IgnoreImplicit()); | 
|  | if (VIT != PropagationMap.end()) { | 
|  | PropagationInfo PInfo = VIT->second; | 
|  | ConsumedState St = PInfo.getAsState(StateMap); | 
|  |  | 
|  | if (St != consumed::CS_None) { | 
|  | StateMap->setState(Var, St); | 
|  | return; | 
|  | } | 
|  | } | 
|  | } | 
|  | // Otherwise | 
|  | StateMap->setState(Var, consumed::CS_Unknown); | 
|  | } | 
|  | } | 
|  | }} // end clang::consumed::ConsumedStmtVisitor | 
|  |  | 
|  | namespace clang { | 
|  | namespace consumed { | 
|  |  | 
|  | 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 = llvm::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) ? llvm::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 ParmVarDecl *Param = cast<ParmVarDecl>(DM.first); | 
|  | const ReturnTypestateAttr *RTA = Param->getAttr<ReturnTypestateAttr>(); | 
|  |  | 
|  | if (!RTA) | 
|  | continue; | 
|  |  | 
|  | ConsumedState ExpectedState = mapReturnTypestateAttrState(RTA); | 
|  | if (DM.second != ExpectedState) | 
|  | WarningsHandler.warnParamReturnTypestateMismatch(BlameLoc, | 
|  | Param->getNameAsString(), stateToString(ExpectedState), | 
|  | stateToString(DM.second)); | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | void ConsumedStateMap::clearTemporaries() { | 
|  | TmpMap.clear(); | 
|  | } | 
|  |  | 
|  | ConsumedState ConsumedStateMap::getState(const VarDecl *Var) const { | 
|  | VarMapType::const_iterator Entry = VarMap.find(Var); | 
|  |  | 
|  | if (Entry != VarMap.end()) | 
|  | return Entry->second; | 
|  |  | 
|  | return CS_None; | 
|  | } | 
|  |  | 
|  | ConsumedState | 
|  | ConsumedStateMap::getState(const CXXBindTemporaryExpr *Tmp) const { | 
|  | TmpMapType::const_iterator Entry = TmpMap.find(Tmp); | 
|  |  | 
|  | if (Entry != TmpMap.end()) | 
|  | return Entry->second; | 
|  |  | 
|  | return CS_None; | 
|  | } | 
|  |  | 
|  | void ConsumedStateMap::intersect(const ConsumedStateMap &Other) { | 
|  | ConsumedState LocalState; | 
|  |  | 
|  | if (this->From && this->From == Other.From && !Other.Reachable) { | 
|  | this->markUnreachable(); | 
|  | return; | 
|  | } | 
|  |  | 
|  | for (const auto &DM : Other.VarMap) { | 
|  | LocalState = this->getState(DM.first); | 
|  |  | 
|  | if (LocalState == CS_None) | 
|  | continue; | 
|  |  | 
|  | if (LocalState != DM.second) | 
|  | VarMap[DM.first] = CS_Unknown; | 
|  | } | 
|  | } | 
|  |  | 
|  | void ConsumedStateMap::intersectAtLoopHead(const CFGBlock *LoopHead, | 
|  | const CFGBlock *LoopBack, const ConsumedStateMap *LoopBackStates, | 
|  | ConsumedWarningsHandlerBase &WarningsHandler) { | 
|  |  | 
|  | ConsumedState LocalState; | 
|  | SourceLocation BlameLoc = getLastStmtLoc(LoopBack); | 
|  |  | 
|  | for (const auto &DM : LoopBackStates->VarMap) { | 
|  | LocalState = this->getState(DM.first); | 
|  |  | 
|  | if (LocalState == CS_None) | 
|  | continue; | 
|  |  | 
|  | if (LocalState != DM.second) { | 
|  | VarMap[DM.first] = CS_Unknown; | 
|  | WarningsHandler.warnLoopStateMismatch(BlameLoc, | 
|  | DM.first->getNameAsString()); | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | void ConsumedStateMap::markUnreachable() { | 
|  | this->Reachable = false; | 
|  | VarMap.clear(); | 
|  | TmpMap.clear(); | 
|  | } | 
|  |  | 
|  | void ConsumedStateMap::setState(const VarDecl *Var, ConsumedState State) { | 
|  | VarMap[Var] = State; | 
|  | } | 
|  |  | 
|  | void ConsumedStateMap::setState(const CXXBindTemporaryExpr *Tmp, | 
|  | ConsumedState State) { | 
|  | TmpMap[Tmp] = State; | 
|  | } | 
|  |  | 
|  | void ConsumedStateMap::remove(const CXXBindTemporaryExpr *Tmp) { | 
|  | TmpMap.erase(Tmp); | 
|  | } | 
|  |  | 
|  | bool ConsumedStateMap::operator!=(const ConsumedStateMap *Other) const { | 
|  | for (const auto &DM : Other->VarMap) | 
|  | if (this->getState(DM.first) != DM.second) | 
|  | return true; | 
|  | return false; | 
|  | } | 
|  |  | 
|  | void ConsumedAnalyzer::determineExpectedReturnState(AnalysisDeclContext &AC, | 
|  | const FunctionDecl *D) { | 
|  | QualType ReturnType; | 
|  | if (const CXXConstructorDecl *Constructor = dyn_cast<CXXConstructorDecl>(D)) { | 
|  | ASTContext &CurrContext = AC.getASTContext(); | 
|  | ReturnType = Constructor->getThisType(CurrContext)->getPointeeType(); | 
|  | } else | 
|  | ReturnType = D->getCallResultType(); | 
|  |  | 
|  | if (const ReturnTypestateAttr *RTSAttr = D->getAttr<ReturnTypestateAttr>()) { | 
|  | const CXXRecordDecl *RD = ReturnType->getAsCXXRecordDecl(); | 
|  | if (!RD || !RD->hasAttr<ConsumableAttr>()) { | 
|  | // FIXME: This should be removed when template instantiation propagates | 
|  | //        attributes at template specialization definition, not | 
|  | //        declaration. When it is removed the test needs to be enabled | 
|  | //        in SemaDeclAttr.cpp. | 
|  | WarningsHandler.warnReturnTypestateForUnconsumableType( | 
|  | RTSAttr->getLocation(), ReturnType.getAsString()); | 
|  | ExpectedReturnState = CS_None; | 
|  | } else | 
|  | ExpectedReturnState = mapReturnTypestateAttrState(RTSAttr); | 
|  | } else if (isConsumableType(ReturnType)) { | 
|  | if (isAutoCastType(ReturnType))   // We can auto-cast the state to the | 
|  | ExpectedReturnState = CS_None;  // expected state. | 
|  | else | 
|  | ExpectedReturnState = mapConsumableAttrState(ReturnType); | 
|  | } | 
|  | else | 
|  | ExpectedReturnState = CS_None; | 
|  | } | 
|  |  | 
|  | bool ConsumedAnalyzer::splitState(const CFGBlock *CurrBlock, | 
|  | const ConsumedStmtVisitor &Visitor) { | 
|  |  | 
|  | std::unique_ptr<ConsumedStateMap> FalseStates( | 
|  | new ConsumedStateMap(*CurrStates)); | 
|  | PropagationInfo PInfo; | 
|  |  | 
|  | if (const IfStmt *IfNode = | 
|  | dyn_cast_or_null<IfStmt>(CurrBlock->getTerminator().getStmt())) { | 
|  |  | 
|  | const Expr *Cond = IfNode->getCond(); | 
|  |  | 
|  | PInfo = Visitor.getInfo(Cond); | 
|  | if (!PInfo.isValid() && isa<BinaryOperator>(Cond)) | 
|  | PInfo = Visitor.getInfo(cast<BinaryOperator>(Cond)->getRHS()); | 
|  |  | 
|  | if (PInfo.isVarTest()) { | 
|  | CurrStates->setSource(Cond); | 
|  | FalseStates->setSource(Cond); | 
|  | splitVarStateForIf(IfNode, PInfo.getVarTest(), CurrStates.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 BinaryOperator *BinOp = | 
|  | dyn_cast_or_null<BinaryOperator>(CurrBlock->getTerminator().getStmt())) { | 
|  |  | 
|  | PInfo = Visitor.getInfo(BinOp->getLHS()); | 
|  | if (!PInfo.isVarTest()) { | 
|  | if ((BinOp = dyn_cast_or_null<BinaryOperator>(BinOp->getLHS()))) { | 
|  | PInfo = Visitor.getInfo(BinOp->getRHS()); | 
|  |  | 
|  | if (!PInfo.isVarTest()) | 
|  | return false; | 
|  |  | 
|  | } else { | 
|  | return false; | 
|  | } | 
|  | } | 
|  |  | 
|  | CurrStates->setSource(BinOp); | 
|  | FalseStates->setSource(BinOp); | 
|  |  | 
|  | const VarTestResult &Test = PInfo.getVarTest(); | 
|  | ConsumedState VarState = CurrStates->getState(Test.Var); | 
|  |  | 
|  | if (BinOp->getOpcode() == BO_LAnd) { | 
|  | if (VarState == CS_Unknown) | 
|  | CurrStates->setState(Test.Var, Test.TestsFor); | 
|  | else if (VarState == invertConsumedUnconsumed(Test.TestsFor)) | 
|  | CurrStates->markUnreachable(); | 
|  |  | 
|  | } else if (BinOp->getOpcode() == BO_LOr) { | 
|  | if (VarState == CS_Unknown) | 
|  | FalseStates->setState(Test.Var, | 
|  | invertConsumedUnconsumed(Test.TestsFor)); | 
|  | else if (VarState == Test.TestsFor) | 
|  | FalseStates->markUnreachable(); | 
|  | } | 
|  |  | 
|  | } else { | 
|  | return false; | 
|  | } | 
|  |  | 
|  | CFGBlock::const_succ_iterator SI = CurrBlock->succ_begin(); | 
|  |  | 
|  | if (*SI) | 
|  | BlockInfo.addInfo(*SI, std::move(CurrStates)); | 
|  | else | 
|  | CurrStates = nullptr; | 
|  |  | 
|  | if (*++SI) | 
|  | BlockInfo.addInfo(*SI, std::move(FalseStates)); | 
|  |  | 
|  | return true; | 
|  | } | 
|  |  | 
|  | void ConsumedAnalyzer::run(AnalysisDeclContext &AC) { | 
|  | const FunctionDecl *D = dyn_cast_or_null<FunctionDecl>(AC.getDecl()); | 
|  | if (!D) | 
|  | return; | 
|  |  | 
|  | CFG *CFGraph = AC.getCFG(); | 
|  | if (!CFGraph) | 
|  | return; | 
|  |  | 
|  | determineExpectedReturnState(AC, D); | 
|  |  | 
|  | PostOrderCFGView *SortedGraph = AC.getAnalysis<PostOrderCFGView>(); | 
|  | // AC.getCFG()->viewCFG(LangOptions()); | 
|  |  | 
|  | BlockInfo = ConsumedBlockInfo(CFGraph->getNumBlockIDs(), SortedGraph); | 
|  |  | 
|  | CurrStates = llvm::make_unique<ConsumedStateMap>(); | 
|  | ConsumedStmtVisitor Visitor(AC, *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()->getLocEnd(); | 
|  | const VarDecl *Var = DTor.getVarDecl(); | 
|  |  | 
|  | Visitor.checkCallability(PropagationInfo(Var), | 
|  | DTor.getDestructorDecl(AC.getASTContext()), | 
|  | Loc); | 
|  | break; | 
|  | } | 
|  |  | 
|  | default: | 
|  | break; | 
|  | } | 
|  | } | 
|  |  | 
|  | // TODO: Handle other forms of branching with precision, including while- | 
|  | //       and for-loops. (Deferred) | 
|  | if (!splitState(CurrBlock, Visitor)) { | 
|  | CurrStates->setSource(nullptr); | 
|  |  | 
|  | if (CurrBlock->succ_size() > 1 || | 
|  | (CurrBlock->succ_size() == 1 && | 
|  | (*CurrBlock->succ_begin())->pred_size() > 1)) { | 
|  |  | 
|  | 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(); | 
|  | } | 
|  | }} // end namespace clang::consumed |