xref: /llvm-project/clang/lib/Analysis/Consumed.cpp (revision e846deae3c40a1b17a3f1584153a7001f352338f)
1 //===- 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"
29 #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 
45 using namespace clang;
46 using namespace consumed;
47 
48 // Key method definition
49 ConsumedWarningsHandlerBase::~ConsumedWarningsHandlerBase() {}
50 
51 static 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 
67 namespace {
68 class 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;
101 
102   AnalysisDeclContext &AC;
103   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 
110 public:
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);
126 
127   ConsumedStmtVisitor(AnalysisDeclContext &AC, ConsumedAnalyzer &Analyzer,
128                       ConsumedStateMap *StateMap)
129       : AC(AC), Analyzer(Analyzer), StateMap(StateMap) {}
130 
131   void reset() {
132     PropagationMap.clear();
133   }
134 };
135 
136 void 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 
144 bool 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 
153 void 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 
165 void ConsumedStmtVisitor::Visit(const Stmt *StmtNode) {
166   ConstStmtVisitor<ConsumedStmtVisitor>::Visit(StmtNode);
167 
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 
175 void 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 
221 void 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 
228 void ConsumedStmtVisitor::VisitCXXConstructExpr(const CXXConstructExpr *Call) {
229   CXXConstructorDecl *Constructor = Call->getConstructor();
230 
231   ASTContext &CurrContext = AC.getASTContext();
232   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 
271 void 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 
291 void 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 
432 void 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 
438 void 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 
450 void 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 
459 void ConsumedStmtVisitor::VisitMemberExpr(const MemberExpr *MExpr) {
460   forwardInfo(MExpr->getBase(), MExpr);
461 }
462 
463 void 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 
472 void 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 
483 namespace {
484 
485 // TODO: Handle variable definitions, e.g. bool valid = x.isValid();
486 //       if (valid) ...; (Deferred)
487 class TestedVarsVisitor : public RecursiveASTVisitor<TestedVarsVisitor> {
488 
489   bool Invert;
490   SourceLocation CurrTestLoc;
491 
492   ConsumedStateMap *StateMap;
493 
494 public:
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 
506 bool 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 
522 bool 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 
535 bool 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 
548 namespace clang {
549 namespace consumed {
550 
551 void 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 
571 void 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 
590 ConsumedStateMap* ConsumedBlockInfo::getInfo(const CFGBlock *Block) {
591   return StateMapsArray[Block->getBlockID()];
592 }
593 
594 void ConsumedBlockInfo::markVisited(const CFGBlock *Block) {
595   VisitedBlocks.insert(Block);
596 }
597 
598 ConsumedState 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 
609 void 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 
622 void 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 
635 void ConsumedStateMap::setState(const VarDecl *Var, ConsumedState State) {
636   Map[Var] = State;
637 }
638 
639 
640 bool 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)
657 bool 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)
680 void 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 
716 void 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);
736 
737     ConsumedStmtVisitor Visitor(AC, *this, CurrStates);
738 
739     // 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 
788 bool isTestingFunction(const CXXMethodDecl *Method) {
789   return Method->hasAttr<TestsUnconsumedAttr>();
790 }
791 
792 }} // end namespace clang::consumed
793