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