blob: e556a8cef5cf52dbf7497a424fa7da3b7b3c70bc [file] [log] [blame]
DeLesley Hutchinsdf7bef02013-08-12 21:20:55 +00001//===- Consumed.cpp --------------------------------------------*- C++ --*-===//
2//
3// The LLVM Compiler Infrastructure
4//
5// This file is distributed under the University of Illinois Open Source
6// License. See LICENSE.TXT for details.
7//
8//===----------------------------------------------------------------------===//
9//
10// A intra-procedural analysis for checking consumed properties. This is based,
11// in part, on research on linear types.
12//
13//===----------------------------------------------------------------------===//
14
15#include "clang/AST/ASTContext.h"
16#include "clang/AST/Attr.h"
17#include "clang/AST/DeclCXX.h"
18#include "clang/AST/ExprCXX.h"
19#include "clang/AST/RecursiveASTVisitor.h"
20#include "clang/AST/StmtVisitor.h"
21#include "clang/AST/StmtCXX.h"
22#include "clang/AST/Type.h"
23#include "clang/Analysis/Analyses/PostOrderCFGView.h"
24#include "clang/Analysis/AnalysisContext.h"
25#include "clang/Analysis/CFG.h"
26#include "clang/Analysis/Analyses/Consumed.h"
27#include "clang/Basic/OperatorKinds.h"
28#include "clang/Basic/SourceLocation.h"
DeLesley Hutchinsdf7bef02013-08-12 21:20:55 +000029#include "llvm/ADT/DenseMap.h"
30#include "llvm/ADT/SmallVector.h"
31#include "llvm/Support/raw_ostream.h"
32
33// TODO: Add support for methods with CallableWhenUnconsumed.
34// TODO: Mark variables as Unknown going into while- or for-loops only if they
35// are referenced inside that block. (Deferred)
36// TODO: Add a method(s) to identify which method calls perform what state
37// transitions. (Deferred)
38// TODO: Take notes on state transitions to provide better warning messages.
39// (Deferred)
40// TODO: Test nested conditionals: A) Checking the same value multiple times,
41// and 2) Checking different values. (Deferred)
42// TODO: Test IsFalseVisitor with values in the unknown state. (Deferred)
43// TODO: Look into combining IsFalseVisitor and TestedVarsVisitor. (Deferred)
44
45using namespace clang;
46using namespace consumed;
47
48// Key method definition
49ConsumedWarningsHandlerBase::~ConsumedWarningsHandlerBase() {}
50
51static StringRef stateToString(ConsumedState State) {
52 switch (State) {
53 case consumed::CS_None:
54 return "none";
55
56 case consumed::CS_Unknown:
57 return "unknown";
58
59 case consumed::CS_Unconsumed:
60 return "unconsumed";
61
62 case consumed::CS_Consumed:
63 return "consumed";
64 }
65}
66
67namespace {
68class ConsumedStmtVisitor : public ConstStmtVisitor<ConsumedStmtVisitor> {
69
70 union PropagationUnion {
71 ConsumedState State;
72 const VarDecl *Var;
73 };
74
75 class PropagationInfo {
76 PropagationUnion StateOrVar;
77
78 public:
79 bool IsVar;
80
81 PropagationInfo() : IsVar(false) {
82 StateOrVar.State = consumed::CS_None;
83 }
84
85 PropagationInfo(ConsumedState State) : IsVar(false) {
86 StateOrVar.State = State;
87 }
88
89 PropagationInfo(const VarDecl *Var) : IsVar(true) {
90 StateOrVar.Var = Var;
91 }
92
93 ConsumedState getState() { return StateOrVar.State; };
94
95 const VarDecl * getVar() { return IsVar ? StateOrVar.Var : NULL; };
96 };
97
98 typedef llvm::DenseMap<const Stmt *, PropagationInfo> MapType;
99 typedef std::pair<const Stmt *, PropagationInfo> PairType;
100 typedef MapType::iterator InfoEntry;
Reid Kleckner2d84f6b2013-08-12 23:49:39 +0000101
102 AnalysisDeclContext &AC;
DeLesley Hutchinsdf7bef02013-08-12 21:20:55 +0000103 ConsumedAnalyzer &Analyzer;
104 ConsumedStateMap *StateMap;
105 MapType PropagationMap;
106
107 void forwardInfo(const Stmt *From, const Stmt *To);
108 bool isLikeMoveAssignment(const CXXMethodDecl *MethodDecl);
109
110public:
111
112 void Visit(const Stmt *StmtNode);
113
114 void VisitBinaryOperator(const BinaryOperator *BinOp);
115 void VisitCallExpr(const CallExpr *Call);
116 void VisitCastExpr(const CastExpr *Cast);
117 void VisitCXXConstructExpr(const CXXConstructExpr *Call);
118 void VisitCXXMemberCallExpr(const CXXMemberCallExpr *Call);
119 void VisitCXXOperatorCallExpr(const CXXOperatorCallExpr *Call);
120 void VisitDeclRefExpr(const DeclRefExpr *DeclRef);
121 void VisitDeclStmt(const DeclStmt *DelcS);
122 void VisitMaterializeTemporaryExpr(const MaterializeTemporaryExpr *Temp);
123 void VisitMemberExpr(const MemberExpr *MExpr);
124 void VisitUnaryOperator(const UnaryOperator *UOp);
125 void VisitVarDecl(const VarDecl *Var);
Reid Kleckner2d84f6b2013-08-12 23:49:39 +0000126
127 ConsumedStmtVisitor(AnalysisDeclContext &AC, ConsumedAnalyzer &Analyzer,
128 ConsumedStateMap *StateMap)
129 : AC(AC), Analyzer(Analyzer), StateMap(StateMap) {}
130
DeLesley Hutchinsdf7bef02013-08-12 21:20:55 +0000131 void reset() {
132 PropagationMap.clear();
133 }
134};
135
136void ConsumedStmtVisitor::forwardInfo(const Stmt *From, const Stmt *To) {
137 InfoEntry Entry = PropagationMap.find(From);
138
139 if (Entry != PropagationMap.end()) {
140 PropagationMap.insert(PairType(To, PropagationInfo(Entry->second)));
141 }
142}
143
144bool ConsumedStmtVisitor::isLikeMoveAssignment(
145 const CXXMethodDecl *MethodDecl) {
146
147 return MethodDecl->isMoveAssignmentOperator() ||
148 (MethodDecl->getOverloadedOperator() == OO_Equal &&
149 MethodDecl->getNumParams() == 1 &&
150 MethodDecl->getParamDecl(0)->getType()->isRValueReferenceType());
151}
152
153void ConsumedStmtVisitor::VisitBinaryOperator(const BinaryOperator *BinOp) {
154 switch (BinOp->getOpcode()) {
155 case BO_PtrMemD:
156 case BO_PtrMemI:
157 forwardInfo(BinOp->getLHS(), BinOp);
158 break;
159
160 default:
161 break;
162 }
163}
164
165void ConsumedStmtVisitor::Visit(const Stmt *StmtNode) {
Hans Wennborg2d66dfa2013-08-12 22:02:09 +0000166 ConstStmtVisitor<ConsumedStmtVisitor>::Visit(StmtNode);
DeLesley Hutchinsdf7bef02013-08-12 21:20:55 +0000167
168 for (Stmt::const_child_iterator CI = StmtNode->child_begin(),
169 CE = StmtNode->child_end(); CI != CE; ++CI) {
170
171 PropagationMap.erase(*CI);
172 }
173}
174
175void ConsumedStmtVisitor::VisitCallExpr(const CallExpr *Call) {
176 if (const FunctionDecl *FunDecl =
177 dyn_cast_or_null<FunctionDecl>(Call->getDirectCallee())) {
178
179 // Special case for the std::move function.
180 // TODO: Make this more specific. (Deferred)
181 if (FunDecl->getNameAsString() == "move") {
182 InfoEntry Entry = PropagationMap.find(Call->getArg(0));
183
184 if (Entry != PropagationMap.end()) {
185 PropagationMap.insert(PairType(Call, Entry->second));
186 }
187
188 return;
189 }
190
191 unsigned Offset = Call->getNumArgs() - FunDecl->getNumParams();
192
193 for (unsigned Index = Offset; Index < Call->getNumArgs(); ++Index) {
194 QualType ParamType = FunDecl->getParamDecl(Index - Offset)->getType();
195
196 InfoEntry Entry = PropagationMap.find(Call->getArg(Index));
197
198 if (Entry == PropagationMap.end() || !Entry->second.IsVar) {
199 continue;
200 }
201
202 PropagationInfo PState = Entry->second;
203
204 if (ParamType->isRValueReferenceType() ||
205 (ParamType->isLValueReferenceType() &&
206 !cast<LValueReferenceType>(*ParamType).isSpelledAsLValue())) {
207
208 StateMap->setState(PState.getVar(), consumed::CS_Consumed);
209
210 } else if (!(ParamType.isConstQualified() ||
211 ((ParamType->isReferenceType() ||
212 ParamType->isPointerType()) &&
213 ParamType->getPointeeType().isConstQualified()))) {
214
215 StateMap->setState(PState.getVar(), consumed::CS_Unknown);
216 }
217 }
218 }
219}
220
221void ConsumedStmtVisitor::VisitCastExpr(const CastExpr *Cast) {
222 InfoEntry Entry = PropagationMap.find(Cast->getSubExpr());
223
224 if (Entry != PropagationMap.end())
225 PropagationMap.insert(PairType(Cast, Entry->second));
226}
227
228void ConsumedStmtVisitor::VisitCXXConstructExpr(const CXXConstructExpr *Call) {
229 CXXConstructorDecl *Constructor = Call->getConstructor();
Reid Kleckner2d84f6b2013-08-12 23:49:39 +0000230
231 ASTContext &CurrContext = AC.getASTContext();
DeLesley Hutchinsdf7bef02013-08-12 21:20:55 +0000232 QualType ThisType = Constructor->getThisType(CurrContext)->getPointeeType();
233
234 if (Analyzer.isConsumableType(ThisType)) {
235 if (Constructor->hasAttr<ConsumesAttr>() ||
236 Constructor->isDefaultConstructor()) {
237
238 PropagationMap.insert(PairType(Call,
239 PropagationInfo(consumed::CS_Consumed)));
240
241 } else if (Constructor->isMoveConstructor()) {
242
243 PropagationInfo PState =
244 PropagationMap.find(Call->getArg(0))->second;
245
246 if (PState.IsVar) {
247 const VarDecl* Var = PState.getVar();
248
249 PropagationMap.insert(PairType(Call,
250 PropagationInfo(StateMap->getState(Var))));
251
252 StateMap->setState(Var, consumed::CS_Consumed);
253
254 } else {
255 PropagationMap.insert(PairType(Call, PState));
256 }
257
258 } else if (Constructor->isCopyConstructor()) {
259 MapType::iterator Entry = PropagationMap.find(Call->getArg(0));
260
261 if (Entry != PropagationMap.end())
262 PropagationMap.insert(PairType(Call, Entry->second));
263
264 } else {
265 PropagationMap.insert(PairType(Call,
266 PropagationInfo(consumed::CS_Unconsumed)));
267 }
268 }
269}
270
271void ConsumedStmtVisitor::VisitCXXMemberCallExpr(
272 const CXXMemberCallExpr *Call) {
273
274 VisitCallExpr(Call);
275
276 InfoEntry Entry = PropagationMap.find(Call->getCallee()->IgnoreParens());
277
278 if (Entry != PropagationMap.end()) {
279 PropagationInfo PState = Entry->second;
280 if (!PState.IsVar) return;
281
282 const CXXMethodDecl *Method = Call->getMethodDecl();
283
284 if (Method->hasAttr<ConsumesAttr>())
285 StateMap->setState(PState.getVar(), consumed::CS_Consumed);
286 else if (!Method->isConst())
287 StateMap->setState(PState.getVar(), consumed::CS_Unknown);
288 }
289}
290
291void ConsumedStmtVisitor::VisitCXXOperatorCallExpr(
292 const CXXOperatorCallExpr *Call) {
293
294 const FunctionDecl *FunDecl =
295 dyn_cast_or_null<FunctionDecl>(Call->getDirectCallee());
296
297 if (!FunDecl) return;
298
299 if (isa<CXXMethodDecl>(FunDecl) &&
300 isLikeMoveAssignment(cast<CXXMethodDecl>(FunDecl))) {
301
302 InfoEntry LEntry = PropagationMap.find(Call->getArg(0));
303 InfoEntry REntry = PropagationMap.find(Call->getArg(1));
304
305 PropagationInfo LPState, RPState;
306
307 if (LEntry != PropagationMap.end() &&
308 REntry != PropagationMap.end()) {
309
310 LPState = LEntry->second;
311 RPState = REntry->second;
312
313 if (LPState.IsVar && RPState.IsVar) {
314 StateMap->setState(LPState.getVar(),
315 StateMap->getState(RPState.getVar()));
316
317 StateMap->setState(RPState.getVar(), consumed::CS_Consumed);
318
319 PropagationMap.insert(PairType(Call, LPState));
320
321 } else if (LPState.IsVar && !RPState.IsVar) {
322 StateMap->setState(LPState.getVar(), RPState.getState());
323
324 PropagationMap.insert(PairType(Call, LPState));
325
326 } else if (!LPState.IsVar && RPState.IsVar) {
327 PropagationMap.insert(PairType(Call,
328 PropagationInfo(StateMap->getState(RPState.getVar()))));
329
330 StateMap->setState(RPState.getVar(), consumed::CS_Consumed);
331
332 } else {
333 PropagationMap.insert(PairType(Call, RPState));
334 }
335
336 } else if (LEntry != PropagationMap.end() &&
337 REntry == PropagationMap.end()) {
338
339 LPState = LEntry->second;
340
341 if (LPState.IsVar) {
342 StateMap->setState(LPState.getVar(), consumed::CS_Unknown);
343
344 PropagationMap.insert(PairType(Call, LPState));
345
346 } else {
347 PropagationMap.insert(PairType(Call,
348 PropagationInfo(consumed::CS_Unknown)));
349 }
350
351 } else if (LEntry == PropagationMap.end() &&
352 REntry != PropagationMap.end()) {
353
354 RPState = REntry->second;
355
356 if (RPState.IsVar) {
357 const VarDecl *Var = RPState.getVar();
358
359 PropagationMap.insert(PairType(Call,
360 PropagationInfo(StateMap->getState(Var))));
361
362 StateMap->setState(Var, consumed::CS_Consumed);
363
364 } else {
365 PropagationMap.insert(PairType(Call, RPState));
366 }
367 }
368
369 } else {
370
371 VisitCallExpr(Call);
372
373 InfoEntry Entry = PropagationMap.find(Call->getArg(0));
374
375 if (Entry != PropagationMap.end()) {
376
377 PropagationInfo PState = Entry->second;
378
379 // TODO: When we support CallableWhenConsumed this will have to check for
380 // the different attributes and change the behavior bellow.
381 // (Deferred)
382 if (FunDecl->hasAttr<CallableWhenUnconsumedAttr>()) {
383 if (PState.IsVar) {
384 const VarDecl *Var = PState.getVar();
385
386 switch (StateMap->getState(Var)) {
387 case CS_Consumed:
388 Analyzer.WarningsHandler.warnUseWhileConsumed(
389 FunDecl->getNameAsString(), Var->getNameAsString(),
390 Call->getExprLoc());
391 break;
392
393 case CS_Unknown:
394 Analyzer.WarningsHandler.warnUseInUnknownState(
395 FunDecl->getNameAsString(), Var->getNameAsString(),
396 Call->getExprLoc());
397 break;
398
399 default:
400 break;
401 }
402
403 } else {
404 switch (PState.getState()) {
405 case CS_Consumed:
406 Analyzer.WarningsHandler.warnUseOfTempWhileConsumed(
407 FunDecl->getNameAsString(), Call->getExprLoc());
408 break;
409
410 case CS_Unknown:
411 Analyzer.WarningsHandler.warnUseOfTempInUnknownState(
412 FunDecl->getNameAsString(), Call->getExprLoc());
413 break;
414
415 default:
416 break;
417 }
418 }
419 }
420
421 // Handle non-constant member operators.
422 if (const CXXMethodDecl *MethodDecl =
423 dyn_cast_or_null<CXXMethodDecl>(FunDecl)) {
424
425 if (!MethodDecl->isConst() && PState.IsVar)
426 StateMap->setState(PState.getVar(), consumed::CS_Unknown);
427 }
428 }
429 }
430}
431
432void ConsumedStmtVisitor::VisitDeclRefExpr(const DeclRefExpr *DeclRef) {
433 if (const VarDecl *Var = dyn_cast_or_null<VarDecl>(DeclRef->getDecl()))
434 if (StateMap->getState(Var) != consumed::CS_None)
435 PropagationMap.insert(PairType(DeclRef, PropagationInfo(Var)));
436}
437
438void ConsumedStmtVisitor::VisitDeclStmt(const DeclStmt *DeclS) {
439 for (DeclStmt::const_decl_iterator DI = DeclS->decl_begin(),
440 DE = DeclS->decl_end(); DI != DE; ++DI) {
441
442 if (isa<VarDecl>(*DI)) VisitVarDecl(cast<VarDecl>(*DI));
443 }
444
445 if (DeclS->isSingleDecl())
446 if (const VarDecl *Var = dyn_cast_or_null<VarDecl>(DeclS->getSingleDecl()))
447 PropagationMap.insert(PairType(DeclS, PropagationInfo(Var)));
448}
449
450void ConsumedStmtVisitor::VisitMaterializeTemporaryExpr(
451 const MaterializeTemporaryExpr *Temp) {
452
453 InfoEntry Entry = PropagationMap.find(Temp->GetTemporaryExpr());
454
455 if (Entry != PropagationMap.end())
456 PropagationMap.insert(PairType(Temp, Entry->second));
457}
458
459void ConsumedStmtVisitor::VisitMemberExpr(const MemberExpr *MExpr) {
460 forwardInfo(MExpr->getBase(), MExpr);
461}
462
463void ConsumedStmtVisitor::VisitUnaryOperator(const UnaryOperator *UOp) {
464 if (UOp->getOpcode() == UO_AddrOf) {
465 InfoEntry Entry = PropagationMap.find(UOp->getSubExpr());
466
467 if (Entry != PropagationMap.end())
468 PropagationMap.insert(PairType(UOp, Entry->second));
469 }
470}
471
472void ConsumedStmtVisitor::VisitVarDecl(const VarDecl *Var) {
473 if (Analyzer.isConsumableType(Var->getType())) {
474 PropagationInfo PState =
475 PropagationMap.find(Var->getInit())->second;
476
477 StateMap->setState(Var, PState.IsVar ?
478 StateMap->getState(PState.getVar()) : PState.getState());
479 }
480}
481} // end anonymous::ConsumedStmtVisitor
482
483namespace {
484
485// TODO: Handle variable definitions, e.g. bool valid = x.isValid();
486// if (valid) ...; (Deferred)
487class TestedVarsVisitor : public RecursiveASTVisitor<TestedVarsVisitor> {
488
489 bool Invert;
490 SourceLocation CurrTestLoc;
491
492 ConsumedStateMap *StateMap;
493
494public:
495 bool IsUsefulConditional;
496 VarTestResult Test;
497
498 TestedVarsVisitor(ConsumedStateMap *StateMap) : Invert(false),
499 StateMap(StateMap), IsUsefulConditional(false) {}
500
501 bool VisitCallExpr(CallExpr *Call);
502 bool VisitDeclRefExpr(DeclRefExpr *DeclRef);
503 bool VisitUnaryOperator(UnaryOperator *UnaryOp);
504};
505
506bool TestedVarsVisitor::VisitCallExpr(CallExpr *Call) {
507 if (const CXXMethodDecl *Method =
508 dyn_cast_or_null<CXXMethodDecl>(Call->getDirectCallee())) {
509
510 if (isTestingFunction(Method)) {
511 CurrTestLoc = Call->getExprLoc();
512 IsUsefulConditional = true;
513 return true;
514 }
515
516 IsUsefulConditional = false;
517 }
518
519 return false;
520}
521
522bool TestedVarsVisitor::VisitDeclRefExpr(DeclRefExpr *DeclRef) {
523 if (const VarDecl *Var = dyn_cast_or_null<VarDecl>(DeclRef->getDecl())) {
524 if (StateMap->getState(Var) != consumed::CS_None) {
525 Test = VarTestResult(Var, CurrTestLoc, !Invert);
526 }
527
528 } else {
529 IsUsefulConditional = false;
530 }
531
532 return IsUsefulConditional;
533}
534
535bool TestedVarsVisitor::VisitUnaryOperator(UnaryOperator *UnaryOp) {
536 if (UnaryOp->getOpcode() == UO_LNot) {
537 Invert = true;
538 TraverseStmt(UnaryOp->getSubExpr());
539
540 } else {
541 IsUsefulConditional = false;
542 }
543
544 return false;
545}
546} // end anonymouse::TestedVarsVisitor
547
548namespace clang {
549namespace consumed {
550
551void ConsumedBlockInfo::addInfo(const CFGBlock *Block,
552 ConsumedStateMap *StateMap,
553 bool &AlreadyOwned) {
554
555 if (VisitedBlocks.alreadySet(Block)) return;
556
557 ConsumedStateMap *Entry = StateMapsArray[Block->getBlockID()];
558
559 if (Entry) {
560 Entry->intersect(StateMap);
561
562 } else if (AlreadyOwned) {
563 StateMapsArray[Block->getBlockID()] = new ConsumedStateMap(*StateMap);
564
565 } else {
566 StateMapsArray[Block->getBlockID()] = StateMap;
567 AlreadyOwned = true;
568 }
569}
570
571void ConsumedBlockInfo::addInfo(const CFGBlock *Block,
572 ConsumedStateMap *StateMap) {
573
574 if (VisitedBlocks.alreadySet(Block)) {
575 delete StateMap;
576 return;
577 }
578
579 ConsumedStateMap *Entry = StateMapsArray[Block->getBlockID()];
580
581 if (Entry) {
582 Entry->intersect(StateMap);
583 delete StateMap;
584
585 } else {
586 StateMapsArray[Block->getBlockID()] = StateMap;
587 }
588}
589
590ConsumedStateMap* ConsumedBlockInfo::getInfo(const CFGBlock *Block) {
591 return StateMapsArray[Block->getBlockID()];
592}
593
594void ConsumedBlockInfo::markVisited(const CFGBlock *Block) {
595 VisitedBlocks.insert(Block);
596}
597
598ConsumedState ConsumedStateMap::getState(const VarDecl *Var) {
599 MapType::const_iterator Entry = Map.find(Var);
600
601 if (Entry != Map.end()) {
602 return Entry->second;
603
604 } else {
605 return CS_None;
606 }
607}
608
609void ConsumedStateMap::intersect(const ConsumedStateMap *Other) {
610 ConsumedState LocalState;
611
612 for (MapType::const_iterator DMI = Other->Map.begin(),
613 DME = Other->Map.end(); DMI != DME; ++DMI) {
614
615 LocalState = this->getState(DMI->first);
616
617 if (LocalState != CS_None && LocalState != DMI->second)
618 setState(DMI->first, CS_Unknown);
619 }
620}
621
622void ConsumedStateMap::makeUnknown() {
623 PairType Pair;
624
625 for (MapType::const_iterator DMI = Map.begin(), DME = Map.end(); DMI != DME;
626 ++DMI) {
627
628 Pair = *DMI;
629
630 Map.erase(Pair.first);
631 Map.insert(PairType(Pair.first, CS_Unknown));
632 }
633}
634
635void ConsumedStateMap::setState(const VarDecl *Var, ConsumedState State) {
636 Map[Var] = State;
637}
638
DeLesley Hutchinsdf7bef02013-08-12 21:20:55 +0000639
640bool ConsumedAnalyzer::isConsumableType(QualType Type) {
641 const CXXRecordDecl *RD =
642 dyn_cast_or_null<CXXRecordDecl>(Type->getAsCXXRecordDecl());
643
644 if (!RD) return false;
645
646 std::pair<CacheMapType::iterator, bool> Entry =
647 ConsumableTypeCache.insert(std::make_pair(RD, false));
648
649 if (Entry.second)
650 Entry.first->second = hasConsumableAttributes(RD);
651
652 return Entry.first->second;
653}
654
655// TODO: Walk the base classes to see if any of them are unique types.
656// (Deferred)
657bool ConsumedAnalyzer::hasConsumableAttributes(const CXXRecordDecl *RD) {
658 for (CXXRecordDecl::method_iterator MI = RD->method_begin(),
659 ME = RD->method_end(); MI != ME; ++MI) {
660
661 for (Decl::attr_iterator AI = (*MI)->attr_begin(), AE = (*MI)->attr_end();
662 AI != AE; ++AI) {
663
664 switch ((*AI)->getKind()) {
665 case attr::CallableWhenUnconsumed:
666 case attr::TestsUnconsumed:
667 return true;
668
669 default:
670 break;
671 }
672 }
673 }
674
675 return false;
676}
677
678// TODO: Handle other forms of branching with precision, including while- and
679// for-loops. (Deferred)
680void ConsumedAnalyzer::splitState(const CFGBlock *CurrBlock,
681 const IfStmt *Terminator) {
682
683 TestedVarsVisitor Visitor(CurrStates);
684 Visitor.TraverseStmt(const_cast<Expr*>(Terminator->getCond()));
685
686 bool HasElse = Terminator->getElse() != NULL;
687
688 ConsumedStateMap *ElseOrMergeStates = new ConsumedStateMap(*CurrStates);
689
690 if (Visitor.IsUsefulConditional) {
691 ConsumedState VarState = CurrStates->getState(Visitor.Test.Var);
692
693 if (VarState != CS_Unknown) {
694 // FIXME: Make this not warn if the test is from a macro expansion.
695 // (Deferred)
696 WarningsHandler.warnUnnecessaryTest(Visitor.Test.Var->getNameAsString(),
697 stateToString(VarState), Visitor.Test.Loc);
698 }
699
700 if (Visitor.Test.UnconsumedInTrueBranch) {
701 CurrStates->setState(Visitor.Test.Var, CS_Unconsumed);
702 if (HasElse) ElseOrMergeStates->setState(Visitor.Test.Var, CS_Consumed);
703
704 } else {
705 CurrStates->setState(Visitor.Test.Var, CS_Consumed);
706 if (HasElse) ElseOrMergeStates->setState(Visitor.Test.Var, CS_Unconsumed);
707 }
708 }
709
710 CFGBlock::const_succ_iterator SI = CurrBlock->succ_begin();
711
712 if (*SI) BlockInfo.addInfo(*SI, CurrStates);
713 if (*++SI) BlockInfo.addInfo(*SI, ElseOrMergeStates);
714}
715
716void ConsumedAnalyzer::run(AnalysisDeclContext &AC) {
717 const FunctionDecl *D = dyn_cast_or_null<FunctionDecl>(AC.getDecl());
718
719 if (!D) return;
720
721 BlockInfo = ConsumedBlockInfo(AC.getCFG());
722
723 PostOrderCFGView *SortedGraph = AC.getAnalysis<PostOrderCFGView>();
724
725 CurrStates = new ConsumedStateMap();
726
727 // Visit all of the function's basic blocks.
728 for (PostOrderCFGView::iterator I = SortedGraph->begin(),
729 E = SortedGraph->end(); I != E; ++I) {
730
731 const CFGBlock *CurrBlock = *I;
732 BlockInfo.markVisited(CurrBlock);
733
734 if (CurrStates == NULL)
735 CurrStates = BlockInfo.getInfo(CurrBlock);
Reid Kleckner2d84f6b2013-08-12 23:49:39 +0000736
737 ConsumedStmtVisitor Visitor(AC, *this, CurrStates);
738
DeLesley Hutchinsdf7bef02013-08-12 21:20:55 +0000739 // Visit all of the basic block's statements.
740 for (CFGBlock::const_iterator BI = CurrBlock->begin(),
741 BE = CurrBlock->end(); BI != BE; ++BI) {
742
743 if (BI->getKind() == CFGElement::Statement)
744 Visitor.Visit(BI->castAs<CFGStmt>().getStmt());
745 }
746
747 // TODO: Remove any variables that have reached the end of their
748 // lifetimes from the state map. (Deferred)
749
750 if (const IfStmt *Terminator =
751 dyn_cast_or_null<IfStmt>(CurrBlock->getTerminator().getStmt())) {
752
753 splitState(CurrBlock, Terminator);
754 CurrStates = NULL;
755
756 } else if (CurrBlock->succ_size() > 1) {
757 CurrStates->makeUnknown();
758
759 bool OwnershipTaken = false;
760
761 for (CFGBlock::const_succ_iterator SI = CurrBlock->succ_begin(),
762 SE = CurrBlock->succ_end(); SI != SE; ++SI) {
763
764 if (*SI) BlockInfo.addInfo(*SI, CurrStates, OwnershipTaken);
765 }
766
767 if (!OwnershipTaken)
768 delete CurrStates;
769
770 CurrStates = NULL;
771
772 } else if (CurrBlock->succ_size() == 1 &&
773 (*CurrBlock->succ_begin())->pred_size() > 1) {
774
775 BlockInfo.addInfo(*CurrBlock->succ_begin(), CurrStates);
776 CurrStates = NULL;
777 }
778
779 Visitor.reset();
780 } // End of block iterator.
781
782 // Delete the last existing state map.
783 delete CurrStates;
784
785 WarningsHandler.emitDiagnostics();
786}
787
DeLesley Hutchinsdf7bef02013-08-12 21:20:55 +0000788bool isTestingFunction(const CXXMethodDecl *Method) {
789 return Method->hasAttr<TestsUnconsumedAttr>();
790}
791
792}} // end namespace clang::consumed