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