diff options
| author | DeLesley Hutchins <delesley@google.com> | 2013-10-11 21:55:33 +0000 |
|---|---|---|
| committer | DeLesley Hutchins <delesley@google.com> | 2013-10-11 21:55:33 +0000 |
| commit | fbdee4e3c2c869941fd0a5d2b5ce06d67b9d0ff0 (patch) | |
| tree | 3414286fe1a6ec006f7f058087aa3f0e524848b6 | |
| parent | 384b40b90dfd4f5001644f1088b95424e2dffd76 (diff) | |
| download | bcm5719-llvm-fbdee4e3c2c869941fd0a5d2b5ce06d67b9d0ff0.tar.gz bcm5719-llvm-fbdee4e3c2c869941fd0a5d2b5ce06d67b9d0ff0.zip | |
Consumed analysis: check destructor calls.
This allows the callable_when attribute to be attached to destructors.
Original patch by chris.wailes@gmail.com, reviewed and edited by delesley.
llvm-svn: 192508
| -rw-r--r-- | clang/lib/Analysis/Consumed.cpp | 75 | ||||
| -rw-r--r-- | clang/test/SemaCXX/warn-consumed-analysis.cpp | 22 |
2 files changed, 76 insertions, 21 deletions
diff --git a/clang/lib/Analysis/Consumed.cpp b/clang/lib/Analysis/Consumed.cpp index d291f627d02..85dd821892a 100644 --- a/clang/lib/Analysis/Consumed.cpp +++ b/clang/lib/Analysis/Consumed.cpp @@ -51,14 +51,14 @@ using namespace consumed; // Key method definition ConsumedWarningsHandlerBase::~ConsumedWarningsHandlerBase() {} -static SourceLocation getWarningLocForLoopExit(const CFGBlock *ExitBlock) { +static SourceLocation getLastStmtLoc(const CFGBlock *Block) { // Find the source location of the last statement in the block, if the block // is not empty. - if (const Stmt *StmtNode = ExitBlock->getTerminator()) { + if (const Stmt *StmtNode = Block->getTerminator()) { return StmtNode->getLocStart(); } else { - for (CFGBlock::const_reverse_iterator BI = ExitBlock->rbegin(), - BE = ExitBlock->rend(); BI != BE; ++BI) { + for (CFGBlock::const_reverse_iterator BI = Block->rbegin(), + BE = Block->rend(); BI != BE; ++BI) { // FIXME: Handle other CFGElement kinds. if (Optional<CFGStmt> CS = BI->getAs<CFGStmt>()) return CS->getStmt()->getLocStart(); @@ -66,10 +66,10 @@ static SourceLocation getWarningLocForLoopExit(const CFGBlock *ExitBlock) { } // The block is empty, and has a single predecessor. Use its exit location. - assert(ExitBlock->pred_size() == 1 && *ExitBlock->pred_begin() && - ExitBlock->succ_size() != 0); + assert(Block->pred_size() == 1 && *Block->pred_begin() && + Block->succ_size() != 0); - return getWarningLocForLoopExit(*ExitBlock->pred_begin()); + return getLastStmtLoc(*Block->pred_begin()); } static ConsumedState invertConsumedUnconsumed(ConsumedState State) { @@ -340,10 +340,6 @@ class ConsumedStmtVisitor : public ConstStmtVisitor<ConsumedStmtVisitor> { ConsumedAnalyzer &Analyzer; ConsumedStateMap *StateMap; MapType PropagationMap; - - void checkCallability(const PropagationInfo &PInfo, - 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); @@ -351,12 +347,16 @@ class ConsumedStmtVisitor : public ConstStmtVisitor<ConsumedStmtVisitor> { QualType ReturnType); 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); + void VisitCXXBindTemporaryExpr(const CXXBindTemporaryExpr *Temp); void VisitCXXConstructExpr(const CXXConstructExpr *Call); void VisitCXXMemberCallExpr(const CXXMemberCallExpr *Call); void VisitCXXOperatorCallExpr(const CXXOperatorCallExpr *Call); @@ -389,7 +389,7 @@ public: void ConsumedStmtVisitor::checkCallability(const PropagationInfo &PInfo, const FunctionDecl *FunDecl, - const CallExpr *Call) { + SourceLocation BlameLoc) { if (!FunDecl->hasAttr<CallableWhenAttr>()) return; @@ -407,7 +407,7 @@ void ConsumedStmtVisitor::checkCallability(const PropagationInfo &PInfo, Analyzer.WarningsHandler.warnUseInInvalidState( FunDecl->getNameAsString(), Var->getNameAsString(), - stateToString(VarState), Call->getExprLoc()); + stateToString(VarState), BlameLoc); } else if (PInfo.isState()) { @@ -417,8 +417,7 @@ void ConsumedStmtVisitor::checkCallability(const PropagationInfo &PInfo, return; Analyzer.WarningsHandler.warnUseOfTempInInvalidState( - FunDecl->getNameAsString(), stateToString(PInfo.getState()), - Call->getExprLoc()); + FunDecl->getNameAsString(), stateToString(PInfo.getState()), BlameLoc); } } @@ -581,6 +580,12 @@ void ConsumedStmtVisitor::VisitCastExpr(const CastExpr *Cast) { forwardInfo(Cast->getSubExpr(), Cast); } +void ConsumedStmtVisitor::VisitCXXBindTemporaryExpr( + const CXXBindTemporaryExpr *Temp) { + + forwardInfo(Temp->getSubExpr(), Temp); +} + void ConsumedStmtVisitor::VisitCXXConstructExpr(const CXXConstructExpr *Call) { CXXConstructorDecl *Constructor = Call->getConstructor(); @@ -622,6 +627,7 @@ void ConsumedStmtVisitor::VisitCXXConstructExpr(const CXXConstructExpr *Call) { } } + void ConsumedStmtVisitor::VisitCXXMemberCallExpr( const CXXMemberCallExpr *Call) { @@ -633,7 +639,7 @@ void ConsumedStmtVisitor::VisitCXXMemberCallExpr( PropagationInfo PInfo = Entry->second; const CXXMethodDecl *MethodDecl = Call->getMethodDecl(); - checkCallability(PInfo, MethodDecl, Call); + checkCallability(PInfo, MethodDecl, Call->getExprLoc()); if (PInfo.isVar()) { if (isTestingFunction(MethodDecl)) @@ -721,7 +727,7 @@ void ConsumedStmtVisitor::VisitCXXOperatorCallExpr( if (Entry != PropagationMap.end()) { PropagationInfo PInfo = Entry->second; - checkCallability(PInfo, FunDecl, Call); + checkCallability(PInfo, FunDecl, Call->getExprLoc()); if (PInfo.isVar()) { if (isTestingFunction(FunDecl)) @@ -1052,7 +1058,7 @@ void ConsumedStateMap::intersectAtLoopHead(const CFGBlock *LoopHead, ConsumedWarningsHandlerBase &WarningsHandler) { ConsumedState LocalState; - SourceLocation BlameLoc = getWarningLocForLoopExit(LoopBack); + SourceLocation BlameLoc = getLastStmtLoc(LoopBack); for (MapType::const_iterator DMI = LoopBackStates->Map.begin(), DME = LoopBackStates->Map.end(); DMI != DME; ++DMI) { @@ -1266,8 +1272,37 @@ void ConsumedAnalyzer::run(AnalysisDeclContext &AC) { case CFGElement::Statement: Visitor.Visit(BI->castAs<CFGStmt>().getStmt()); break; - case CFGElement::AutomaticObjectDtor: - CurrStates->remove(BI->castAs<CFGAutomaticObjDtor>().getVarDecl()); + + 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()); + break; + } + + case CFGElement::AutomaticObjectDtor: { + const CFGAutomaticObjDtor DTor = BI->castAs<CFGAutomaticObjDtor>(); + + 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); + } + break; + } + default: break; } diff --git a/clang/test/SemaCXX/warn-consumed-analysis.cpp b/clang/test/SemaCXX/warn-consumed-analysis.cpp index 0618f77b5f4..28b77947898 100644 --- a/clang/test/SemaCXX/warn-consumed-analysis.cpp +++ b/clang/test/SemaCXX/warn-consumed-analysis.cpp @@ -14,7 +14,7 @@ template <typename T> class CONSUMABLE(unconsumed) ConsumableClass { T var; - public: +public: ConsumableClass(); ConsumableClass(nullptr_t p) RETURN_TYPESTATE(consumed); ConsumableClass(T val) RETURN_TYPESTATE(unconsumed); @@ -46,6 +46,15 @@ class CONSUMABLE(unconsumed) ConsumableClass { void consume() CONSUMES; }; +class CONSUMABLE(unconsumed) DestructorTester { +public: + DestructorTester(int); + + void operator*(); + + ~DestructorTester() CALLABLE_WHEN("consumed"); +}; + void baf0(const ConsumableClass<int> var); void baf1(const ConsumableClass<int> &var); void baf2(const ConsumableClass<int> *var); @@ -83,6 +92,17 @@ void testInitialization() { } } +void testDestruction() { + DestructorTester D0(42), D1(42); + + *D0; + *D1; + + D0.~DestructorTester(); // expected-warning {{invalid invocation of method '~DestructorTester' on object 'D0' while it is in the 'unconsumed' state}} + + return; // expected-warning {{invalid invocation of method '~DestructorTester' on object 'D0' while it is in the 'unconsumed' state}} expected-warning {{invalid invocation of method '~DestructorTester' on object 'D1' while it is in the 'unconsumed' state}} +} + void testTempValue() { *ConsumableClass<int>(); // expected-warning {{invalid invocation of method 'operator*' on a temporary object while it is in the 'consumed' state}} } |

