Consumed.cpp revision b7dc1f5f30e1e46e44389b036d48f9614cfd5fe3
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/Compiler.h"
32#include "llvm/Support/raw_ostream.h"
33
34// TODO: Correctly identify unreachable blocks when chaining boolean operators.
35// TODO: Warn about unreachable code.
36// TODO: Switch to using a bitmap to track unreachable blocks.
37// TODO: Mark variables as Unknown going into while- or for-loops only if they
38//       are referenced inside that block. (Deferred)
39// TODO: Handle variable definitions, e.g. bool valid = x.isValid();
40//       if (valid) ...; (Deferred)
41// TODO: Add a method(s) to identify which method calls perform what state
42//       transitions. (Deferred)
43// TODO: Take notes on state transitions to provide better warning messages.
44//       (Deferred)
45// TODO: Test nested conditionals: A) Checking the same value multiple times,
46//       and 2) Checking different values. (Deferred)
47// TODO: Test IsFalseVisitor with values in the unknown state. (Deferred)
48// TODO: Look into combining IsFalseVisitor and TestedVarsVisitor. (Deferred)
49
50using namespace clang;
51using namespace consumed;
52
53// Key method definition
54ConsumedWarningsHandlerBase::~ConsumedWarningsHandlerBase() {}
55
56static ConsumedState invertConsumedUnconsumed(ConsumedState State) {
57  switch (State) {
58  case CS_Unconsumed:
59    return CS_Consumed;
60  case CS_Consumed:
61    return CS_Unconsumed;
62  case CS_None:
63    return CS_None;
64  case CS_Unknown:
65    return CS_Unknown;
66  }
67  llvm_unreachable("invalid enum");
68}
69
70static bool isKnownState(ConsumedState State) {
71  switch (State) {
72  case CS_Unconsumed:
73  case CS_Consumed:
74    return true;
75  case CS_None:
76  case CS_Unknown:
77    return false;
78  }
79}
80
81static bool isTestingFunction(const FunctionDecl *FunDecl) {
82  return FunDecl->hasAttr<TestsUnconsumedAttr>();
83}
84
85static StringRef stateToString(ConsumedState State) {
86  switch (State) {
87  case consumed::CS_None:
88    return "none";
89
90  case consumed::CS_Unknown:
91    return "unknown";
92
93  case consumed::CS_Unconsumed:
94    return "unconsumed";
95
96  case consumed::CS_Consumed:
97    return "consumed";
98  }
99  llvm_unreachable("invalid enum");
100}
101
102namespace {
103struct VarTestResult {
104  const VarDecl *Var;
105  ConsumedState TestsFor;
106};
107} // end anonymous::VarTestResult
108
109namespace clang {
110namespace consumed {
111
112enum EffectiveOp {
113  EO_And,
114  EO_Or
115};
116
117class PropagationInfo {
118  enum {
119    IT_None,
120    IT_State,
121    IT_Test,
122    IT_BinTest,
123    IT_Var
124  } InfoType;
125
126  union {
127    ConsumedState State;
128    VarTestResult Test;
129    const VarDecl *Var;
130    struct {
131      const BinaryOperator *Source;
132      EffectiveOp EOp;
133      VarTestResult LTest;
134      VarTestResult RTest;
135    } BinTest;
136  };
137
138public:
139  PropagationInfo() : InfoType(IT_None) {}
140
141  PropagationInfo(const VarTestResult &Test) : InfoType(IT_Test), Test(Test) {}
142  PropagationInfo(const VarDecl *Var, ConsumedState TestsFor)
143    : InfoType(IT_Test) {
144
145    Test.Var      = Var;
146    Test.TestsFor = TestsFor;
147  }
148
149  PropagationInfo(const BinaryOperator *Source, EffectiveOp EOp,
150                  const VarTestResult &LTest, const VarTestResult &RTest)
151    : InfoType(IT_BinTest) {
152
153    BinTest.Source  = Source;
154    BinTest.EOp     = EOp;
155    BinTest.LTest   = LTest;
156    BinTest.RTest   = RTest;
157  }
158
159  PropagationInfo(const BinaryOperator *Source, EffectiveOp EOp,
160                  const VarDecl *LVar, ConsumedState LTestsFor,
161                  const VarDecl *RVar, ConsumedState RTestsFor)
162    : InfoType(IT_BinTest) {
163
164    BinTest.Source         = Source;
165    BinTest.EOp            = EOp;
166    BinTest.LTest.Var      = LVar;
167    BinTest.LTest.TestsFor = LTestsFor;
168    BinTest.RTest.Var      = RVar;
169    BinTest.RTest.TestsFor = RTestsFor;
170  }
171
172  PropagationInfo(ConsumedState State) : InfoType(IT_State), State(State) {}
173  PropagationInfo(const VarDecl *Var) : InfoType(IT_Var), Var(Var) {}
174
175  const ConsumedState & getState() const {
176    assert(InfoType == IT_State);
177    return State;
178  }
179
180  const VarTestResult & getTest() const {
181    assert(InfoType == IT_Test);
182    return Test;
183  }
184
185  const VarTestResult & getLTest() const {
186    assert(InfoType == IT_BinTest);
187    return BinTest.LTest;
188  }
189
190  const VarTestResult & getRTest() const {
191    assert(InfoType == IT_BinTest);
192    return BinTest.RTest;
193  }
194
195  const VarDecl * getVar() const {
196    assert(InfoType == IT_Var);
197    return Var;
198  }
199
200  EffectiveOp testEffectiveOp() const {
201    assert(InfoType == IT_BinTest);
202    return BinTest.EOp;
203  }
204
205  const BinaryOperator * testSourceNode() const {
206    assert(InfoType == IT_BinTest);
207    return BinTest.Source;
208  }
209
210  bool isValid()   const { return InfoType != IT_None;     }
211  bool isState()   const { return InfoType == IT_State;    }
212  bool isTest()    const { return InfoType == IT_Test;     }
213  bool isBinTest() const { return InfoType == IT_BinTest;  }
214  bool isVar()     const { return InfoType == IT_Var;      }
215
216  PropagationInfo invertTest() const {
217    assert(InfoType == IT_Test || InfoType == IT_BinTest);
218
219    if (InfoType == IT_Test) {
220      return PropagationInfo(Test.Var, invertConsumedUnconsumed(Test.TestsFor));
221
222    } else if (InfoType == IT_BinTest) {
223      return PropagationInfo(BinTest.Source,
224        BinTest.EOp == EO_And ? EO_Or : EO_And,
225        BinTest.LTest.Var, invertConsumedUnconsumed(BinTest.LTest.TestsFor),
226        BinTest.RTest.Var, invertConsumedUnconsumed(BinTest.RTest.TestsFor));
227    } else {
228      return PropagationInfo();
229    }
230  }
231};
232
233class ConsumedStmtVisitor : public ConstStmtVisitor<ConsumedStmtVisitor> {
234
235  typedef llvm::DenseMap<const Stmt *, PropagationInfo> MapType;
236  typedef std::pair<const Stmt *, PropagationInfo> PairType;
237  typedef MapType::iterator InfoEntry;
238  typedef MapType::const_iterator ConstInfoEntry;
239
240  AnalysisDeclContext &AC;
241  ConsumedAnalyzer &Analyzer;
242  ConsumedStateMap *StateMap;
243  MapType PropagationMap;
244
245  void checkCallability(const PropagationInfo &PInfo,
246                        const FunctionDecl *FunDecl,
247                        const CallExpr *Call);
248  void forwardInfo(const Stmt *From, const Stmt *To);
249  void handleTestingFunctionCall(const CallExpr *Call, const VarDecl *Var);
250  bool isLikeMoveAssignment(const CXXMethodDecl *MethodDecl);
251
252public:
253
254  void Visit(const Stmt *StmtNode);
255
256  void VisitBinaryOperator(const BinaryOperator *BinOp);
257  void VisitCallExpr(const CallExpr *Call);
258  void VisitCastExpr(const CastExpr *Cast);
259  void VisitCXXConstructExpr(const CXXConstructExpr *Call);
260  void VisitCXXMemberCallExpr(const CXXMemberCallExpr *Call);
261  void VisitCXXOperatorCallExpr(const CXXOperatorCallExpr *Call);
262  void VisitDeclRefExpr(const DeclRefExpr *DeclRef);
263  void VisitDeclStmt(const DeclStmt *DelcS);
264  void VisitMaterializeTemporaryExpr(const MaterializeTemporaryExpr *Temp);
265  void VisitMemberExpr(const MemberExpr *MExpr);
266  void VisitUnaryOperator(const UnaryOperator *UOp);
267  void VisitVarDecl(const VarDecl *Var);
268
269  ConsumedStmtVisitor(AnalysisDeclContext &AC, ConsumedAnalyzer &Analyzer)
270      : AC(AC), Analyzer(Analyzer), StateMap(NULL) {}
271
272  PropagationInfo getInfo(const Stmt *StmtNode) const {
273    ConstInfoEntry Entry = PropagationMap.find(StmtNode);
274
275    if (Entry != PropagationMap.end())
276      return Entry->second;
277    else
278      return PropagationInfo();
279  }
280
281  void reset(ConsumedStateMap *NewStateMap) {
282    StateMap = NewStateMap;
283  }
284};
285
286// TODO: When we support CallableWhenConsumed this will have to check for
287//       the different attributes and change the behavior bellow. (Deferred)
288void ConsumedStmtVisitor::checkCallability(const PropagationInfo &PInfo,
289                                           const FunctionDecl *FunDecl,
290                                           const CallExpr *Call) {
291
292  if (!FunDecl->hasAttr<CallableWhenUnconsumedAttr>()) return;
293
294  if (PInfo.isVar()) {
295    const VarDecl *Var = PInfo.getVar();
296
297    switch (StateMap->getState(Var)) {
298    case CS_Consumed:
299      Analyzer.WarningsHandler.warnUseWhileConsumed(
300        FunDecl->getNameAsString(), Var->getNameAsString(),
301        Call->getExprLoc());
302      break;
303
304    case CS_Unknown:
305      Analyzer.WarningsHandler.warnUseInUnknownState(
306        FunDecl->getNameAsString(), Var->getNameAsString(),
307        Call->getExprLoc());
308      break;
309
310    case CS_None:
311    case CS_Unconsumed:
312      break;
313    }
314
315  } else {
316    switch (PInfo.getState()) {
317    case CS_Consumed:
318      Analyzer.WarningsHandler.warnUseOfTempWhileConsumed(
319        FunDecl->getNameAsString(), Call->getExprLoc());
320      break;
321
322    case CS_Unknown:
323      Analyzer.WarningsHandler.warnUseOfTempInUnknownState(
324        FunDecl->getNameAsString(), Call->getExprLoc());
325      break;
326
327    case CS_None:
328    case CS_Unconsumed:
329      break;
330    }
331  }
332}
333
334void ConsumedStmtVisitor::forwardInfo(const Stmt *From, const Stmt *To) {
335  InfoEntry Entry = PropagationMap.find(From);
336
337  if (Entry != PropagationMap.end())
338    PropagationMap.insert(PairType(To, Entry->second));
339}
340
341void ConsumedStmtVisitor::handleTestingFunctionCall(const CallExpr *Call,
342                                                    const VarDecl  *Var) {
343
344  ConsumedState VarState = StateMap->getState(Var);
345
346  if (VarState != CS_Unknown) {
347    SourceLocation CallLoc = Call->getExprLoc();
348
349    if (!CallLoc.isMacroID())
350      Analyzer.WarningsHandler.warnUnnecessaryTest(Var->getNameAsString(),
351        stateToString(VarState), CallLoc);
352  }
353
354  PropagationMap.insert(PairType(Call, PropagationInfo(Var, CS_Unconsumed)));
355}
356
357bool ConsumedStmtVisitor::isLikeMoveAssignment(
358  const CXXMethodDecl *MethodDecl) {
359
360  return MethodDecl->isMoveAssignmentOperator() ||
361         (MethodDecl->getOverloadedOperator() == OO_Equal &&
362          MethodDecl->getNumParams() == 1 &&
363          MethodDecl->getParamDecl(0)->getType()->isRValueReferenceType());
364}
365
366void ConsumedStmtVisitor::Visit(const Stmt *StmtNode) {
367
368  ConstStmtVisitor<ConsumedStmtVisitor>::Visit(StmtNode);
369
370  for (Stmt::const_child_iterator CI = StmtNode->child_begin(),
371       CE = StmtNode->child_end(); CI != CE; ++CI) {
372
373    PropagationMap.erase(*CI);
374  }
375}
376
377void ConsumedStmtVisitor::VisitBinaryOperator(const BinaryOperator *BinOp) {
378  switch (BinOp->getOpcode()) {
379  case BO_LAnd:
380  case BO_LOr : {
381    InfoEntry LEntry = PropagationMap.find(BinOp->getLHS()),
382              REntry = PropagationMap.find(BinOp->getRHS());
383
384    VarTestResult LTest, RTest;
385
386    if (LEntry != PropagationMap.end() && LEntry->second.isTest()) {
387      LTest = LEntry->second.getTest();
388
389    } else {
390      LTest.Var      = NULL;
391      LTest.TestsFor = CS_None;
392    }
393
394    if (REntry != PropagationMap.end() && REntry->second.isTest()) {
395      RTest = REntry->second.getTest();
396
397    } else {
398      RTest.Var      = NULL;
399      RTest.TestsFor = CS_None;
400    }
401
402    if (!(LTest.Var == NULL && RTest.Var == NULL))
403      PropagationMap.insert(PairType(BinOp, PropagationInfo(BinOp,
404        static_cast<EffectiveOp>(BinOp->getOpcode() == BO_LOr), LTest, RTest)));
405
406    break;
407  }
408
409  case BO_PtrMemD:
410  case BO_PtrMemI:
411    forwardInfo(BinOp->getLHS(), BinOp);
412    break;
413
414  default:
415    break;
416  }
417}
418
419void ConsumedStmtVisitor::VisitCallExpr(const CallExpr *Call) {
420  if (const FunctionDecl *FunDecl =
421    dyn_cast_or_null<FunctionDecl>(Call->getDirectCallee())) {
422
423    // Special case for the std::move function.
424    // TODO: Make this more specific. (Deferred)
425    if (FunDecl->getNameAsString() == "move") {
426      InfoEntry Entry = PropagationMap.find(Call->getArg(0));
427
428      if (Entry != PropagationMap.end()) {
429        PropagationMap.insert(PairType(Call, Entry->second));
430      }
431
432      return;
433    }
434
435    unsigned Offset = Call->getNumArgs() - FunDecl->getNumParams();
436
437    for (unsigned Index = Offset; Index < Call->getNumArgs(); ++Index) {
438      QualType ParamType = FunDecl->getParamDecl(Index - Offset)->getType();
439
440      InfoEntry Entry = PropagationMap.find(Call->getArg(Index));
441
442      if (Entry == PropagationMap.end() || !Entry->second.isVar()) {
443        continue;
444      }
445
446      PropagationInfo PInfo = Entry->second;
447
448      if (ParamType->isRValueReferenceType() ||
449          (ParamType->isLValueReferenceType() &&
450           !cast<LValueReferenceType>(*ParamType).isSpelledAsLValue())) {
451
452        StateMap->setState(PInfo.getVar(), consumed::CS_Consumed);
453
454      } else if (!(ParamType.isConstQualified() ||
455                   ((ParamType->isReferenceType() ||
456                     ParamType->isPointerType()) &&
457                    ParamType->getPointeeType().isConstQualified()))) {
458
459        StateMap->setState(PInfo.getVar(), consumed::CS_Unknown);
460      }
461    }
462  }
463}
464
465void ConsumedStmtVisitor::VisitCastExpr(const CastExpr *Cast) {
466  forwardInfo(Cast->getSubExpr(), Cast);
467}
468
469void ConsumedStmtVisitor::VisitCXXConstructExpr(const CXXConstructExpr *Call) {
470  CXXConstructorDecl *Constructor = Call->getConstructor();
471
472  ASTContext &CurrContext = AC.getASTContext();
473  QualType ThisType = Constructor->getThisType(CurrContext)->getPointeeType();
474
475  if (Analyzer.isConsumableType(ThisType)) {
476    if (Constructor->hasAttr<ConsumesAttr>() ||
477        Constructor->isDefaultConstructor()) {
478
479      PropagationMap.insert(PairType(Call,
480        PropagationInfo(consumed::CS_Consumed)));
481
482    } else if (Constructor->isMoveConstructor()) {
483
484      PropagationInfo PInfo =
485        PropagationMap.find(Call->getArg(0))->second;
486
487      if (PInfo.isVar()) {
488        const VarDecl* Var = PInfo.getVar();
489
490        PropagationMap.insert(PairType(Call,
491          PropagationInfo(StateMap->getState(Var))));
492
493        StateMap->setState(Var, consumed::CS_Consumed);
494
495      } else {
496        PropagationMap.insert(PairType(Call, PInfo));
497      }
498
499    } else if (Constructor->isCopyConstructor()) {
500      MapType::iterator Entry = PropagationMap.find(Call->getArg(0));
501
502      if (Entry != PropagationMap.end())
503        PropagationMap.insert(PairType(Call, Entry->second));
504
505    } else {
506      PropagationMap.insert(PairType(Call,
507        PropagationInfo(consumed::CS_Unconsumed)));
508    }
509  }
510}
511
512void ConsumedStmtVisitor::VisitCXXMemberCallExpr(
513  const CXXMemberCallExpr *Call) {
514
515  VisitCallExpr(Call);
516
517  InfoEntry Entry = PropagationMap.find(Call->getCallee()->IgnoreParens());
518
519  if (Entry != PropagationMap.end()) {
520    PropagationInfo PInfo = Entry->second;
521    const CXXMethodDecl *MethodDecl = Call->getMethodDecl();
522
523    checkCallability(PInfo, MethodDecl, Call);
524
525    if (PInfo.isVar()) {
526      if (isTestingFunction(MethodDecl))
527        handleTestingFunctionCall(Call, PInfo.getVar());
528      else if (MethodDecl->hasAttr<ConsumesAttr>())
529        StateMap->setState(PInfo.getVar(), consumed::CS_Consumed);
530      else if (!MethodDecl->isConst())
531        StateMap->setState(PInfo.getVar(), consumed::CS_Unknown);
532    }
533  }
534}
535
536void ConsumedStmtVisitor::VisitCXXOperatorCallExpr(
537  const CXXOperatorCallExpr *Call) {
538
539  const FunctionDecl *FunDecl =
540    dyn_cast_or_null<FunctionDecl>(Call->getDirectCallee());
541
542  if (!FunDecl) return;
543
544  if (isa<CXXMethodDecl>(FunDecl) &&
545      isLikeMoveAssignment(cast<CXXMethodDecl>(FunDecl))) {
546
547    InfoEntry LEntry = PropagationMap.find(Call->getArg(0));
548    InfoEntry REntry = PropagationMap.find(Call->getArg(1));
549
550    PropagationInfo LPInfo, RPInfo;
551
552    if (LEntry != PropagationMap.end() &&
553        REntry != PropagationMap.end()) {
554
555      LPInfo = LEntry->second;
556      RPInfo = REntry->second;
557
558      if (LPInfo.isVar() && RPInfo.isVar()) {
559        StateMap->setState(LPInfo.getVar(),
560          StateMap->getState(RPInfo.getVar()));
561
562        StateMap->setState(RPInfo.getVar(), consumed::CS_Consumed);
563
564        PropagationMap.insert(PairType(Call, LPInfo));
565
566      } else if (LPInfo.isVar() && !RPInfo.isVar()) {
567        StateMap->setState(LPInfo.getVar(), RPInfo.getState());
568
569        PropagationMap.insert(PairType(Call, LPInfo));
570
571      } else if (!LPInfo.isVar() && RPInfo.isVar()) {
572        PropagationMap.insert(PairType(Call,
573          PropagationInfo(StateMap->getState(RPInfo.getVar()))));
574
575        StateMap->setState(RPInfo.getVar(), consumed::CS_Consumed);
576
577      } else {
578        PropagationMap.insert(PairType(Call, RPInfo));
579      }
580
581    } else if (LEntry != PropagationMap.end() &&
582               REntry == PropagationMap.end()) {
583
584      LPInfo = LEntry->second;
585
586      if (LPInfo.isVar()) {
587        StateMap->setState(LPInfo.getVar(), consumed::CS_Unknown);
588
589        PropagationMap.insert(PairType(Call, LPInfo));
590
591      } else {
592        PropagationMap.insert(PairType(Call,
593          PropagationInfo(consumed::CS_Unknown)));
594      }
595
596    } else if (LEntry == PropagationMap.end() &&
597               REntry != PropagationMap.end()) {
598
599      RPInfo = REntry->second;
600
601      if (RPInfo.isVar()) {
602        const VarDecl *Var = RPInfo.getVar();
603
604        PropagationMap.insert(PairType(Call,
605          PropagationInfo(StateMap->getState(Var))));
606
607        StateMap->setState(Var, consumed::CS_Consumed);
608
609      } else {
610        PropagationMap.insert(PairType(Call, RPInfo));
611      }
612    }
613
614  } else {
615
616    VisitCallExpr(Call);
617
618    InfoEntry Entry = PropagationMap.find(Call->getArg(0));
619
620    if (Entry != PropagationMap.end()) {
621      PropagationInfo PInfo = Entry->second;
622
623      checkCallability(PInfo, FunDecl, Call);
624
625      if (PInfo.isVar()) {
626        if (isTestingFunction(FunDecl)) {
627          handleTestingFunctionCall(Call, PInfo.getVar());
628
629        } else if (FunDecl->hasAttr<ConsumesAttr>()) {
630          StateMap->setState(PInfo.getVar(), consumed::CS_Consumed);
631
632        } else if (const CXXMethodDecl *MethodDecl =
633                     dyn_cast_or_null<CXXMethodDecl>(FunDecl)) {
634
635          if (!MethodDecl->isConst())
636            StateMap->setState(PInfo.getVar(), consumed::CS_Unknown);
637        }
638      }
639    }
640  }
641}
642
643void ConsumedStmtVisitor::VisitDeclRefExpr(const DeclRefExpr *DeclRef) {
644  if (const VarDecl *Var = dyn_cast_or_null<VarDecl>(DeclRef->getDecl()))
645    if (StateMap->getState(Var) != consumed::CS_None)
646      PropagationMap.insert(PairType(DeclRef, PropagationInfo(Var)));
647}
648
649void ConsumedStmtVisitor::VisitDeclStmt(const DeclStmt *DeclS) {
650  for (DeclStmt::const_decl_iterator DI = DeclS->decl_begin(),
651       DE = DeclS->decl_end(); DI != DE; ++DI) {
652
653    if (isa<VarDecl>(*DI)) VisitVarDecl(cast<VarDecl>(*DI));
654  }
655
656  if (DeclS->isSingleDecl())
657    if (const VarDecl *Var = dyn_cast_or_null<VarDecl>(DeclS->getSingleDecl()))
658      PropagationMap.insert(PairType(DeclS, PropagationInfo(Var)));
659}
660
661void ConsumedStmtVisitor::VisitMaterializeTemporaryExpr(
662  const MaterializeTemporaryExpr *Temp) {
663
664  InfoEntry Entry = PropagationMap.find(Temp->GetTemporaryExpr());
665
666  if (Entry != PropagationMap.end())
667    PropagationMap.insert(PairType(Temp, Entry->second));
668}
669
670void ConsumedStmtVisitor::VisitMemberExpr(const MemberExpr *MExpr) {
671  forwardInfo(MExpr->getBase(), MExpr);
672}
673
674void ConsumedStmtVisitor::VisitUnaryOperator(const UnaryOperator *UOp) {
675  InfoEntry Entry = PropagationMap.find(UOp->getSubExpr()->IgnoreParens());
676  if (Entry == PropagationMap.end()) return;
677
678  switch (UOp->getOpcode()) {
679  case UO_AddrOf:
680    PropagationMap.insert(PairType(UOp, Entry->second));
681    break;
682
683  case UO_LNot:
684    if (Entry->second.isTest() || Entry->second.isBinTest())
685      PropagationMap.insert(PairType(UOp, Entry->second.invertTest()));
686    break;
687
688  default:
689    break;
690  }
691}
692
693void ConsumedStmtVisitor::VisitVarDecl(const VarDecl *Var) {
694  if (Analyzer.isConsumableType(Var->getType())) {
695    PropagationInfo PInfo =
696      PropagationMap.find(Var->getInit())->second;
697
698    StateMap->setState(Var, PInfo.isVar() ?
699      StateMap->getState(PInfo.getVar()) : PInfo.getState());
700  }
701}
702}} // end clang::consumed::ConsumedStmtVisitor
703
704namespace clang {
705namespace consumed {
706
707void splitVarStateForIf(const IfStmt * IfNode, const VarTestResult &Test,
708                        ConsumedStateMap *ThenStates,
709                        ConsumedStateMap *ElseStates) {
710
711  ConsumedState VarState = ThenStates->getState(Test.Var);
712
713  if (VarState == CS_Unknown) {
714    ThenStates->setState(Test.Var, Test.TestsFor);
715    if (ElseStates)
716      ElseStates->setState(Test.Var, invertConsumedUnconsumed(Test.TestsFor));
717
718  } else if (VarState == invertConsumedUnconsumed(Test.TestsFor)) {
719    ThenStates->markUnreachable();
720
721  } else if (VarState == Test.TestsFor && ElseStates) {
722    ElseStates->markUnreachable();
723  }
724}
725
726void splitVarStateForIfBinOp(const PropagationInfo &PInfo,
727  ConsumedStateMap *ThenStates, ConsumedStateMap *ElseStates) {
728
729  const VarTestResult &LTest = PInfo.getLTest(),
730                      &RTest = PInfo.getRTest();
731
732  ConsumedState LState = LTest.Var ? ThenStates->getState(LTest.Var) : CS_None,
733                RState = RTest.Var ? ThenStates->getState(RTest.Var) : CS_None;
734
735  if (LTest.Var) {
736    if (PInfo.testEffectiveOp() == EO_And) {
737      if (LState == CS_Unknown) {
738        ThenStates->setState(LTest.Var, LTest.TestsFor);
739
740      } else if (LState == invertConsumedUnconsumed(LTest.TestsFor)) {
741        ThenStates->markUnreachable();
742
743      } else if (LState == LTest.TestsFor && isKnownState(RState)) {
744        if (RState == RTest.TestsFor) {
745          if (ElseStates)
746            ElseStates->markUnreachable();
747        } else {
748          ThenStates->markUnreachable();
749        }
750      }
751
752    } else {
753      if (LState == CS_Unknown && ElseStates) {
754        ElseStates->setState(LTest.Var,
755                             invertConsumedUnconsumed(LTest.TestsFor));
756
757      } else if (LState == LTest.TestsFor && ElseStates) {
758        ElseStates->markUnreachable();
759
760      } else if (LState == invertConsumedUnconsumed(LTest.TestsFor) &&
761                 isKnownState(RState)) {
762
763        if (RState == RTest.TestsFor) {
764          if (ElseStates)
765            ElseStates->markUnreachable();
766        } else {
767          ThenStates->markUnreachable();
768        }
769      }
770    }
771  }
772
773  if (RTest.Var) {
774    if (PInfo.testEffectiveOp() == EO_And) {
775      if (RState == CS_Unknown)
776        ThenStates->setState(RTest.Var, RTest.TestsFor);
777      else if (RState == invertConsumedUnconsumed(RTest.TestsFor))
778        ThenStates->markUnreachable();
779
780    } else if (ElseStates) {
781      if (RState == CS_Unknown)
782        ElseStates->setState(RTest.Var,
783                             invertConsumedUnconsumed(RTest.TestsFor));
784      else if (RState == RTest.TestsFor)
785        ElseStates->markUnreachable();
786    }
787  }
788}
789
790void ConsumedBlockInfo::addInfo(const CFGBlock *Block,
791                                ConsumedStateMap *StateMap,
792                                bool &AlreadyOwned) {
793
794  if (VisitedBlocks.alreadySet(Block)) return;
795
796  ConsumedStateMap *Entry = StateMapsArray[Block->getBlockID()];
797
798  if (Entry) {
799    Entry->intersect(StateMap);
800
801  } else if (AlreadyOwned) {
802    StateMapsArray[Block->getBlockID()] = new ConsumedStateMap(*StateMap);
803
804  } else {
805    StateMapsArray[Block->getBlockID()] = StateMap;
806    AlreadyOwned = true;
807  }
808}
809
810void ConsumedBlockInfo::addInfo(const CFGBlock *Block,
811                                ConsumedStateMap *StateMap) {
812
813  if (VisitedBlocks.alreadySet(Block)) {
814    delete StateMap;
815    return;
816  }
817
818  ConsumedStateMap *Entry = StateMapsArray[Block->getBlockID()];
819
820  if (Entry) {
821    Entry->intersect(StateMap);
822    delete StateMap;
823
824  } else {
825    StateMapsArray[Block->getBlockID()] = StateMap;
826  }
827}
828
829ConsumedStateMap* ConsumedBlockInfo::getInfo(const CFGBlock *Block) {
830  return StateMapsArray[Block->getBlockID()];
831}
832
833void ConsumedBlockInfo::markVisited(const CFGBlock *Block) {
834  VisitedBlocks.insert(Block);
835}
836
837ConsumedState ConsumedStateMap::getState(const VarDecl *Var) {
838  MapType::const_iterator Entry = Map.find(Var);
839
840  if (Entry != Map.end()) {
841    return Entry->second;
842
843  } else {
844    return CS_None;
845  }
846}
847
848void ConsumedStateMap::intersect(const ConsumedStateMap *Other) {
849  ConsumedState LocalState;
850
851  if (this->From && this->From == Other->From && !Other->Reachable) {
852    this->markUnreachable();
853    return;
854  }
855
856  for (MapType::const_iterator DMI = Other->Map.begin(),
857       DME = Other->Map.end(); DMI != DME; ++DMI) {
858
859    LocalState = this->getState(DMI->first);
860
861    if (LocalState == CS_None)
862      continue;
863
864    if (LocalState != DMI->second)
865       Map[DMI->first] = CS_Unknown;
866  }
867}
868
869void ConsumedStateMap::markUnreachable() {
870  this->Reachable = false;
871  Map.clear();
872}
873
874void ConsumedStateMap::makeUnknown() {
875  for (MapType::const_iterator DMI = Map.begin(), DME = Map.end(); DMI != DME;
876       ++DMI) {
877
878    Map[DMI->first] = CS_Unknown;
879  }
880}
881
882void ConsumedStateMap::setState(const VarDecl *Var, ConsumedState State) {
883  Map[Var] = State;
884}
885
886void ConsumedStateMap::remove(const VarDecl *Var) {
887  Map.erase(Var);
888}
889
890bool ConsumedAnalyzer::isConsumableType(QualType Type) {
891  const CXXRecordDecl *RD =
892    dyn_cast_or_null<CXXRecordDecl>(Type->getAsCXXRecordDecl());
893
894  if (!RD) return false;
895
896  std::pair<CacheMapType::iterator, bool> Entry =
897    ConsumableTypeCache.insert(std::make_pair(RD, false));
898
899  if (Entry.second)
900    Entry.first->second = hasConsumableAttributes(RD);
901
902  return Entry.first->second;
903}
904
905// TODO: Walk the base classes to see if any of them are unique types.
906//       (Deferred)
907bool ConsumedAnalyzer::hasConsumableAttributes(const CXXRecordDecl *RD) {
908  for (CXXRecordDecl::method_iterator MI = RD->method_begin(),
909       ME = RD->method_end(); MI != ME; ++MI) {
910
911    for (Decl::attr_iterator AI = (*MI)->attr_begin(), AE = (*MI)->attr_end();
912         AI != AE; ++AI) {
913
914      switch ((*AI)->getKind()) {
915      case attr::CallableWhenUnconsumed:
916      case attr::TestsUnconsumed:
917        return true;
918
919      default:
920        break;
921      }
922    }
923  }
924
925  return false;
926}
927
928bool ConsumedAnalyzer::splitState(const CFGBlock *CurrBlock,
929                                  const ConsumedStmtVisitor &Visitor) {
930
931  ConsumedStateMap *FalseStates = new ConsumedStateMap(*CurrStates);
932  PropagationInfo PInfo;
933
934  if (const IfStmt *IfNode =
935    dyn_cast_or_null<IfStmt>(CurrBlock->getTerminator().getStmt())) {
936
937    bool HasElse = IfNode->getElse() != NULL;
938    const Stmt *Cond = IfNode->getCond();
939
940    PInfo = Visitor.getInfo(Cond);
941    if (!PInfo.isValid() && isa<BinaryOperator>(Cond))
942      PInfo = Visitor.getInfo(cast<BinaryOperator>(Cond)->getRHS());
943
944    if (PInfo.isTest()) {
945      CurrStates->setSource(Cond);
946      FalseStates->setSource(Cond);
947
948      splitVarStateForIf(IfNode, PInfo.getTest(), CurrStates,
949                         HasElse ? FalseStates : NULL);
950
951    } else if (PInfo.isBinTest()) {
952      CurrStates->setSource(PInfo.testSourceNode());
953      FalseStates->setSource(PInfo.testSourceNode());
954
955      splitVarStateForIfBinOp(PInfo, CurrStates, HasElse ? FalseStates : NULL);
956
957    } else {
958      delete FalseStates;
959      return false;
960    }
961
962  } else if (const BinaryOperator *BinOp =
963    dyn_cast_or_null<BinaryOperator>(CurrBlock->getTerminator().getStmt())) {
964
965    PInfo = Visitor.getInfo(BinOp->getLHS());
966    if (!PInfo.isTest()) {
967      if ((BinOp = dyn_cast_or_null<BinaryOperator>(BinOp->getLHS()))) {
968        PInfo = Visitor.getInfo(BinOp->getRHS());
969
970        if (!PInfo.isTest()) {
971          delete FalseStates;
972          return false;
973        }
974
975      } else {
976        delete FalseStates;
977        return false;
978      }
979    }
980
981    CurrStates->setSource(BinOp);
982    FalseStates->setSource(BinOp);
983
984    const VarTestResult &Test = PInfo.getTest();
985    ConsumedState VarState = CurrStates->getState(Test.Var);
986
987    if (BinOp->getOpcode() == BO_LAnd) {
988      if (VarState == CS_Unknown)
989        CurrStates->setState(Test.Var, Test.TestsFor);
990      else if (VarState == invertConsumedUnconsumed(Test.TestsFor))
991        CurrStates->markUnreachable();
992
993    } else if (BinOp->getOpcode() == BO_LOr) {
994      if (VarState == CS_Unknown)
995        FalseStates->setState(Test.Var,
996                              invertConsumedUnconsumed(Test.TestsFor));
997      else if (VarState == Test.TestsFor)
998        FalseStates->markUnreachable();
999    }
1000
1001  } else {
1002    delete FalseStates;
1003    return false;
1004  }
1005
1006  CFGBlock::const_succ_iterator SI = CurrBlock->succ_begin();
1007
1008  if (*SI)
1009    BlockInfo.addInfo(*SI, CurrStates);
1010  else
1011    delete CurrStates;
1012
1013  if (*++SI)
1014    BlockInfo.addInfo(*SI, FalseStates);
1015  else
1016    delete FalseStates;
1017
1018  CurrStates = NULL;
1019  return true;
1020}
1021
1022void ConsumedAnalyzer::run(AnalysisDeclContext &AC) {
1023  const FunctionDecl *D = dyn_cast_or_null<FunctionDecl>(AC.getDecl());
1024
1025  if (!D) return;
1026
1027  BlockInfo = ConsumedBlockInfo(AC.getCFG());
1028
1029  PostOrderCFGView *SortedGraph = AC.getAnalysis<PostOrderCFGView>();
1030
1031  CurrStates = new ConsumedStateMap();
1032  ConsumedStmtVisitor Visitor(AC, *this);
1033
1034  // Visit all of the function's basic blocks.
1035  for (PostOrderCFGView::iterator I = SortedGraph->begin(),
1036       E = SortedGraph->end(); I != E; ++I) {
1037
1038    const CFGBlock *CurrBlock = *I;
1039    BlockInfo.markVisited(CurrBlock);
1040
1041    if (CurrStates == NULL)
1042      CurrStates = BlockInfo.getInfo(CurrBlock);
1043
1044    if (!CurrStates) {
1045      continue;
1046
1047    } else if (!CurrStates->isReachable()) {
1048      delete CurrStates;
1049      CurrStates = NULL;
1050      continue;
1051    }
1052
1053    Visitor.reset(CurrStates);
1054
1055    // Visit all of the basic block's statements.
1056    for (CFGBlock::const_iterator BI = CurrBlock->begin(),
1057         BE = CurrBlock->end(); BI != BE; ++BI) {
1058
1059      switch (BI->getKind()) {
1060      case CFGElement::Statement:
1061        Visitor.Visit(BI->castAs<CFGStmt>().getStmt());
1062        break;
1063      case CFGElement::AutomaticObjectDtor:
1064        CurrStates->remove(BI->castAs<CFGAutomaticObjDtor>().getVarDecl());
1065      default:
1066        break;
1067      }
1068    }
1069
1070    // TODO: Handle other forms of branching with precision, including while-
1071    //       and for-loops. (Deferred)
1072    if (!splitState(CurrBlock, Visitor)) {
1073      CurrStates->setSource(NULL);
1074
1075      if (CurrBlock->succ_size() > 1) {
1076        CurrStates->makeUnknown();
1077
1078        bool OwnershipTaken = false;
1079
1080        for (CFGBlock::const_succ_iterator SI = CurrBlock->succ_begin(),
1081             SE = CurrBlock->succ_end(); SI != SE; ++SI) {
1082
1083          if (*SI) BlockInfo.addInfo(*SI, CurrStates, OwnershipTaken);
1084        }
1085
1086        if (!OwnershipTaken)
1087          delete CurrStates;
1088
1089        CurrStates = NULL;
1090
1091      } else if (CurrBlock->succ_size() == 1 &&
1092                 (*CurrBlock->succ_begin())->pred_size() > 1) {
1093
1094        BlockInfo.addInfo(*CurrBlock->succ_begin(), CurrStates);
1095        CurrStates = NULL;
1096      }
1097    }
1098  } // End of block iterator.
1099
1100  // Delete the last existing state map.
1101  delete CurrStates;
1102
1103  WarningsHandler.emitDiagnostics();
1104}
1105}} // end namespace clang::consumed
1106