r194900 - Consumed analysis: track state of temporary objects.
DeLesley Hutchins
delesley at google.com
Fri Nov 15 16:22:44 PST 2013
Author: delesley
Date: Fri Nov 15 18:22:43 2013
New Revision: 194900
URL: http://llvm.org/viewvc/llvm-project?rev=194900&view=rev
Log:
Consumed analysis: track state of temporary objects.
Earlier versions discarded the state too soon, and did not track state changes,
e.g. when passing a temporary to a move constructor. Patch by
chris.wailes at gmail.com; review and minor fixes by delesley.
Modified:
cfe/trunk/include/clang/Analysis/Analyses/Consumed.h
cfe/trunk/lib/Analysis/Consumed.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=194900&r1=194899&r2=194900&view=diff
==============================================================================
--- cfe/trunk/include/clang/Analysis/Analyses/Consumed.h (original)
+++ cfe/trunk/include/clang/Analysis/Analyses/Consumed.h Fri Nov 15 18:22:43 2013
@@ -130,28 +130,37 @@ namespace consumed {
class ConsumedStateMap {
- typedef llvm::DenseMap<const VarDecl *, ConsumedState> MapType;
- typedef std::pair<const VarDecl *, ConsumedState> PairType;
+ typedef llvm::DenseMap<const VarDecl *, ConsumedState> VarMapType;
+ typedef llvm::DenseMap<const CXXBindTemporaryExpr *, ConsumedState>
+ TmpMapType;
protected:
bool Reachable;
const Stmt *From;
- MapType Map;
+ VarMapType VarMap;
+ TmpMapType TmpMap;
public:
ConsumedStateMap() : Reachable(true), From(NULL) {}
ConsumedStateMap(const ConsumedStateMap &Other)
- : Reachable(Other.Reachable), From(Other.From), Map(Other.Map) {}
+ : Reachable(Other.Reachable), From(Other.From), VarMap(Other.VarMap),
+ TmpMap() {}
/// \brief Warn if any of the parameters being tracked are not in the state
/// they were declared to be in upon return from a function.
void checkParamsForReturnTypestate(SourceLocation BlameLoc,
ConsumedWarningsHandlerBase &WarningsHandler) const;
+ /// \brief Clear the TmpMap.
+ void clearTemporaries();
+
/// \brief Get the consumed state of a given variable.
ConsumedState getState(const VarDecl *Var) const;
+ /// \brief Get the consumed state of a given temporary value.
+ ConsumedState getState(const CXXBindTemporaryExpr *Tmp) const;
+
/// \brief Merge this state map with another map.
void intersect(const ConsumedStateMap *Other);
@@ -173,6 +182,9 @@ namespace consumed {
/// \brief Set the consumed state of a given variable.
void setState(const VarDecl *Var, ConsumedState State);
+ /// \brief Set the consumed state of a given temporary value.
+ void setState(const CXXBindTemporaryExpr *Tmp, ConsumedState State);
+
/// \brief Remove the variable from our state map.
void remove(const VarDecl *Var);
Modified: cfe/trunk/lib/Analysis/Consumed.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Analysis/Consumed.cpp?rev=194900&r1=194899&r2=194900&view=diff
==============================================================================
--- cfe/trunk/lib/Analysis/Consumed.cpp (original)
+++ cfe/trunk/lib/Analysis/Consumed.cpp Fri Nov 15 18:22:43 2013
@@ -32,7 +32,9 @@
#include "llvm/Support/Compiler.h"
#include "llvm/Support/raw_ostream.h"
-// TODO: Use information from tests in while-loop conditional.
+// 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
@@ -280,9 +282,10 @@ class PropagationInfo {
enum {
IT_None,
IT_State,
- IT_Test,
+ IT_VarTest,
IT_BinTest,
- IT_Var
+ IT_Var,
+ IT_Tmp
} InfoType;
struct BinTestTy {
@@ -294,22 +297,23 @@ class PropagationInfo {
union {
ConsumedState State;
- VarTestResult Test;
+ VarTestResult VarTest;
const VarDecl *Var;
+ const CXXBindTemporaryExpr *Tmp;
BinTestTy BinTest;
};
- QualType TempType;
-
public:
PropagationInfo() : InfoType(IT_None) {}
- PropagationInfo(const VarTestResult &Test) : InfoType(IT_Test), Test(Test) {}
+ PropagationInfo(const VarTestResult &VarTest)
+ : InfoType(IT_VarTest), VarTest(VarTest) {}
+
PropagationInfo(const VarDecl *Var, ConsumedState TestsFor)
- : InfoType(IT_Test) {
+ : InfoType(IT_VarTest) {
- Test.Var = Var;
- Test.TestsFor = TestsFor;
+ VarTest.Var = Var;
+ VarTest.TestsFor = TestsFor;
}
PropagationInfo(const BinaryOperator *Source, EffectiveOp EOp,
@@ -335,24 +339,21 @@ public:
BinTest.RTest.TestsFor = RTestsFor;
}
- PropagationInfo(ConsumedState State, QualType TempType)
- : InfoType(IT_State), State(State), TempType(TempType) {}
+ 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 QualType & getTempType() const {
- assert(InfoType == IT_State);
- return TempType;
- }
-
- const VarTestResult & getTest() const {
- assert(InfoType == IT_Test);
- return Test;
+ const VarTestResult & getVarTest() const {
+ assert(InfoType == IT_VarTest);
+ return VarTest;
}
const VarTestResult & getLTest() const {
@@ -370,6 +371,24 @@ public:
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;
@@ -380,17 +399,27 @@ public:
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; }
+ inline bool isValid() const { return InfoType != IT_None; }
+ inline bool isState() const { return InfoType == IT_State; }
+ inline bool isVarTest() const { return InfoType == IT_VarTest; }
+ inline bool isBinTest() const { return InfoType == IT_BinTest; }
+ inline bool isVar() const { return InfoType == IT_Var; }
+ inline bool isTmp() const { return InfoType == IT_Tmp; }
+
+ bool isTest() const {
+ return InfoType == IT_VarTest || InfoType == IT_BinTest;
+ }
+
+ bool isPointerToValue() const {
+ return InfoType == IT_Var || InfoType == IT_Tmp;
+ }
PropagationInfo invertTest() const {
- assert(InfoType == IT_Test || InfoType == IT_BinTest);
+ assert(InfoType == IT_VarTest || InfoType == IT_BinTest);
- if (InfoType == IT_Test) {
- return PropagationInfo(Test.Var, invertConsumedUnconsumed(Test.TestsFor));
+ if (InfoType == IT_VarTest) {
+ return PropagationInfo(VarTest.Var,
+ invertConsumedUnconsumed(VarTest.TestsFor));
} else if (InfoType == IT_BinTest) {
return PropagationInfo(BinTest.Source,
@@ -403,6 +432,18 @@ public:
}
};
+static inline void
+setStateForVarOrTmp(ConsumedStateMap *StateMap, const PropagationInfo &PInfo,
+ ConsumedState State) {
+
+ assert(PInfo.isVar() || PInfo.isTmp());
+
+ if (PInfo.isVar())
+ StateMap->setState(PInfo.getVar(), State);
+ else
+ StateMap->setState(PInfo.getTmp(), State);
+}
+
class ConsumedStmtVisitor : public ConstStmtVisitor<ConsumedStmtVisitor> {
typedef llvm::DenseMap<const Stmt *, PropagationInfo> MapType;
@@ -418,22 +459,11 @@ class ConsumedStmtVisitor : public Const
bool isLikeMoveAssignment(const CXXMethodDecl *MethodDecl);
void propagateReturnType(const Stmt *Call, const FunctionDecl *Fun,
QualType ReturnType);
-
- inline ConsumedState getPInfoState(const PropagationInfo& PInfo) {
- if (PInfo.isVar())
- return StateMap->getState(PInfo.getVar());
- else if (PInfo.isState())
- return PInfo.getState();
- else
- return CS_None;
- }
public:
void checkCallability(const PropagationInfo &PInfo,
const FunctionDecl *FunDecl,
SourceLocation BlameLoc);
-
- void Visit(const Stmt *StmtNode);
void VisitBinaryOperator(const BinaryOperator *BinOp);
void VisitCallExpr(const CallExpr *Call);
@@ -472,6 +502,7 @@ public:
void ConsumedStmtVisitor::checkCallability(const PropagationInfo &PInfo,
const FunctionDecl *FunDecl,
SourceLocation BlameLoc) {
+ assert(!PInfo.isTest());
if (!FunDecl->hasAttr<CallableWhenAttr>())
return;
@@ -479,27 +510,23 @@ void ConsumedStmtVisitor::checkCallabili
const CallableWhenAttr *CWAttr = FunDecl->getAttr<CallableWhenAttr>();
if (PInfo.isVar()) {
- const VarDecl *Var = PInfo.getVar();
- ConsumedState VarState = StateMap->getState(Var);
+ ConsumedState VarState = StateMap->getState(PInfo.getVar());
- assert(VarState != CS_None && "Invalid state");
-
- if (isCallableInState(CWAttr, VarState))
+ if (VarState == CS_None || isCallableInState(CWAttr, VarState))
return;
Analyzer.WarningsHandler.warnUseInInvalidState(
- FunDecl->getNameAsString(), Var->getNameAsString(),
+ FunDecl->getNameAsString(), PInfo.getVar()->getNameAsString(),
stateToString(VarState), BlameLoc);
- } else if (PInfo.isState()) {
-
- assert(PInfo.getState() != CS_None && "Invalid state");
+ } else {
+ ConsumedState TmpState = PInfo.getAsState(StateMap);
- if (isCallableInState(CWAttr, PInfo.getState()))
+ if (TmpState == CS_None || isCallableInState(CWAttr, TmpState))
return;
Analyzer.WarningsHandler.warnUseOfTempInInvalidState(
- FunDecl->getNameAsString(), stateToString(PInfo.getState()), BlameLoc);
+ FunDecl->getNameAsString(), stateToString(TmpState), BlameLoc);
}
}
@@ -532,19 +559,7 @@ void ConsumedStmtVisitor::propagateRetur
else
ReturnState = mapConsumableAttrState(ReturnType);
- PropagationMap.insert(PairType(Call,
- PropagationInfo(ReturnState, ReturnType)));
- }
-}
-
-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);
+ PropagationMap.insert(PairType(Call, PropagationInfo(ReturnState)));
}
}
@@ -557,16 +572,16 @@ void ConsumedStmtVisitor::VisitBinaryOpe
VarTestResult LTest, RTest;
- if (LEntry != PropagationMap.end() && LEntry->second.isTest()) {
- LTest = LEntry->second.getTest();
+ if (LEntry != PropagationMap.end() && LEntry->second.isVarTest()) {
+ LTest = LEntry->second.getVarTest();
} else {
LTest.Var = NULL;
LTest.TestsFor = CS_None;
}
- if (REntry != PropagationMap.end() && REntry->second.isTest()) {
- RTest = REntry->second.getTest();
+ if (REntry != PropagationMap.end() && REntry->second.isVarTest()) {
+ RTest = REntry->second.getVarTest();
} else {
RTest.Var = NULL;
@@ -609,8 +624,7 @@ void ConsumedStmtVisitor::VisitCallExpr(
InfoEntry Entry = PropagationMap.find(Call->getArg(Index));
- if (Entry == PropagationMap.end() ||
- !(Entry->second.isState() || Entry->second.isVar()))
+ if (Entry == PropagationMap.end() || Entry->second.isTest())
continue;
PropagationInfo PInfo = Entry->second;
@@ -618,9 +632,7 @@ void ConsumedStmtVisitor::VisitCallExpr(
// Check that the parameter is in the correct state.
if (Param->hasAttr<ParamTypestateAttr>()) {
- ConsumedState ParamState =
- PInfo.isState() ? PInfo.getState() :
- StateMap->getState(PInfo.getVar());
+ ConsumedState ParamState = PInfo.getAsState(StateMap);
ConsumedState ExpectedState =
mapParamTypestateAttrState(Param->getAttr<ParamTypestateAttr>());
@@ -631,22 +643,22 @@ void ConsumedStmtVisitor::VisitCallExpr(
stateToString(ExpectedState), stateToString(ParamState));
}
- if (!Entry->second.isVar())
+ if (!(Entry->second.isVar() || Entry->second.isTmp()))
continue;
// Adjust state on the caller side.
if (isRValueRefish(ParamType)) {
- StateMap->setState(PInfo.getVar(), consumed::CS_Consumed);
+ setStateForVarOrTmp(StateMap, PInfo, consumed::CS_Consumed);
} else if (Param->hasAttr<ReturnTypestateAttr>()) {
- StateMap->setState(PInfo.getVar(),
+ setStateForVarOrTmp(StateMap, PInfo,
mapReturnTypestateAttrState(Param->getAttr<ReturnTypestateAttr>()));
} else if (!isValueType(ParamType) &&
!ParamType->getPointeeType().isConstQualified()) {
- StateMap->setState(PInfo.getVar(), consumed::CS_Unknown);
+ setStateForVarOrTmp(StateMap, PInfo, consumed::CS_Unknown);
}
}
@@ -665,7 +677,12 @@ void ConsumedStmtVisitor::VisitCastExpr(
void ConsumedStmtVisitor::VisitCXXBindTemporaryExpr(
const CXXBindTemporaryExpr *Temp) {
- forwardInfo(Temp->getSubExpr(), Temp);
+ InfoEntry Entry = PropagationMap.find(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) {
@@ -679,14 +696,16 @@ void ConsumedStmtVisitor::VisitCXXConstr
// FIXME: What should happen if someone annotates the move constructor?
if (Constructor->hasAttr<ReturnTypestateAttr>()) {
+ // TODO: Adjust state of args appropriately.
+
ReturnTypestateAttr *RTAttr = Constructor->getAttr<ReturnTypestateAttr>();
ConsumedState RetState = mapReturnTypestateAttrState(RTAttr);
- PropagationMap.insert(PairType(Call, PropagationInfo(RetState, ThisType)));
+ PropagationMap.insert(PairType(Call, PropagationInfo(RetState)));
} else if (Constructor->isDefaultConstructor()) {
PropagationMap.insert(PairType(Call,
- PropagationInfo(consumed::CS_Consumed, ThisType)));
+ PropagationInfo(consumed::CS_Consumed)));
} else if (Constructor->isMoveConstructor()) {
@@ -699,10 +718,18 @@ void ConsumedStmtVisitor::VisitCXXConstr
const VarDecl* Var = PInfo.getVar();
PropagationMap.insert(PairType(Call,
- PropagationInfo(StateMap->getState(Var), ThisType)));
+ PropagationInfo(StateMap->getState(Var))));
StateMap->setState(Var, consumed::CS_Consumed);
+ } else if (PInfo.isTmp()) {
+ const CXXBindTemporaryExpr *Tmp = PInfo.getTmp();
+
+ PropagationMap.insert(PairType(Call,
+ PropagationInfo(StateMap->getState(Tmp))));
+
+ StateMap->setState(Tmp, consumed::CS_Consumed);
+
} else {
PropagationMap.insert(PairType(Call, PInfo));
}
@@ -711,8 +738,10 @@ void ConsumedStmtVisitor::VisitCXXConstr
forwardInfo(Call->getArg(0), Call);
} else {
+ // TODO: Adjust state of args appropriately.
+
ConsumedState RetState = mapConsumableAttrState(ThisType);
- PropagationMap.insert(PairType(Call, PropagationInfo(RetState, ThisType)));
+ PropagationMap.insert(PairType(Call, PropagationInfo(RetState)));
}
}
@@ -736,6 +765,9 @@ void ConsumedStmtVisitor::VisitCXXMember
else if (MethodDecl->hasAttr<SetTypestateAttr>())
StateMap->setState(PInfo.getVar(),
mapSetTypestateAttrState(MethodDecl->getAttr<SetTypestateAttr>()));
+ } else if (PInfo.isTmp() && MethodDecl->hasAttr<SetTypestateAttr>()) {
+ StateMap->setState(PInfo.getTmp(),
+ mapSetTypestateAttrState(MethodDecl->getAttr<SetTypestateAttr>()));
}
}
}
@@ -762,28 +794,17 @@ void ConsumedStmtVisitor::VisitCXXOperat
LPInfo = LEntry->second;
RPInfo = REntry->second;
- if (LPInfo.isVar() && RPInfo.isVar()) {
- StateMap->setState(LPInfo.getVar(),
- StateMap->getState(RPInfo.getVar()));
-
- StateMap->setState(RPInfo.getVar(), consumed::CS_Consumed);
-
+ if (LPInfo.isPointerToValue() && RPInfo.isPointerToValue()) {
+ setStateForVarOrTmp(StateMap, LPInfo, RPInfo.getAsState(StateMap));
PropagationMap.insert(PairType(Call, LPInfo));
+ setStateForVarOrTmp(StateMap, RPInfo, consumed::CS_Consumed);
- } else if (LPInfo.isVar() && !RPInfo.isVar()) {
- StateMap->setState(LPInfo.getVar(), RPInfo.getState());
-
+ } else if (RPInfo.isState()) {
+ setStateForVarOrTmp(StateMap, LPInfo, RPInfo.getState());
PropagationMap.insert(PairType(Call, LPInfo));
- } else if (!LPInfo.isVar() && RPInfo.isVar()) {
- PropagationMap.insert(PairType(Call,
- PropagationInfo(StateMap->getState(RPInfo.getVar()),
- LPInfo.getTempType())));
-
- StateMap->setState(RPInfo.getVar(), consumed::CS_Consumed);
-
} else {
- PropagationMap.insert(PairType(Call, RPInfo));
+ setStateForVarOrTmp(StateMap, RPInfo, consumed::CS_Consumed);
}
} else if (LEntry != PropagationMap.end() &&
@@ -791,21 +812,24 @@ void ConsumedStmtVisitor::VisitCXXOperat
LPInfo = LEntry->second;
- if (LPInfo.isVar()) {
- StateMap->setState(LPInfo.getVar(), consumed::CS_Unknown);
-
+ assert(!LPInfo.isTest());
+
+ if (LPInfo.isPointerToValue()) {
+ setStateForVarOrTmp(StateMap, LPInfo, consumed::CS_Unknown);
PropagationMap.insert(PairType(Call, LPInfo));
- } else if (LPInfo.isState()) {
+ } else {
PropagationMap.insert(PairType(Call,
- PropagationInfo(consumed::CS_Unknown, LPInfo.getTempType())));
+ PropagationInfo(consumed::CS_Unknown)));
}
} else if (LEntry == PropagationMap.end() &&
REntry != PropagationMap.end()) {
- if (REntry->second.isVar())
- StateMap->setState(REntry->second.getVar(), consumed::CS_Consumed);
+ RPInfo = REntry->second;
+
+ if (RPInfo.isPointerToValue())
+ setStateForVarOrTmp(StateMap, RPInfo, consumed::CS_Consumed);
}
} else {
@@ -826,7 +850,11 @@ void ConsumedStmtVisitor::VisitCXXOperat
else if (FunDecl->hasAttr<SetTypestateAttr>())
StateMap->setState(PInfo.getVar(),
mapSetTypestateAttrState(FunDecl->getAttr<SetTypestateAttr>()));
- }
+
+ } else if (PInfo.isTmp() && FunDecl->hasAttr<SetTypestateAttr>()) {
+ StateMap->setState(PInfo.getTmp(),
+ mapSetTypestateAttrState(FunDecl->getAttr<SetTypestateAttr>()));
+ }
}
}
}
@@ -892,10 +920,7 @@ void ConsumedStmtVisitor::VisitReturnStm
InfoEntry Entry = PropagationMap.find(Ret->getRetValue());
if (Entry != PropagationMap.end()) {
- assert(Entry->second.isState() || Entry->second.isVar());
-
- ConsumedState RetState = Entry->second.isState() ?
- Entry->second.getState() : StateMap->getState(Entry->second.getVar());
+ ConsumedState RetState = Entry->second.getAsState(StateMap);
if (RetState != ExpectedState)
Analyzer.WarningsHandler.warnReturnTypestateMismatch(
@@ -918,7 +943,7 @@ void ConsumedStmtVisitor::VisitUnaryOper
break;
case UO_LNot:
- if (Entry->second.isTest() || Entry->second.isBinTest())
+ if (Entry->second.isTest())
PropagationMap.insert(PairType(UOp, Entry->second.invertTest()));
break;
@@ -931,10 +956,12 @@ void ConsumedStmtVisitor::VisitUnaryOper
void ConsumedStmtVisitor::VisitVarDecl(const VarDecl *Var) {
if (isConsumableType(Var->getType())) {
if (Var->hasInit()) {
- MapType::iterator VIT = PropagationMap.find(Var->getInit());
+ MapType::iterator VIT = PropagationMap.find(
+ Var->getInit()->IgnoreImplicit());
if (VIT != PropagationMap.end()) {
PropagationInfo PInfo = VIT->second;
- ConsumedState St = getPInfoState(PInfo);
+ ConsumedState St = PInfo.getAsState(StateMap);
+
if (St != consumed::CS_None) {
StateMap->setState(Var, St);
return;
@@ -1133,8 +1160,8 @@ void ConsumedStateMap::checkParamsForRet
ConsumedState ExpectedState;
- for (MapType::const_iterator DMI = Map.begin(), DME = Map.end(); DMI != DME;
- ++DMI) {
+ for (VarMapType::const_iterator DMI = VarMap.begin(), DME = VarMap.end();
+ DMI != DME; ++DMI) {
if (isa<ParmVarDecl>(DMI->first)) {
const ParmVarDecl *Param = cast<ParmVarDecl>(DMI->first);
@@ -1153,15 +1180,27 @@ void ConsumedStateMap::checkParamsForRet
}
}
+void ConsumedStateMap::clearTemporaries() {
+ TmpMap.clear();
+}
+
ConsumedState ConsumedStateMap::getState(const VarDecl *Var) const {
- MapType::const_iterator Entry = Map.find(Var);
+ VarMapType::const_iterator Entry = VarMap.find(Var);
- if (Entry != Map.end()) {
+ if (Entry != VarMap.end())
return Entry->second;
- } else {
- return CS_None;
- }
+ 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) {
@@ -1172,8 +1211,8 @@ void ConsumedStateMap::intersect(const C
return;
}
- for (MapType::const_iterator DMI = Other->Map.begin(), DME = Other->Map.end();
- DMI != DME; ++DMI) {
+ for (VarMapType::const_iterator DMI = Other->VarMap.begin(),
+ DME = Other->VarMap.end(); DMI != DME; ++DMI) {
LocalState = this->getState(DMI->first);
@@ -1181,7 +1220,7 @@ void ConsumedStateMap::intersect(const C
continue;
if (LocalState != DMI->second)
- Map[DMI->first] = CS_Unknown;
+ VarMap[DMI->first] = CS_Unknown;
}
}
@@ -1192,8 +1231,8 @@ void ConsumedStateMap::intersectAtLoopHe
ConsumedState LocalState;
SourceLocation BlameLoc = getLastStmtLoc(LoopBack);
- for (MapType::const_iterator DMI = LoopBackStates->Map.begin(),
- DME = LoopBackStates->Map.end(); DMI != DME; ++DMI) {
+ for (VarMapType::const_iterator DMI = LoopBackStates->VarMap.begin(),
+ DME = LoopBackStates->VarMap.end(); DMI != DME; ++DMI) {
LocalState = this->getState(DMI->first);
@@ -1201,7 +1240,7 @@ void ConsumedStateMap::intersectAtLoopHe
continue;
if (LocalState != DMI->second) {
- Map[DMI->first] = CS_Unknown;
+ VarMap[DMI->first] = CS_Unknown;
WarningsHandler.warnLoopStateMismatch(
BlameLoc, DMI->first->getNameAsString());
}
@@ -1210,20 +1249,26 @@ void ConsumedStateMap::intersectAtLoopHe
void ConsumedStateMap::markUnreachable() {
this->Reachable = false;
- Map.clear();
+ VarMap.clear();
+ TmpMap.clear();
}
void ConsumedStateMap::setState(const VarDecl *Var, ConsumedState State) {
- Map[Var] = State;
+ VarMap[Var] = State;
+}
+
+void ConsumedStateMap::setState(const CXXBindTemporaryExpr *Tmp,
+ ConsumedState State) {
+ TmpMap[Tmp] = State;
}
void ConsumedStateMap::remove(const VarDecl *Var) {
- Map.erase(Var);
+ VarMap.erase(Var);
}
bool ConsumedStateMap::operator!=(const ConsumedStateMap *Other) const {
- for (MapType::const_iterator DMI = Other->Map.begin(), DME = Other->Map.end();
- DMI != DME; ++DMI) {
+ for (VarMapType::const_iterator DMI = Other->VarMap.begin(),
+ DME = Other->VarMap.end(); DMI != DME; ++DMI) {
if (this->getState(DMI->first) != DMI->second)
return true;
@@ -1276,10 +1321,10 @@ bool ConsumedAnalyzer::splitState(const
if (!PInfo.isValid() && isa<BinaryOperator>(Cond))
PInfo = Visitor.getInfo(cast<BinaryOperator>(Cond)->getRHS());
- if (PInfo.isTest()) {
+ if (PInfo.isVarTest()) {
CurrStates->setSource(Cond);
FalseStates->setSource(Cond);
- splitVarStateForIf(IfNode, PInfo.getTest(), CurrStates,
+ splitVarStateForIf(IfNode, PInfo.getVarTest(), CurrStates,
FalseStates.get());
} else if (PInfo.isBinTest()) {
@@ -1295,11 +1340,11 @@ bool ConsumedAnalyzer::splitState(const
dyn_cast_or_null<BinaryOperator>(CurrBlock->getTerminator().getStmt())) {
PInfo = Visitor.getInfo(BinOp->getLHS());
- if (!PInfo.isTest()) {
+ if (!PInfo.isVarTest()) {
if ((BinOp = dyn_cast_or_null<BinaryOperator>(BinOp->getLHS()))) {
PInfo = Visitor.getInfo(BinOp->getRHS());
- if (!PInfo.isTest())
+ if (!PInfo.isVarTest())
return false;
} else {
@@ -1310,7 +1355,7 @@ bool ConsumedAnalyzer::splitState(const
CurrStates->setSource(BinOp);
FalseStates->setSource(BinOp);
- const VarTestResult &Test = PInfo.getTest();
+ const VarTestResult &Test = PInfo.getVarTest();
ConsumedState VarState = CurrStates->getState(Test.Var);
if (BinOp->getOpcode() == BO_LAnd) {
@@ -1402,30 +1447,21 @@ void ConsumedAnalyzer::run(AnalysisDeclC
case CFGElement::TemporaryDtor: {
const CFGTemporaryDtor DTor = BI->castAs<CFGTemporaryDtor>();
const CXXBindTemporaryExpr *BTE = DTor.getBindTemporaryExpr();
- PropagationInfo PInfo = Visitor.getInfo(BTE);
- if (PInfo.isValid())
- Visitor.checkCallability(PInfo,
- DTor.getDestructorDecl(AC.getASTContext()),
- BTE->getExprLoc());
+ Visitor.checkCallability(PropagationInfo(BTE),
+ DTor.getDestructorDecl(AC.getASTContext()),
+ BTE->getExprLoc());
break;
}
case CFGElement::AutomaticObjectDtor: {
const CFGAutomaticObjDtor DTor = BI->castAs<CFGAutomaticObjDtor>();
-
+ SourceLocation Loc = DTor.getTriggerStmt()->getLocEnd();
const VarDecl *Var = DTor.getVarDecl();
- ConsumedState VarState = CurrStates->getState(Var);
- if (VarState != CS_None) {
- PropagationInfo PInfo(Var);
-
- Visitor.checkCallability(PInfo,
- DTor.getDestructorDecl(AC.getASTContext()),
- getLastStmtLoc(CurrBlock));
-
- CurrStates->remove(Var);
- }
+ Visitor.checkCallability(PropagationInfo(Var),
+ DTor.getDestructorDecl(AC.getASTContext()),
+ Loc);
break;
}
@@ -1434,6 +1470,8 @@ void ConsumedAnalyzer::run(AnalysisDeclC
}
}
+ CurrStates->clearTemporaries();
+
// TODO: Handle other forms of branching with precision, including while-
// and for-loops. (Deferred)
if (!splitState(CurrBlock, Visitor)) {
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=194900&r1=194899&r2=194900&view=diff
==============================================================================
--- cfe/trunk/test/SemaCXX/warn-consumed-analysis.cpp (original)
+++ cfe/trunk/test/SemaCXX/warn-consumed-analysis.cpp Fri Nov 15 18:22:43 2013
@@ -655,10 +655,10 @@ public:
Status(int c) RETURN_TYPESTATE(unconsumed);
Status(const Status &other);
- //Status(Status &&other);
+ Status(Status &&other);
Status& operator=(const Status &other) CALLABLE_WHEN("unknown", "consumed");
- //Status& operator=(Status &&other) CALLABLE_WHEN("unknown", "consumed");
+ Status& operator=(Status &&other) CALLABLE_WHEN("unknown", "consumed");
bool check() const SET_TYPESTATE(consumed);
void ignore() const SET_TYPESTATE(consumed);
@@ -672,18 +672,124 @@ public:
bool cond();
Status doSomething();
-void handleStatus(const Status& s);
+void handleStatus(const Status& s RETURN_TYPESTATE(consumed));
void handleStatusPtr(const Status* s);
-int a;
+void testSimpleTemporaries0() {
+ doSomething(); // expected-warning {{invalid invocation of method '~Status' on a temporary object while it is in the 'unconsumed' state}}
+}
+
+void testSimpleTemporaries1() {
+ doSomething().ignore();
+}
+
+void testSimpleTemporaries2() {
+ handleStatus(doSomething());
+}
+
+void testSimpleTemporaries3() {
+ Status s = doSomething();
+} // expected-warning {{invalid invocation of method '~Status' on object 's' while it is in the 'unconsumed' state}}
+
+void testSimpleTemporaries4() {
+ Status s = doSomething();
+ s.check();
+}
+
+void testSimpleTemporaries5() {
+ Status s = doSomething();
+ s.clear(); // expected-warning {{invalid invocation of method 'clear' on object 's' while it is in the 'unconsumed' state}}
+}
+
+void testSimpleTemporaries6() {
+ Status s = doSomething();
+ handleStatus(s);
+}
+void testSimpleTemporaries7() {
+ Status s;
+ s = doSomething();
+} // expected-warning {{invalid invocation of method '~Status' on object 's' while it is in the 'unconsumed' state}}
+
+void testTemporariesWithConditionals0() {
+ int a;
+
+ Status s = doSomething();
+ if (cond()) a = 0;
+ else a = 1;
+} // expected-warning {{invalid invocation of method '~Status' on object 's' while it is in the 'unconsumed' state}}
+
+void testTemporariesWithConditionals1() {
+ int a;
+
+ Status s = doSomething();
+ if (cond()) a = 0;
+ else a = 1;
+ s.ignore();
+}
+
+void testTemporariesWithConditionals2() {
+ int a;
+
+ Status s = doSomething();
+ s.ignore();
+ if (cond()) a = 0;
+ else a = 1;
+}
-void test() {
+void testTemporariesWithConditionals3() {
+ Status s = doSomething();
if (cond()) {
- Status s = doSomething();
- return; // Warning: Store it, but don't check.
+ s.check();
}
}
+void testTemporariesAndConstructors0() {
+ Status s(doSomething());
+ s.check();
+}
+
+void testTemporariesAndConstructors1() {
+ // Test the copy constructor.
+
+ Status s1 = doSomething();
+ Status s2(s1);
+ s2.check();
+} // expected-warning {{invalid invocation of method '~Status' on object 's1' while it is in the 'unconsumed' state}}
+
+void testTemporariesAndConstructors2() {
+ // Test the move constructor.
+
+ Status s1 = doSomething();
+ Status s2(static_cast<Status&&>(s1));
+ s2.check();
+}
+
+void testTemporariesAndOperators0() {
+ // Test the assignment operator.
+
+ Status s1 = doSomething();
+ Status s2;
+ s2 = s1;
+ s2.check();
+} // expected-warning {{invalid invocation of method '~Status' on object 's1' while it is in the 'unconsumed' state}}
+
+void testTemporariesAndOperators1() {
+ // Test the move assignment operator.
+
+ Status s1 = doSomething();
+ Status s2;
+ s2 = static_cast<Status&&>(s1);
+ s2.check();
+}
+
+void testTemporariesAndOperators2() {
+ Status s1 = doSomething();
+ Status s2 = doSomething();
+ s1 = s2; // expected-warning {{invalid invocation of method 'operator=' on object 's1' while it is in the 'unconsumed' state}}
+ s1.check();
+ s2.check();
+}
+
} // end namespace InitializerAssertionFailTest
More information about the cfe-commits
mailing list