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@gmail.com; review and minor fixes by delesley.

llvm-svn: 194900
This commit is contained in:
DeLesley Hutchins 2013-11-16 00:22:43 +00:00
parent b88cc2fc52
commit 68cc3f13c2
3 changed files with 319 additions and 163 deletions

View File

@ -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);

View File

@ -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 VarDecl *Var, ConsumedState TestsFor)
: InfoType(IT_Test) {
PropagationInfo(const VarTestResult &VarTest)
: InfoType(IT_VarTest), VarTest(VarTest) {}
Test.Var = Var;
Test.TestsFor = TestsFor;
PropagationInfo(const VarDecl *Var, ConsumedState TestsFor)
: InfoType(IT_VarTest) {
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;
@ -419,22 +460,11 @@ class ConsumedStmtVisitor : public ConstStmtVisitor<ConsumedStmtVisitor> {
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);
void VisitCastExpr(const CastExpr *Cast);
@ -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::checkCallability(const PropagationInfo &PInfo,
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()) {
} else {
ConsumedState TmpState = PInfo.getAsState(StateMap);
assert(PInfo.getState() != CS_None && "Invalid state");
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::propagateReturnType(const Stmt *Call,
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::VisitBinaryOperator(const BinaryOperator *BinOp) {
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(const CallExpr *Call) {
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(const CallExpr *Call) {
// 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(const CallExpr *Call) {
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(const CastExpr *Cast) {
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::VisitCXXConstructExpr(const CXXConstructExpr *Call) {
// 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::VisitCXXConstructExpr(const CXXConstructExpr *Call) {
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::VisitCXXConstructExpr(const CXXConstructExpr *Call) {
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::VisitCXXMemberCallExpr(
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::VisitCXXOperatorCallExpr(
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::VisitCXXOperatorCallExpr(
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,6 +850,10 @@ void ConsumedStmtVisitor::VisitCXXOperatorCallExpr(
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::VisitReturnStmt(const ReturnStmt *Ret) {
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::VisitUnaryOperator(const UnaryOperator *UOp) {
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::VisitUnaryOperator(const UnaryOperator *UOp) {
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::checkParamsForReturnTypestate(SourceLocation BlameLoc,
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::checkParamsForReturnTypestate(SourceLocation BlameLoc,
}
}
ConsumedState ConsumedStateMap::getState(const VarDecl *Var) const {
MapType::const_iterator Entry = Map.find(Var);
void ConsumedStateMap::clearTemporaries() {
TmpMap.clear();
}
if (Entry != Map.end()) {
ConsumedState ConsumedStateMap::getState(const VarDecl *Var) const {
VarMapType::const_iterator Entry = VarMap.find(Var);
if (Entry != VarMap.end())
return Entry->second;
} else {
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 ConsumedStateMap *Other) {
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 ConsumedStateMap *Other) {
continue;
if (LocalState != DMI->second)
Map[DMI->first] = CS_Unknown;
VarMap[DMI->first] = CS_Unknown;
}
}
@ -1192,8 +1231,8 @@ void ConsumedStateMap::intersectAtLoopHead(const CFGBlock *LoopHead,
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::intersectAtLoopHead(const CFGBlock *LoopHead,
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::intersectAtLoopHead(const CFGBlock *LoopHead,
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 CFGBlock *CurrBlock,
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 CFGBlock *CurrBlock,
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 CFGBlock *CurrBlock,
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,10 +1447,8 @@ void ConsumedAnalyzer::run(AnalysisDeclContext &AC) {
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,
Visitor.checkCallability(PropagationInfo(BTE),
DTor.getDestructorDecl(AC.getASTContext()),
BTE->getExprLoc());
break;
@ -1413,19 +1456,12 @@ void ConsumedAnalyzer::run(AnalysisDeclContext &AC) {
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,
Visitor.checkCallability(PropagationInfo(Var),
DTor.getDestructorDecl(AC.getASTContext()),
getLastStmtLoc(CurrBlock));
CurrStates->remove(Var);
}
Loc);
break;
}
@ -1434,6 +1470,8 @@ void ConsumedAnalyzer::run(AnalysisDeclContext &AC) {
}
}
CurrStates->clearTemporaries();
// TODO: Handle other forms of branching with precision, including while-
// and for-loops. (Deferred)
if (!splitState(CurrBlock, Visitor)) {

View File

@ -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,17 +672,123 @@ public:
bool cond();
Status doSomething();
void handleStatus(const Status& s);
void handleStatus(const Status& s RETURN_TYPESTATE(consumed));
void handleStatusPtr(const Status* s);
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;
void test() {
if (cond()) {
Status s = doSomething();
return; // Warning: Store it, but don't check.
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 testTemporariesWithConditionals3() {
Status s = doSomething();
if (cond()) {
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