Index: include/clang/Analysis/Analyses/Consumed.h =================================================================== --- include/clang/Analysis/Analyses/Consumed.h +++ include/clang/Analysis/Analyses/Consumed.h @@ -24,7 +24,9 @@ namespace clang { namespace consumed { - + + class ConsumedStmtVisitor; + typedef SmallVector OptionalNotes; typedef std::pair DelayedDiag; typedef std::list DiagList; @@ -38,7 +40,7 @@ /// \brief Emit the warnings and notes left by the analysis. virtual void emitDiagnostics() {} - /// Warn about unnecessary-test errors. + /// \brief Warn about unnecessary-test errors. /// \param VariableName -- The name of the variable that holds the unique /// value. /// @@ -47,7 +49,7 @@ StringRef VariableState, SourceLocation Loc) {} - /// Warn about use-while-consumed errors. + /// \brief Warn about use-while-consumed errors. /// \param MethodName -- The name of the method that was incorrectly /// invoked. /// @@ -55,7 +57,7 @@ virtual void warnUseOfTempWhileConsumed(StringRef MethodName, SourceLocation Loc) {} - /// Warn about use-in-unknown-state errors. + /// \brief Warn about use-in-unknown-state errors. /// \param MethodName -- The name of the method that was incorrectly /// invoked. /// @@ -63,7 +65,7 @@ virtual void warnUseOfTempInUnknownState(StringRef MethodName, SourceLocation Loc) {} - /// Warn about use-while-consumed errors. + /// \brief Warn about use-while-consumed errors. /// \param MethodName -- The name of the method that was incorrectly /// invoked. /// @@ -75,7 +77,7 @@ StringRef VariableName, SourceLocation Loc) {} - /// Warn about use-in-unknown-state errors. + /// \brief Warn about use-in-unknown-state errors. /// \param MethodName -- The name of the method that was incorrectly /// invoked. /// @@ -104,18 +106,35 @@ protected: + bool Reachable; + const Stmt *From; MapType Map; public: + ConsumedStateMap() : Reachable(true), From(NULL) {} + ConsumedStateMap(const ConsumedStateMap &Other) + : Reachable(Other.Reachable), From(Other.From), Map(Other.Map) {} + /// \brief Get the consumed state of a given variable. ConsumedState getState(const VarDecl *Var); /// \brief Merge this state map with another map. void intersect(const ConsumedStateMap *Other); + /// \brief Return true if this block is reachable. + bool isReachable() const { return Reachable; } + /// \brief Mark all variables as unknown. void makeUnknown(); + /// \brief Mark the block as unreachable. + void markUnreachable(); + + /// \brief Set the source for a decision about the branching of states. + /// \param Source -- The statement that was the origin of a branching + /// decision. + void setSource(const Stmt *Source) { this->From = Source; } + /// \brief Set the consumed state of a given variable. void setState(const VarDecl *Var, ConsumedState State); @@ -144,18 +163,6 @@ void markVisited(const CFGBlock *Block); }; - - struct VarTestResult { - const VarDecl *Var; - SourceLocation Loc; - bool UnconsumedInTrueBranch; - - VarTestResult() : Var(NULL), Loc(), UnconsumedInTrueBranch(true) {} - - VarTestResult(const VarDecl *Var, SourceLocation Loc, - bool UnconsumedInTrueBranch) - : Var(Var), Loc(Loc), UnconsumedInTrueBranch(UnconsumedInTrueBranch) {} - }; /// A class that handles the analysis of uniqueness violations. class ConsumedAnalyzer { @@ -169,7 +176,8 @@ CacheMapType ConsumableTypeCache; bool hasConsumableAttributes(const CXXRecordDecl *RD); - void splitState(const CFGBlock *CurrBlock, const IfStmt *Terminator); + bool splitState(const CFGBlock *CurrBlock, + const ConsumedStmtVisitor &Visitor); public: Index: lib/Analysis/Consumed.cpp =================================================================== --- lib/Analysis/Consumed.cpp +++ lib/Analysis/Consumed.cpp @@ -28,10 +28,16 @@ #include "clang/Basic/SourceLocation.h" #include "llvm/ADT/DenseMap.h" #include "llvm/ADT/SmallVector.h" +#include "llvm/Support/Compiler.h" #include "llvm/Support/raw_ostream.h" +// TODO: Correctly identify unreachable blocks when chaining boolean operators. +// TODO: Warn about unreachable code. +// TODO: Switch to using a bitmap to track unreachable blocks. // TODO: Mark variables as Unknown going into while- or for-loops only if they // are referenced inside that block. (Deferred) +// TODO: Handle variable definitions, e.g. bool valid = x.isValid(); +// if (valid) ...; (Deferred) // TODO: Add a method(s) to identify which method calls perform what state // transitions. (Deferred) // TODO: Take notes on state transitions to provide better warning messages. @@ -47,6 +53,31 @@ // Key method definition ConsumedWarningsHandlerBase::~ConsumedWarningsHandlerBase() {} +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 isKnownState(ConsumedState State) { + switch (State) { + case CS_Unconsumed: + case CS_Consumed: + return true; + case CS_None: + case CS_Unknown: + return false; + } +} + static bool isTestingFunction(const FunctionDecl *FunDecl) { return FunDecl->hasAttr(); } @@ -69,40 +100,143 @@ } namespace { -class ConsumedStmtVisitor : public ConstStmtVisitor { +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_Test, + IT_BinTest, + IT_Var + } InfoType; - union PropagationUnion { + union { ConsumedState State; + VarTestResult Test; const VarDecl *Var; + struct { + const BinaryOperator *Source; + EffectiveOp EOp; + VarTestResult LTest; + VarTestResult RTest; + } BinTest; }; - class PropagationInfo { - PropagationUnion StateOrVar; +public: + PropagationInfo() : InfoType(IT_None) {} - public: - bool IsVar; + PropagationInfo(const VarTestResult &Test) : InfoType(IT_Test), Test(Test) {} + PropagationInfo(const VarDecl *Var, ConsumedState TestsFor) + : InfoType(IT_Test) { - PropagationInfo() : IsVar(false) { - StateOrVar.State = consumed::CS_None; - } + Test.Var = Var; + Test.TestsFor = TestsFor; + } + + PropagationInfo(const BinaryOperator *Source, EffectiveOp EOp, + const VarTestResult <est, const VarTestResult &RTest) + : InfoType(IT_BinTest) { - PropagationInfo(ConsumedState State) : IsVar(false) { - StateOrVar.State = State; - } + 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) { - PropagationInfo(const VarDecl *Var) : IsVar(true) { - StateOrVar.Var = Var; - } + 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) {} + + const ConsumedState & getState() const { + assert(InfoType == IT_State); + return State; + } + + const VarTestResult & getTest() const { + assert(InfoType == IT_Test); + return Test; + } + + 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; + } + + 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 isTest() const { return InfoType == IT_Test; } + bool isBinTest() const { return InfoType == IT_BinTest; } + bool isVar() const { return InfoType == IT_Var; } + + PropagationInfo invertTest() const { + assert(InfoType == IT_Test || InfoType == IT_BinTest); - ConsumedState getState() const { return StateOrVar.State; } + if (InfoType == IT_Test) { + return PropagationInfo(Test.Var, invertConsumedUnconsumed(Test.TestsFor)); - const VarDecl * getVar() const { return IsVar ? StateOrVar.Var : NULL; } - }; + } 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(); + } + } +}; + +class ConsumedStmtVisitor : public ConstStmtVisitor { typedef llvm::DenseMap MapType; typedef std::pair PairType; typedef MapType::iterator InfoEntry; - + typedef MapType::const_iterator ConstInfoEntry; + AnalysisDeclContext &AC; ConsumedAnalyzer &Analyzer; ConsumedStateMap *StateMap; @@ -112,10 +246,11 @@ const FunctionDecl *FunDecl, const CallExpr *Call); void forwardInfo(const Stmt *From, const Stmt *To); + void handleTestingFunctionCall(const CallExpr *Call, const VarDecl *Var); bool isLikeMoveAssignment(const CXXMethodDecl *MethodDecl); public: - + void Visit(const Stmt *StmtNode); void VisitBinaryOperator(const BinaryOperator *BinOp); @@ -131,12 +266,20 @@ void VisitUnaryOperator(const UnaryOperator *UOp); void VisitVarDecl(const VarDecl *Var); - ConsumedStmtVisitor(AnalysisDeclContext &AC, ConsumedAnalyzer &Analyzer, - ConsumedStateMap *StateMap) - : AC(AC), Analyzer(Analyzer), StateMap(StateMap) {} - - void reset() { - PropagationMap.clear(); + ConsumedStmtVisitor(AnalysisDeclContext &AC, ConsumedAnalyzer &Analyzer) + : AC(AC), Analyzer(Analyzer), StateMap(NULL) {} + + PropagationInfo getInfo(const Stmt *StmtNode) const { + ConstInfoEntry Entry = PropagationMap.find(StmtNode); + + if (Entry != PropagationMap.end()) + return Entry->second; + else + return PropagationInfo(); + } + + void reset(ConsumedStateMap *NewStateMap) { + StateMap = NewStateMap; } }; @@ -148,7 +291,7 @@ if (!FunDecl->hasAttr()) return; - if (PInfo.IsVar) { + if (PInfo.isVar()) { const VarDecl *Var = PInfo.getVar(); switch (StateMap->getState(Var)) { @@ -191,9 +334,24 @@ void ConsumedStmtVisitor::forwardInfo(const Stmt *From, const Stmt *To) { InfoEntry Entry = PropagationMap.find(From); - if (Entry != PropagationMap.end()) { - PropagationMap.insert(PairType(To, PropagationInfo(Entry->second))); + if (Entry != PropagationMap.end()) + PropagationMap.insert(PairType(To, Entry->second)); +} + +void ConsumedStmtVisitor::handleTestingFunctionCall(const CallExpr *Call, + const VarDecl *Var) { + + ConsumedState VarState = StateMap->getState(Var); + + if (VarState != CS_Unknown) { + SourceLocation CallLoc = Call->getExprLoc(); + + if (!CallLoc.isMacroID()) + Analyzer.WarningsHandler.warnUnnecessaryTest(Var->getNameAsString(), + stateToString(VarState), CallLoc); } + + PropagationMap.insert(PairType(Call, PropagationInfo(Var, CS_Unconsumed))); } bool ConsumedStmtVisitor::isLikeMoveAssignment( @@ -205,8 +363,49 @@ MethodDecl->getParamDecl(0)->getType()->isRValueReferenceType()); } +void ConsumedStmtVisitor::Visit(const Stmt *StmtNode) { + + ConstStmtVisitor::Visit(StmtNode); + + for (Stmt::const_child_iterator CI = StmtNode->child_begin(), + CE = StmtNode->child_end(); CI != CE; ++CI) { + + PropagationMap.erase(*CI); + } +} + void ConsumedStmtVisitor::VisitBinaryOperator(const BinaryOperator *BinOp) { switch (BinOp->getOpcode()) { + case BO_LAnd: + case BO_LOr : { + InfoEntry LEntry = PropagationMap.find(BinOp->getLHS()), + REntry = PropagationMap.find(BinOp->getRHS()); + + VarTestResult LTest, RTest; + + if (LEntry != PropagationMap.end() && LEntry->second.isTest()) { + LTest = LEntry->second.getTest(); + + } else { + LTest.Var = NULL; + LTest.TestsFor = CS_None; + } + + if (REntry != PropagationMap.end() && REntry->second.isTest()) { + RTest = REntry->second.getTest(); + + } else { + RTest.Var = NULL; + RTest.TestsFor = CS_None; + } + + if (!(LTest.Var == NULL && RTest.Var == NULL)) + PropagationMap.insert(PairType(BinOp, PropagationInfo(BinOp, + static_cast(BinOp->getOpcode() == BO_LOr), LTest, RTest))); + + break; + } + case BO_PtrMemD: case BO_PtrMemI: forwardInfo(BinOp->getLHS(), BinOp); @@ -217,16 +416,6 @@ } } -void ConsumedStmtVisitor::Visit(const Stmt *StmtNode) { - ConstStmtVisitor::Visit(StmtNode); - - for (Stmt::const_child_iterator CI = StmtNode->child_begin(), - CE = StmtNode->child_end(); CI != CE; ++CI) { - - PropagationMap.erase(*CI); - } -} - void ConsumedStmtVisitor::VisitCallExpr(const CallExpr *Call) { if (const FunctionDecl *FunDecl = dyn_cast_or_null(Call->getDirectCallee())) { @@ -250,7 +439,7 @@ InfoEntry Entry = PropagationMap.find(Call->getArg(Index)); - if (Entry == PropagationMap.end() || !Entry->second.IsVar) { + if (Entry == PropagationMap.end() || !Entry->second.isVar()) { continue; } @@ -274,10 +463,7 @@ } void ConsumedStmtVisitor::VisitCastExpr(const CastExpr *Cast) { - InfoEntry Entry = PropagationMap.find(Cast->getSubExpr()); - - if (Entry != PropagationMap.end()) - PropagationMap.insert(PairType(Cast, Entry->second)); + forwardInfo(Cast->getSubExpr(), Cast); } void ConsumedStmtVisitor::VisitCXXConstructExpr(const CXXConstructExpr *Call) { @@ -298,7 +484,7 @@ PropagationInfo PInfo = PropagationMap.find(Call->getArg(0))->second; - if (PInfo.IsVar) { + if (PInfo.isVar()) { const VarDecl* Var = PInfo.getVar(); PropagationMap.insert(PairType(Call, @@ -336,8 +522,10 @@ checkCallability(PInfo, MethodDecl, Call); - if (PInfo.IsVar) { - if (MethodDecl->hasAttr()) + if (PInfo.isVar()) { + if (isTestingFunction(MethodDecl)) + handleTestingFunctionCall(Call, PInfo.getVar()); + else if (MethodDecl->hasAttr()) StateMap->setState(PInfo.getVar(), consumed::CS_Consumed); else if (!MethodDecl->isConst()) StateMap->setState(PInfo.getVar(), consumed::CS_Unknown); @@ -367,7 +555,7 @@ LPInfo = LEntry->second; RPInfo = REntry->second; - if (LPInfo.IsVar && RPInfo.IsVar) { + if (LPInfo.isVar() && RPInfo.isVar()) { StateMap->setState(LPInfo.getVar(), StateMap->getState(RPInfo.getVar())); @@ -375,12 +563,12 @@ PropagationMap.insert(PairType(Call, LPInfo)); - } else if (LPInfo.IsVar && !RPInfo.IsVar) { + } else if (LPInfo.isVar() && !RPInfo.isVar()) { StateMap->setState(LPInfo.getVar(), RPInfo.getState()); PropagationMap.insert(PairType(Call, LPInfo)); - } else if (!LPInfo.IsVar && RPInfo.IsVar) { + } else if (!LPInfo.isVar() && RPInfo.isVar()) { PropagationMap.insert(PairType(Call, PropagationInfo(StateMap->getState(RPInfo.getVar())))); @@ -395,7 +583,7 @@ LPInfo = LEntry->second; - if (LPInfo.IsVar) { + if (LPInfo.isVar()) { StateMap->setState(LPInfo.getVar(), consumed::CS_Unknown); PropagationMap.insert(PairType(Call, LPInfo)); @@ -410,7 +598,7 @@ RPInfo = REntry->second; - if (RPInfo.IsVar) { + if (RPInfo.isVar()) { const VarDecl *Var = RPInfo.getVar(); PropagationMap.insert(PairType(Call, @@ -434,14 +622,16 @@ checkCallability(PInfo, FunDecl, Call); - if (PInfo.IsVar) { - if (FunDecl->hasAttr()) { - // Handle consuming operators. + if (PInfo.isVar()) { + if (isTestingFunction(FunDecl)) { + handleTestingFunctionCall(Call, PInfo.getVar()); + + } else if (FunDecl->hasAttr()) { StateMap->setState(PInfo.getVar(), consumed::CS_Consumed); + } else if (const CXXMethodDecl *MethodDecl = - dyn_cast_or_null(FunDecl)) { + dyn_cast_or_null(FunDecl)) { - // Handle non-constant member operators. if (!MethodDecl->isConst()) StateMap->setState(PInfo.getVar(), consumed::CS_Unknown); } @@ -482,11 +672,21 @@ } void ConsumedStmtVisitor::VisitUnaryOperator(const UnaryOperator *UOp) { - if (UOp->getOpcode() == UO_AddrOf) { - InfoEntry Entry = PropagationMap.find(UOp->getSubExpr()); - - if (Entry != PropagationMap.end()) - PropagationMap.insert(PairType(UOp, Entry->second)); + InfoEntry Entry = PropagationMap.find(UOp->getSubExpr()->IgnoreParens()); + if (Entry == PropagationMap.end()) return; + + switch (UOp->getOpcode()) { + case UO_AddrOf: + PropagationMap.insert(PairType(UOp, Entry->second)); + break; + + case UO_LNot: + if (Entry->second.isTest() || Entry->second.isBinTest()) + PropagationMap.insert(PairType(UOp, Entry->second.invertTest())); + break; + + default: + break; } } @@ -495,74 +695,97 @@ PropagationInfo PInfo = PropagationMap.find(Var->getInit())->second; - StateMap->setState(Var, PInfo.IsVar ? + StateMap->setState(Var, PInfo.isVar() ? StateMap->getState(PInfo.getVar()) : PInfo.getState()); } } -} // end anonymous::ConsumedStmtVisitor +}} // end clang::consumed::ConsumedStmtVisitor -namespace { +namespace clang { +namespace consumed { -// TODO: Handle variable definitions, e.g. bool valid = x.isValid(); -// if (valid) ...; (Deferred) -class TestedVarsVisitor : public RecursiveASTVisitor { - - bool Invert; - SourceLocation CurrTestLoc; - - ConsumedStateMap *StateMap; - -public: - bool IsUsefulConditional; - VarTestResult Test; +void splitVarStateForIf(const IfStmt * IfNode, const VarTestResult &Test, + ConsumedStateMap *ThenStates, + ConsumedStateMap *ElseStates) { + + ConsumedState VarState = ThenStates->getState(Test.Var); - TestedVarsVisitor(ConsumedStateMap *StateMap) : Invert(false), - StateMap(StateMap), IsUsefulConditional(false) {} + if (VarState == CS_Unknown) { + ThenStates->setState(Test.Var, Test.TestsFor); + if (ElseStates) + ElseStates->setState(Test.Var, invertConsumedUnconsumed(Test.TestsFor)); - bool VisitCallExpr(CallExpr *Call); - bool VisitDeclRefExpr(DeclRefExpr *DeclRef); - bool VisitUnaryOperator(UnaryOperator *UnaryOp); -}; - -bool TestedVarsVisitor::VisitCallExpr(CallExpr *Call) { - if (const FunctionDecl *FunDecl = - dyn_cast_or_null(Call->getDirectCallee())) { - - if (isTestingFunction(FunDecl)) { - CurrTestLoc = Call->getExprLoc(); - IsUsefulConditional = true; - return true; - } + } else if (VarState == invertConsumedUnconsumed(Test.TestsFor)) { + ThenStates->markUnreachable(); - IsUsefulConditional = false; + } else if (VarState == Test.TestsFor && ElseStates) { + ElseStates->markUnreachable(); } - - return false; } -bool TestedVarsVisitor::VisitDeclRefExpr(DeclRefExpr *DeclRef) { - if (const VarDecl *Var = dyn_cast_or_null(DeclRef->getDecl())) - if (StateMap->getState(Var) != consumed::CS_None) - Test = VarTestResult(Var, CurrTestLoc, !Invert); +void splitVarStateForIfBinOp(const PropagationInfo &PInfo, + ConsumedStateMap *ThenStates, ConsumedStateMap *ElseStates) { - return true; -} - -bool TestedVarsVisitor::VisitUnaryOperator(UnaryOperator *UnaryOp) { - if (UnaryOp->getOpcode() == UO_LNot) { - Invert = true; - TraverseStmt(UnaryOp->getSubExpr()); - - } else { - IsUsefulConditional = false; + 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) { + if (ElseStates) + ElseStates->markUnreachable(); + } else { + ThenStates->markUnreachable(); + } + } + + } else { + if (LState == CS_Unknown && ElseStates) { + ElseStates->setState(LTest.Var, + invertConsumedUnconsumed(LTest.TestsFor)); + + } else if (LState == LTest.TestsFor && ElseStates) { + ElseStates->markUnreachable(); + + } else if (LState == invertConsumedUnconsumed(LTest.TestsFor) && + isKnownState(RState)) { + + if (RState == RTest.TestsFor) { + if (ElseStates) + ElseStates->markUnreachable(); + } else { + ThenStates->markUnreachable(); + } + } + } } - return false; + 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 (ElseStates) { + if (RState == CS_Unknown) + ElseStates->setState(RTest.Var, + invertConsumedUnconsumed(RTest.TestsFor)); + else if (RState == RTest.TestsFor) + ElseStates->markUnreachable(); + } + } } -} // end anonymouse::TestedVarsVisitor - -namespace clang { -namespace consumed { void ConsumedBlockInfo::addInfo(const CFGBlock *Block, ConsumedStateMap *StateMap, @@ -625,26 +848,34 @@ void ConsumedStateMap::intersect(const ConsumedStateMap *Other) { ConsumedState LocalState; + if (this->From && this->From == Other->From && !Other->Reachable) { + this->markUnreachable(); + return; + } + for (MapType::const_iterator DMI = Other->Map.begin(), DME = Other->Map.end(); DMI != DME; ++DMI) { LocalState = this->getState(DMI->first); - if (LocalState != CS_None && LocalState != DMI->second) - setState(DMI->first, CS_Unknown); + if (LocalState == CS_None) + continue; + + if (LocalState != DMI->second) + Map[DMI->first] = CS_Unknown; } } +void ConsumedStateMap::markUnreachable() { + this->Reachable = false; + Map.clear(); +} + void ConsumedStateMap::makeUnknown() { - PairType Pair; - for (MapType::const_iterator DMI = Map.begin(), DME = Map.end(); DMI != DME; ++DMI) { - Pair = *DMI; - - Map.erase(Pair.first); - Map.insert(PairType(Pair.first, CS_Unknown)); + Map[DMI->first] = CS_Unknown; } } @@ -694,42 +925,98 @@ return false; } -// TODO: Handle other forms of branching with precision, including while- and -// for-loops. (Deferred) -void ConsumedAnalyzer::splitState(const CFGBlock *CurrBlock, - const IfStmt *Terminator) { - - TestedVarsVisitor Visitor(CurrStates); - Visitor.TraverseStmt(const_cast(Terminator->getCond())); +bool ConsumedAnalyzer::splitState(const CFGBlock *CurrBlock, + const ConsumedStmtVisitor &Visitor) { - bool HasElse = Terminator->getElse() != NULL; + ConsumedStateMap *FalseStates = new ConsumedStateMap(*CurrStates); + PropagationInfo PInfo; - ConsumedStateMap *ElseOrMergeStates = new ConsumedStateMap(*CurrStates); - - if (Visitor.IsUsefulConditional) { - ConsumedState VarState = CurrStates->getState(Visitor.Test.Var); + if (const IfStmt *IfNode = + dyn_cast_or_null(CurrBlock->getTerminator().getStmt())) { - if (VarState != CS_Unknown) { - // FIXME: Make this not warn if the test is from a macro expansion. - // (Deferred) - WarningsHandler.warnUnnecessaryTest(Visitor.Test.Var->getNameAsString(), - stateToString(VarState), Visitor.Test.Loc); - } + bool HasElse = IfNode->getElse() != NULL; + const Stmt *Cond = IfNode->getCond(); + + PInfo = Visitor.getInfo(Cond); + if (!PInfo.isValid() && isa(Cond)) + PInfo = Visitor.getInfo(cast(Cond)->getRHS()); - if (Visitor.Test.UnconsumedInTrueBranch) { - CurrStates->setState(Visitor.Test.Var, CS_Unconsumed); - if (HasElse) ElseOrMergeStates->setState(Visitor.Test.Var, CS_Consumed); + if (PInfo.isTest()) { + CurrStates->setSource(Cond); + FalseStates->setSource(Cond); + + splitVarStateForIf(IfNode, PInfo.getTest(), CurrStates, + HasElse ? FalseStates : NULL); + + } else if (PInfo.isBinTest()) { + CurrStates->setSource(PInfo.testSourceNode()); + FalseStates->setSource(PInfo.testSourceNode()); + + splitVarStateForIfBinOp(PInfo, CurrStates, HasElse ? FalseStates : NULL); } else { - CurrStates->setState(Visitor.Test.Var, CS_Consumed); - if (HasElse) ElseOrMergeStates->setState(Visitor.Test.Var, CS_Unconsumed); + delete FalseStates; + return false; } - } + } else if (const BinaryOperator *BinOp = + dyn_cast_or_null(CurrBlock->getTerminator().getStmt())) { + + PInfo = Visitor.getInfo(BinOp->getLHS()); + if (!PInfo.isTest()) { + if ((BinOp = dyn_cast_or_null(BinOp->getLHS()))) { + PInfo = Visitor.getInfo(BinOp->getRHS()); + + if (!PInfo.isTest()) { + delete FalseStates; + return false; + } + + } else { + delete FalseStates; + return false; + } + } + + CurrStates->setSource(BinOp); + FalseStates->setSource(BinOp); + + const VarTestResult &Test = PInfo.getTest(); + 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 { + delete FalseStates; + return false; + } + CFGBlock::const_succ_iterator SI = CurrBlock->succ_begin(); - if (*SI) BlockInfo.addInfo(*SI, CurrStates); - if (*++SI) BlockInfo.addInfo(*SI, ElseOrMergeStates); + if (*SI) + BlockInfo.addInfo(*SI, CurrStates); + else + delete CurrStates; + + if (*++SI) + BlockInfo.addInfo(*SI, FalseStates); + else + delete FalseStates; + + CurrStates = NULL; + return true; } void ConsumedAnalyzer::run(AnalysisDeclContext &AC) { @@ -742,6 +1029,7 @@ PostOrderCFGView *SortedGraph = AC.getAnalysis(); CurrStates = new ConsumedStateMap(); + ConsumedStmtVisitor Visitor(AC, *this); // Visit all of the function's basic blocks. for (PostOrderCFGView::iterator I = SortedGraph->begin(), @@ -752,9 +1040,18 @@ if (CurrStates == NULL) CurrStates = BlockInfo.getInfo(CurrBlock); - - ConsumedStmtVisitor Visitor(AC, *this, CurrStates); - + + if (!CurrStates) { + continue; + + } else if (!CurrStates->isReachable()) { + delete CurrStates; + CurrStates = NULL; + continue; + } + + Visitor.reset(CurrStates); + // Visit all of the basic block's statements. for (CFGBlock::const_iterator BI = CurrBlock->begin(), BE = CurrBlock->end(); BI != BE; ++BI) { @@ -770,36 +1067,34 @@ } } - if (const IfStmt *Terminator = - dyn_cast_or_null(CurrBlock->getTerminator().getStmt())) { - - splitState(CurrBlock, Terminator); - CurrStates = NULL; - - } else if (CurrBlock->succ_size() > 1) { - CurrStates->makeUnknown(); - - bool OwnershipTaken = false; + // TODO: Handle other forms of branching with precision, including while- + // and for-loops. (Deferred) + if (!splitState(CurrBlock, Visitor)) { + CurrStates->setSource(NULL); - for (CFGBlock::const_succ_iterator SI = CurrBlock->succ_begin(), - SE = CurrBlock->succ_end(); SI != SE; ++SI) { + if (CurrBlock->succ_size() > 1) { + CurrStates->makeUnknown(); - if (*SI) BlockInfo.addInfo(*SI, CurrStates, OwnershipTaken); + bool OwnershipTaken = false; + + for (CFGBlock::const_succ_iterator SI = CurrBlock->succ_begin(), + SE = CurrBlock->succ_end(); SI != SE; ++SI) { + + if (*SI) BlockInfo.addInfo(*SI, CurrStates, OwnershipTaken); + } + + if (!OwnershipTaken) + delete CurrStates; + + CurrStates = NULL; + + } else if (CurrBlock->succ_size() == 1 && + (*CurrBlock->succ_begin())->pred_size() > 1) { + + BlockInfo.addInfo(*CurrBlock->succ_begin(), CurrStates); + CurrStates = NULL; } - - if (!OwnershipTaken) - delete CurrStates; - - CurrStates = NULL; - - } else if (CurrBlock->succ_size() == 1 && - (*CurrBlock->succ_begin())->pred_size() > 1) { - - BlockInfo.addInfo(*CurrBlock->succ_begin(), CurrStates); - CurrStates = NULL; } - - Visitor.reset(); } // End of block iterator. // Delete the last existing state map. Index: test/SemaCXX/warn-consumed-analysis-strict.cpp =================================================================== --- test/SemaCXX/warn-consumed-analysis-strict.cpp +++ test/SemaCXX/warn-consumed-analysis-strict.cpp @@ -4,6 +4,8 @@ #define CONSUMES __attribute__ ((consumes)) #define TESTS_UNCONSUMED __attribute__ ((tests_unconsumed)) +#define TEST_VAR(Var) Var.isValid() + typedef decltype(nullptr) nullptr_t; template @@ -11,7 +13,7 @@ T var; public: - ConsumableClass(void); + ConsumableClass(); ConsumableClass(T val); ConsumableClass(ConsumableClass &other); ConsumableClass(ConsumableClass &&other); @@ -26,20 +28,24 @@ template ConsumableClass& operator=(ConsumableClass &&other); - void operator*(void) const CALLABLE_WHEN_UNCONSUMED; + void operator()(int a) CONSUMES; + void operator*() const CALLABLE_WHEN_UNCONSUMED; + void unconsumedCall() const CALLABLE_WHEN_UNCONSUMED; - bool isValid(void) const TESTS_UNCONSUMED; + bool isValid() const TESTS_UNCONSUMED; + operator bool() const TESTS_UNCONSUMED; + bool operator!=(nullptr_t) const TESTS_UNCONSUMED; - void constCall(void) const; - void nonconstCall(void); + void constCall() const; + void nonconstCall(); - void consume(void) CONSUMES; + void consume() CONSUMES; }; void baf0(ConsumableClass &var); void baf1(ConsumableClass *var); -void testIfStmt(void) { +void testIfStmt() { ConsumableClass var; if (var.isValid()) { // expected-warning {{unnecessary test. Variable 'var' is known to be in the 'consumed' state}} @@ -51,27 +57,113 @@ } } -void testConditionalMerge(void) { - ConsumableClass var; +void testComplexConditionals() { + ConsumableClass var0, var1, var2; + + // Coerce all variables into the unknown state. + baf0(var0); + baf0(var1); + baf0(var2); - if (var.isValid()) {// expected-warning {{unnecessary test. Variable 'var' is known to be in the 'consumed' state}} + if (var0 && var1) { + *var0; + *var1; - // Empty + } else { + *var0; // expected-warning {{invocation of method 'operator*' on object 'var0' while it is in an unknown state}} + *var1; // expected-warning {{invocation of method 'operator*' on object 'var1' while it is in an unknown state}} } - *var; // expected-warning {{invocation of method 'operator*' on object 'var' while it is in an unknown state}} + if (var0 || var1) { + *var0; // expected-warning {{invocation of method 'operator*' on object 'var0' while it is in an unknown state}} + *var1; // expected-warning {{invocation of method 'operator*' on object 'var1' while it is in an unknown state}} + + } else { + *var0; // expected-warning {{invocation of method 'operator*' on object 'var0' while it is in the 'consumed' state}} + *var1; // expected-warning {{invocation of method 'operator*' on object 'var1' while it is in the 'consumed' state}} + } - if (var.isValid()) { - // Empty + if (var0 && !var1) { + *var0; + *var1; // expected-warning {{invocation of method 'operator*' on object 'var1' while it is in the 'consumed' state}} } else { - // Empty + *var0; // expected-warning {{invocation of method 'operator*' on object 'var0' while it is in an unknown state}} + *var1; // expected-warning {{invocation of method 'operator*' on object 'var1' while it is in an unknown state}} } - *var; // expected-warning {{invocation of method 'operator*' on object 'var' while it is in an unknown state}} + if (var0 || !var1) { + *var0; // expected-warning {{invocation of method 'operator*' on object 'var0' while it is in an unknown state}} + *var1; // expected-warning {{invocation of method 'operator*' on object 'var1' while it is in an unknown state}} + + } else { + *var0; // expected-warning {{invocation of method 'operator*' on object 'var0' while it is in the 'consumed' state}} + *var1; + } + + if (!var0 && !var1) { + *var0; // expected-warning {{invocation of method 'operator*' on object 'var0' while it is in the 'consumed' state}} + *var1; // expected-warning {{invocation of method 'operator*' on object 'var1' while it is in the 'consumed' state}} + + } else { + *var0; // expected-warning {{invocation of method 'operator*' on object 'var0' while it is in an unknown state}} + *var1; // expected-warning {{invocation of method 'operator*' on object 'var1' while it is in an unknown state}} + } + + if (!(var0 || var1)) { + *var0; // expected-warning {{invocation of method 'operator*' on object 'var0' while it is in the 'consumed' state}} + *var1; // expected-warning {{invocation of method 'operator*' on object 'var1' while it is in the 'consumed' state}} + + } else { + *var0; // expected-warning {{invocation of method 'operator*' on object 'var0' while it is in an unknown state}} + *var1; // expected-warning {{invocation of method 'operator*' on object 'var1' while it is in an unknown state}} + } + + if (!var0 || !var1) { + *var0; // expected-warning {{invocation of method 'operator*' on object 'var0' while it is in an unknown state}} + *var1; // expected-warning {{invocation of method 'operator*' on object 'var1' while it is in an unknown state}} + + } else { + *var0; + *var1; + } + + if (!(var0 && var1)) { + *var0; // expected-warning {{invocation of method 'operator*' on object 'var0' while it is in an unknown state}} + *var1; // expected-warning {{invocation of method 'operator*' on object 'var1' while it is in an unknown state}} + + } else { + *var0; + *var1; + } + + if (var0 && var1 && var2) { + *var0; + *var1; + *var2; + + } else { + *var0; // expected-warning {{invocation of method 'operator*' on object 'var0' while it is in an unknown state}} + *var1; // expected-warning {{invocation of method 'operator*' on object 'var1' while it is in an unknown state}} + *var2; // expected-warning {{invocation of method 'operator*' on object 'var2' while it is in an unknown state}} + } + +#if 0 + // FIXME: Get this test to pass. + if (var0 || var1 || var2) { + *var0; // expected-warning {{invocation of method 'operator*' on object 'var0' while it is in an unknown state}} + *var1; // expected-warning {{invocation of method 'operator*' on object 'var1' while it is in an unknown state}} + *var2; // expected-warning {{invocation of method 'operator*' on object 'var2' while it is in an unknown state}} + + } else { + *var0; // expected-warning {{invocation of method 'operator*' on object 'var0' while it is in the 'consumed' state}} + *var1; // expected-warning {{invocation of method 'operator*' on object 'var1' while it is in the 'consumed' state}} + *var2; // expected-warning {{invocation of method 'operator*' on object 'var2' while it is in the 'consumed' state}} + } +#endif } -void testCallingConventions(void) { +void testCallingConventions() { ConsumableClass var(42); baf0(var); @@ -82,14 +174,14 @@ *var; // expected-warning {{invocation of method 'operator*' on object 'var' while it is in an unknown state}} } -void testMoveAsignmentish(void) { +void testMoveAsignmentish() { ConsumableClass var; var = nullptr; *var; // expected-warning {{invocation of method 'operator*' on object 'var' while it is in an unknown state}} } -void testConstAndNonConstMemberFunctions(void) { +void testConstAndNonConstMemberFunctions() { ConsumableClass var(42); var.constCall(); @@ -99,7 +191,15 @@ *var; // expected-warning {{invocation of method 'operator*' on object 'var' while it is in an unknown state}} } -void testSimpleForLoop(void) { +void testNoWarnTestFromMacroExpansion() { + ConsumableClass var(42); + + if (TEST_VAR(var)) { + *var; + } +} + +void testSimpleForLoop() { ConsumableClass var; for (int i = 0; i < 10; ++i) { @@ -109,7 +209,7 @@ *var; // expected-warning {{invocation of method 'operator*' on object 'var' while it is in an unknown state}} } -void testSimpleWhileLoop(void) { +void testSimpleWhileLoop() { int i = 0; ConsumableClass var; Index: test/SemaCXX/warn-consumed-analysis.cpp =================================================================== --- test/SemaCXX/warn-consumed-analysis.cpp +++ test/SemaCXX/warn-consumed-analysis.cpp @@ -11,7 +11,7 @@ T var; public: - ConsumableClass(void); + ConsumableClass(); ConsumableClass(nullptr_t p) CONSUMES; ConsumableClass(T val); ConsumableClass(ConsumableClass &other); @@ -28,17 +28,17 @@ ConsumableClass& operator=(ConsumableClass &&other); void operator()(int a) CONSUMES; - void operator*(void) const CALLABLE_WHEN_UNCONSUMED; - void unconsumedCall(void) const CALLABLE_WHEN_UNCONSUMED; + void operator*() const CALLABLE_WHEN_UNCONSUMED; + void unconsumedCall() const CALLABLE_WHEN_UNCONSUMED; - bool isValid(void) const TESTS_UNCONSUMED; + bool isValid() const TESTS_UNCONSUMED; operator bool() const TESTS_UNCONSUMED; bool operator!=(nullptr_t) const TESTS_UNCONSUMED; - void constCall(void) const; - void nonconstCall(void); + void constCall() const; + void nonconstCall(); - void consume(void) CONSUMES; + void consume() CONSUMES; }; void baf0(const ConsumableClass var); @@ -47,7 +47,7 @@ void baf3(ConsumableClass &&var); -void testInitialization(void) { +void testInitialization() { ConsumableClass var0; ConsumableClass var1 = ConsumableClass(); @@ -58,10 +58,10 @@ if (var0.isValid()) { *var0; - *var1; // expected-warning {{invocation of method 'operator*' on object 'var1' while it is in the 'consumed' state}} + *var1; } else { - *var0; // expected-warning {{invocation of method 'operator*' on object 'var0' while it is in the 'consumed' state}} + *var0; // expected-warning {{invocation of method 'operator*' on object 'var0' while it is in the 'consumed' state}} } } @@ -69,7 +69,7 @@ *ConsumableClass(); // expected-warning {{invocation of method 'operator*' on a temporary object while it is in the 'consumed' state}} } -void testSimpleRValueRefs(void) { +void testSimpleRValueRefs() { ConsumableClass var0; ConsumableClass var1(42); @@ -82,39 +82,149 @@ *var1; // expected-warning {{invocation of method 'operator*' on object 'var1' while it is in the 'consumed' state}} } -void testIfStmt(void) { +void testIfStmt() { ConsumableClass var; if (var.isValid()) { - // Empty - + *var; } else { *var; // expected-warning {{invocation of method 'operator*' on object 'var' while it is in the 'consumed' state}} } if (!var.isValid()) { *var; // expected-warning {{invocation of method 'operator*' on object 'var' while it is in the 'consumed' state}} - } else { *var; } if (var) { // Empty - } else { *var; // expected-warning {{invocation of method 'operator*' on object 'var' while it is in the 'consumed' state}} } if (var != nullptr) { // Empty - } else { *var; // expected-warning {{invocation of method 'operator*' on object 'var' while it is in the 'consumed' state}} } } -void testCallingConventions(void) { +void testComplexConditionals() { + ConsumableClass var0, var1, var2; + + if (var0 && var1) { + *var0; + *var1; + + } else { + *var0; // expected-warning {{invocation of method 'operator*' on object 'var0' while it is in the 'consumed' state}} + *var1; // expected-warning {{invocation of method 'operator*' on object 'var1' while it is in the 'consumed' state}} + } + + if (var0 || var1) { + *var0; + *var1; + + } else { + *var0; // expected-warning {{invocation of method 'operator*' on object 'var0' while it is in the 'consumed' state}} + *var1; // expected-warning {{invocation of method 'operator*' on object 'var1' while it is in the 'consumed' state}} + } + + if (var0 && !var1) { + *var0; + *var1; + + } else { + *var0; // expected-warning {{invocation of method 'operator*' on object 'var0' while it is in the 'consumed' state}} + *var1; // expected-warning {{invocation of method 'operator*' on object 'var1' while it is in the 'consumed' state}} + } + + if (var0 || !var1) { + *var0; // expected-warning {{invocation of method 'operator*' on object 'var0' while it is in the 'consumed' state}} + *var1; // expected-warning {{invocation of method 'operator*' on object 'var1' while it is in the 'consumed' state}} + + } else { + *var0; + *var1; + } + + if (!var0 && !var1) { + *var0; // expected-warning {{invocation of method 'operator*' on object 'var0' while it is in the 'consumed' state}} + *var1; // expected-warning {{invocation of method 'operator*' on object 'var1' while it is in the 'consumed' state}} + + } else { + *var0; + *var1; + } + + if (!var0 || !var1) { + *var0; // expected-warning {{invocation of method 'operator*' on object 'var0' while it is in the 'consumed' state}} + *var1; // expected-warning {{invocation of method 'operator*' on object 'var1' while it is in the 'consumed' state}} + + } else { + *var0; + *var1; + } + + if (!(var0 && var1)) { + *var0; // expected-warning {{invocation of method 'operator*' on object 'var0' while it is in the 'consumed' state}} + *var1; // expected-warning {{invocation of method 'operator*' on object 'var1' while it is in the 'consumed' state}} + + } else { + *var0; + *var1; + } + + if (!(var0 || var1)) { + *var0; // expected-warning {{invocation of method 'operator*' on object 'var0' while it is in the 'consumed' state}} + *var1; // expected-warning {{invocation of method 'operator*' on object 'var1' while it is in the 'consumed' state}} + + } else { + *var0; + *var1; + } + + if (var0 && var1 && var2) { + *var0; + *var1; + *var2; + + } else { + *var0; // expected-warning {{invocation of method 'operator*' on object 'var0' while it is in the 'consumed' state}} + *var1; // expected-warning {{invocation of method 'operator*' on object 'var1' while it is in the 'consumed' state}} + *var2; // expected-warning {{invocation of method 'operator*' on object 'var2' while it is in the 'consumed' state}} + } + +#if 0 + // FIXME: Get this test to pass. + if (var0 || var1 || var2) { + *var0; + *var1; + *var2; + + } else { + *var0; // expected-warning {{invocation of method 'operator*' on object 'var0' while it is in the 'consumed' state}} + *var1; // expected-warning {{invocation of method 'operator*' on object 'var1' while it is in the 'consumed' state}} + *var2; // expected-warning {{invocation of method 'operator*' on object 'var2' while it is in the 'consumed' state}} + } +#endif +} + +void testStateChangeInBranch() { + ConsumableClass var; + + // Make var enter the 'unknown' state. + baf1(var); + + if (!var) { + var = ConsumableClass(42); + } + + *var; +} + +void testCallingConventions() { ConsumableClass var(42); baf0(var); @@ -130,7 +240,7 @@ *var; // expected-warning {{invocation of method 'operator*' on object 'var' while it is in the 'consumed' state}} } -void testMoveAsignmentish(void) { +void testMoveAsignmentish() { ConsumableClass var0; ConsumableClass var1(42); @@ -143,26 +253,25 @@ *var1; // expected-warning {{invocation of method 'operator*' on object 'var1' while it is in the 'consumed' state}} } -void testConditionalMerge(void) { +void testConditionalMerge() { ConsumableClass var; if (var.isValid()) { // Empty } - *var; + *var; // expected-warning {{invocation of method 'operator*' on object 'var' while it is in the 'consumed' state}} if (var.isValid()) { // Empty - } else { // Empty } - *var; + *var; // expected-warning {{invocation of method 'operator*' on object 'var' while it is in the 'consumed' state}} } -void testConsumes0(void) { +void testConsumes0() { ConsumableClass var(42); *var; @@ -172,13 +281,13 @@ *var; // expected-warning {{invocation of method 'operator*' on object 'var' while it is in the 'consumed' state}} } -void testConsumes1(void) { +void testConsumes1() { ConsumableClass var(nullptr); *var; // expected-warning {{invocation of method 'operator*' on object 'var' while it is in the 'consumed' state}} } -void testConsumes2(void) { +void testConsumes2() { ConsumableClass var(42); var.unconsumedCall(); @@ -187,7 +296,19 @@ var.unconsumedCall(); // expected-warning {{invocation of method 'unconsumedCall' on object 'var' while it is in the 'consumed' state}} } -void testSimpleForLoop(void) { +void testNonsenseState() { + ConsumableClass var(42); + + if (var) { + *var; + } else { + *var; + } + + *var; +} + +void testSimpleForLoop() { ConsumableClass var; for (int i = 0; i < 10; ++i) { @@ -197,7 +318,7 @@ *var; } -void testSimpleWhileLoop(void) { +void testSimpleWhileLoop() { int i = 0; ConsumableClass var;