r189594 - Consumed analysis: improve handling of conditionals.
DeLesley Hutchins
delesley at google.com
Thu Aug 29 10:26:58 PDT 2013
Author: delesley
Date: Thu Aug 29 12:26:57 2013
New Revision: 189594
URL: http://llvm.org/viewvc/llvm-project?rev=189594&view=rev
Log:
Consumed analysis: improve handling of conditionals.
Patch by chris.wailes at gmail.com.
* The TestedVarsVisitor was folded into the ConsumedStmtVisitor.
* The VarTestResult class was updated to allow these changes.
* The PropagationInfo class was updated for the same reasons.
* Correctly handle short-circuiting of Boolean operations.
* Blocks are now marked as unreachable when we can statically prove we will
never branch to them.
* Unreachable blocks are skipped by the analysis.
Modified:
cfe/trunk/include/clang/Analysis/Analyses/Consumed.h
cfe/trunk/lib/Analysis/Consumed.cpp
cfe/trunk/test/SemaCXX/warn-consumed-analysis-strict.cpp
cfe/trunk/test/SemaCXX/warn-consumed-analysis.cpp
Modified: cfe/trunk/include/clang/Analysis/Analyses/Consumed.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Analysis/Analyses/Consumed.h?rev=189594&r1=189593&r2=189594&view=diff
==============================================================================
--- cfe/trunk/include/clang/Analysis/Analyses/Consumed.h (original)
+++ cfe/trunk/include/clang/Analysis/Analyses/Consumed.h Thu Aug 29 12:26:57 2013
@@ -24,7 +24,9 @@
namespace clang {
namespace consumed {
-
+
+ class ConsumedStmtVisitor;
+
typedef SmallVector<PartialDiagnosticAt, 1> OptionalNotes;
typedef std::pair<PartialDiagnosticAt, OptionalNotes> DelayedDiag;
typedef std::list<DelayedDiag> DiagList;
@@ -38,7 +40,7 @@ namespace consumed {
/// \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 @@ namespace consumed {
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 @@ namespace consumed {
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 @@ namespace consumed {
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 @@ namespace consumed {
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 @@ namespace consumed {
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 @@ namespace consumed {
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 @@ namespace consumed {
CacheMapType ConsumableTypeCache;
bool hasConsumableAttributes(const CXXRecordDecl *RD);
- void splitState(const CFGBlock *CurrBlock, const IfStmt *Terminator);
+ bool splitState(const CFGBlock *CurrBlock,
+ const ConsumedStmtVisitor &Visitor);
public:
Modified: cfe/trunk/lib/Analysis/Consumed.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Analysis/Consumed.cpp?rev=189594&r1=189593&r2=189594&view=diff
==============================================================================
--- cfe/trunk/lib/Analysis/Consumed.cpp (original)
+++ cfe/trunk/lib/Analysis/Consumed.cpp Thu Aug 29 12:26:57 2013
@@ -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 @@ using namespace consumed;
// 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<TestsUnconsumedAttr>();
}
@@ -69,40 +100,143 @@ static StringRef stateToString(ConsumedS
}
namespace {
-class ConsumedStmtVisitor : public ConstStmtVisitor<ConsumedStmtVisitor> {
+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<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;
@@ -112,10 +246,11 @@ class ConsumedStmtVisitor : public Const
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 @@ public:
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 @@ void ConsumedStmtVisitor::checkCallabili
if (!FunDecl->hasAttr<CallableWhenUnconsumedAttr>()) return;
- if (PInfo.IsVar) {
+ if (PInfo.isVar()) {
const VarDecl *Var = PInfo.getVar();
switch (StateMap->getState(Var)) {
@@ -191,9 +334,24 @@ void ConsumedStmtVisitor::checkCallabili
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 @@ bool ConsumedStmtVisitor::isLikeMoveAssi
MethodDecl->getParamDecl(0)->getType()->isRValueReferenceType());
}
+void ConsumedStmtVisitor::Visit(const Stmt *StmtNode) {
+
+ ConstStmtVisitor<ConsumedStmtVisitor>::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<EffectiveOp>(BinOp->getOpcode() == BO_LOr), LTest, RTest)));
+
+ break;
+ }
+
case BO_PtrMemD:
case BO_PtrMemI:
forwardInfo(BinOp->getLHS(), BinOp);
@@ -217,16 +416,6 @@ void ConsumedStmtVisitor::VisitBinaryOpe
}
}
-void ConsumedStmtVisitor::Visit(const Stmt *StmtNode) {
- ConstStmtVisitor<ConsumedStmtVisitor>::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<FunctionDecl>(Call->getDirectCallee())) {
@@ -250,7 +439,7 @@ void ConsumedStmtVisitor::VisitCallExpr(
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::VisitCallExpr(
}
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 @@ void ConsumedStmtVisitor::VisitCXXConstr
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 @@ void ConsumedStmtVisitor::VisitCXXMember
checkCallability(PInfo, MethodDecl, Call);
- if (PInfo.IsVar) {
- if (MethodDecl->hasAttr<ConsumesAttr>())
+ if (PInfo.isVar()) {
+ if (isTestingFunction(MethodDecl))
+ handleTestingFunctionCall(Call, PInfo.getVar());
+ else if (MethodDecl->hasAttr<ConsumesAttr>())
StateMap->setState(PInfo.getVar(), consumed::CS_Consumed);
else if (!MethodDecl->isConst())
StateMap->setState(PInfo.getVar(), consumed::CS_Unknown);
@@ -367,7 +555,7 @@ void ConsumedStmtVisitor::VisitCXXOperat
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 @@ void ConsumedStmtVisitor::VisitCXXOperat
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 @@ void ConsumedStmtVisitor::VisitCXXOperat
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 @@ void ConsumedStmtVisitor::VisitCXXOperat
RPInfo = REntry->second;
- if (RPInfo.IsVar) {
+ if (RPInfo.isVar()) {
const VarDecl *Var = RPInfo.getVar();
PropagationMap.insert(PairType(Call,
@@ -434,14 +622,16 @@ void ConsumedStmtVisitor::VisitCXXOperat
checkCallability(PInfo, FunDecl, Call);
- if (PInfo.IsVar) {
- if (FunDecl->hasAttr<ConsumesAttr>()) {
- // Handle consuming operators.
+ if (PInfo.isVar()) {
+ if (isTestingFunction(FunDecl)) {
+ handleTestingFunctionCall(Call, PInfo.getVar());
+
+ } else if (FunDecl->hasAttr<ConsumesAttr>()) {
StateMap->setState(PInfo.getVar(), consumed::CS_Consumed);
+
} else if (const CXXMethodDecl *MethodDecl =
- dyn_cast_or_null<CXXMethodDecl>(FunDecl)) {
+ dyn_cast_or_null<CXXMethodDecl>(FunDecl)) {
- // Handle non-constant member operators.
if (!MethodDecl->isConst())
StateMap->setState(PInfo.getVar(), consumed::CS_Unknown);
}
@@ -482,11 +672,21 @@ void ConsumedStmtVisitor::VisitMemberExp
}
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 @@ void ConsumedStmtVisitor::VisitVarDecl(c
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<TestedVarsVisitor> {
-
- 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<FunctionDecl>(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<VarDecl>(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 @@ ConsumedState ConsumedStateMap::getState
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 @@ bool ConsumedAnalyzer::hasConsumableAttr
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<Expr*>(Terminator->getCond()));
-
- bool HasElse = Terminator->getElse() != NULL;
+bool ConsumedAnalyzer::splitState(const CFGBlock *CurrBlock,
+ const ConsumedStmtVisitor &Visitor) {
- ConsumedStateMap *ElseOrMergeStates = new ConsumedStateMap(*CurrStates);
+ ConsumedStateMap *FalseStates = new ConsumedStateMap(*CurrStates);
+ PropagationInfo PInfo;
- if (Visitor.IsUsefulConditional) {
- ConsumedState VarState = CurrStates->getState(Visitor.Test.Var);
+ if (const IfStmt *IfNode =
+ dyn_cast_or_null<IfStmt>(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<BinaryOperator>(Cond))
+ PInfo = Visitor.getInfo(cast<BinaryOperator>(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<BinaryOperator>(CurrBlock->getTerminator().getStmt())) {
+
+ PInfo = Visitor.getInfo(BinOp->getLHS());
+ if (!PInfo.isTest()) {
+ if ((BinOp = dyn_cast_or_null<BinaryOperator>(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 @@ void ConsumedAnalyzer::run(AnalysisDeclC
PostOrderCFGView *SortedGraph = AC.getAnalysis<PostOrderCFGView>();
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 @@ void ConsumedAnalyzer::run(AnalysisDeclC
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 @@ void ConsumedAnalyzer::run(AnalysisDeclC
}
}
- if (const IfStmt *Terminator =
- dyn_cast_or_null<IfStmt>(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.
Modified: cfe/trunk/test/SemaCXX/warn-consumed-analysis-strict.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/SemaCXX/warn-consumed-analysis-strict.cpp?rev=189594&r1=189593&r2=189594&view=diff
==============================================================================
--- cfe/trunk/test/SemaCXX/warn-consumed-analysis-strict.cpp (original)
+++ cfe/trunk/test/SemaCXX/warn-consumed-analysis-strict.cpp Thu Aug 29 12:26:57 2013
@@ -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 <typename T>
@@ -11,7 +13,7 @@ class ConsumableClass {
T var;
public:
- ConsumableClass(void);
+ ConsumableClass();
ConsumableClass(T val);
ConsumableClass(ConsumableClass<T> &other);
ConsumableClass(ConsumableClass<T> &&other);
@@ -26,20 +28,24 @@ class ConsumableClass {
template <typename U>
ConsumableClass<T>& operator=(ConsumableClass<U> &&other);
- void operator*(void) const CALLABLE_WHEN_UNCONSUMED;
-
- bool isValid(void) const TESTS_UNCONSUMED;
+ void operator()(int a) CONSUMES;
+ void operator*() const CALLABLE_WHEN_UNCONSUMED;
+ void unconsumedCall() const CALLABLE_WHEN_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<int> &var);
void baf1(ConsumableClass<int> *var);
-void testIfStmt(void) {
+void testIfStmt() {
ConsumableClass<int> var;
if (var.isValid()) { // expected-warning {{unnecessary test. Variable 'var' is known to be in the 'consumed' state}}
@@ -51,27 +57,113 @@ void testIfStmt(void) {
}
}
-void testConditionalMerge(void) {
- ConsumableClass<int> var;
+void testComplexConditionals() {
+ ConsumableClass<int> var0, var1, var2;
- if (var.isValid()) {// expected-warning {{unnecessary test. Variable 'var' is known to be in the 'consumed' state}}
+ // Coerce all variables into the unknown state.
+ baf0(var0);
+ baf0(var1);
+ baf0(var2);
+
+ 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<int> var(42);
baf0(var);
@@ -82,14 +174,14 @@ void testCallingConventions(void) {
*var; // expected-warning {{invocation of method 'operator*' on object 'var' while it is in an unknown state}}
}
-void testMoveAsignmentish(void) {
+void testMoveAsignmentish() {
ConsumableClass<int> 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<int> var(42);
var.constCall();
@@ -99,7 +191,15 @@ void testConstAndNonConstMemberFunctions
*var; // expected-warning {{invocation of method 'operator*' on object 'var' while it is in an unknown state}}
}
-void testSimpleForLoop(void) {
+void testNoWarnTestFromMacroExpansion() {
+ ConsumableClass<int> var(42);
+
+ if (TEST_VAR(var)) {
+ *var;
+ }
+}
+
+void testSimpleForLoop() {
ConsumableClass<int> var;
for (int i = 0; i < 10; ++i) {
@@ -109,7 +209,7 @@ void testSimpleForLoop(void) {
*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<int> var;
Modified: cfe/trunk/test/SemaCXX/warn-consumed-analysis.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/SemaCXX/warn-consumed-analysis.cpp?rev=189594&r1=189593&r2=189594&view=diff
==============================================================================
--- cfe/trunk/test/SemaCXX/warn-consumed-analysis.cpp (original)
+++ cfe/trunk/test/SemaCXX/warn-consumed-analysis.cpp Thu Aug 29 12:26:57 2013
@@ -11,7 +11,7 @@ class ConsumableClass {
T var;
public:
- ConsumableClass(void);
+ ConsumableClass();
ConsumableClass(nullptr_t p) CONSUMES;
ConsumableClass(T val);
ConsumableClass(ConsumableClass<T> &other);
@@ -28,17 +28,17 @@ class ConsumableClass {
ConsumableClass<T>& operator=(ConsumableClass<U> &&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<int> var);
@@ -47,7 +47,7 @@ void baf2(const ConsumableClass<int> *va
void baf3(ConsumableClass<int> &&var);
-void testInitialization(void) {
+void testInitialization() {
ConsumableClass<int> var0;
ConsumableClass<int> var1 = ConsumableClass<int>();
@@ -58,10 +58,10 @@ void testInitialization(void) {
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 @@ void testTempValue() {
*ConsumableClass<int>(); // expected-warning {{invocation of method 'operator*' on a temporary object while it is in the 'consumed' state}}
}
-void testSimpleRValueRefs(void) {
+void testSimpleRValueRefs() {
ConsumableClass<int> var0;
ConsumableClass<int> var1(42);
@@ -82,39 +82,149 @@ void testSimpleRValueRefs(void) {
*var1; // expected-warning {{invocation of method 'operator*' on object 'var1' while it is in the 'consumed' state}}
}
-void testIfStmt(void) {
+void testIfStmt() {
ConsumableClass<int> 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<int> 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<int> var;
+
+ // Make var enter the 'unknown' state.
+ baf1(var);
+
+ if (!var) {
+ var = ConsumableClass<int>(42);
+ }
+
+ *var;
+}
+
+void testCallingConventions() {
ConsumableClass<int> var(42);
baf0(var);
@@ -130,7 +240,7 @@ void testCallingConventions(void) {
*var; // expected-warning {{invocation of method 'operator*' on object 'var' while it is in the 'consumed' state}}
}
-void testMoveAsignmentish(void) {
+void testMoveAsignmentish() {
ConsumableClass<int> var0;
ConsumableClass<long> var1(42);
@@ -143,26 +253,25 @@ void testMoveAsignmentish(void) {
*var1; // expected-warning {{invocation of method 'operator*' on object 'var1' while it is in the 'consumed' state}}
}
-void testConditionalMerge(void) {
+void testConditionalMerge() {
ConsumableClass<int> 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<int> var(42);
*var;
@@ -172,13 +281,13 @@ void testConsumes0(void) {
*var; // expected-warning {{invocation of method 'operator*' on object 'var' while it is in the 'consumed' state}}
}
-void testConsumes1(void) {
+void testConsumes1() {
ConsumableClass<int> 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<int> var(42);
var.unconsumedCall();
@@ -187,7 +296,19 @@ void testConsumes2(void) {
var.unconsumedCall(); // expected-warning {{invocation of method 'unconsumedCall' on object 'var' while it is in the 'consumed' state}}
}
-void testSimpleForLoop(void) {
+void testNonsenseState() {
+ ConsumableClass<int> var(42);
+
+ if (var) {
+ *var;
+ } else {
+ *var;
+ }
+
+ *var;
+}
+
+void testSimpleForLoop() {
ConsumableClass<int> var;
for (int i = 0; i < 10; ++i) {
@@ -197,7 +318,7 @@ void testSimpleForLoop(void) {
*var;
}
-void testSimpleWhileLoop(void) {
+void testSimpleWhileLoop() {
int i = 0;
ConsumableClass<int> var;
More information about the cfe-commits
mailing list