1259701Sdim//===- Consumed.cpp --------------------------------------------*- C++ --*-===//
2259701Sdim//
3259701Sdim//                     The LLVM Compiler Infrastructure
4259701Sdim//
5259701Sdim// This file is distributed under the University of Illinois Open Source
6259701Sdim// License. See LICENSE.TXT for details.
7259701Sdim//
8259701Sdim//===----------------------------------------------------------------------===//
9259701Sdim//
10259701Sdim// A intra-procedural analysis for checking consumed properties.  This is based,
11259701Sdim// in part, on research on linear types.
12259701Sdim//
13259701Sdim//===----------------------------------------------------------------------===//
14259701Sdim
15259701Sdim#include "clang/AST/ASTContext.h"
16259701Sdim#include "clang/AST/Attr.h"
17259701Sdim#include "clang/AST/DeclCXX.h"
18259701Sdim#include "clang/AST/ExprCXX.h"
19259701Sdim#include "clang/AST/RecursiveASTVisitor.h"
20259701Sdim#include "clang/AST/StmtVisitor.h"
21259701Sdim#include "clang/AST/StmtCXX.h"
22259701Sdim#include "clang/AST/Type.h"
23259701Sdim#include "clang/Analysis/Analyses/PostOrderCFGView.h"
24259701Sdim#include "clang/Analysis/AnalysisContext.h"
25259701Sdim#include "clang/Analysis/CFG.h"
26259701Sdim#include "clang/Analysis/Analyses/Consumed.h"
27259701Sdim#include "clang/Basic/OperatorKinds.h"
28259701Sdim#include "clang/Basic/SourceLocation.h"
29259701Sdim#include "llvm/ADT/DenseMap.h"
30259701Sdim#include "llvm/ADT/OwningPtr.h"
31259701Sdim#include "llvm/ADT/SmallVector.h"
32259701Sdim#include "llvm/Support/Compiler.h"
33259701Sdim#include "llvm/Support/raw_ostream.h"
34259701Sdim
35259701Sdim// TODO: Adjust states of args to constructors in the same way that arguments to
36259701Sdim//       function calls are handled.
37259701Sdim// TODO: Use information from tests in for- and while-loop conditional.
38259701Sdim// TODO: Add notes about the actual and expected state for
39259701Sdim// TODO: Correctly identify unreachable blocks when chaining boolean operators.
40259701Sdim// TODO: Adjust the parser and AttributesList class to support lists of
41259701Sdim//       identifiers.
42259701Sdim// TODO: Warn about unreachable code.
43259701Sdim// TODO: Switch to using a bitmap to track unreachable blocks.
44259701Sdim// TODO: Handle variable definitions, e.g. bool valid = x.isValid();
45259701Sdim//       if (valid) ...; (Deferred)
46259701Sdim// TODO: Take notes on state transitions to provide better warning messages.
47259701Sdim//       (Deferred)
48259701Sdim// TODO: Test nested conditionals: A) Checking the same value multiple times,
49259701Sdim//       and 2) Checking different values. (Deferred)
50259701Sdim
51259701Sdimusing namespace clang;
52259701Sdimusing namespace consumed;
53259701Sdim
54259701Sdim// Key method definition
55259701SdimConsumedWarningsHandlerBase::~ConsumedWarningsHandlerBase() {}
56259701Sdim
57259701Sdimstatic SourceLocation getFirstStmtLoc(const CFGBlock *Block) {
58259701Sdim  // Find the source location of the first statement in the block, if the block
59259701Sdim  // is not empty.
60259701Sdim  for (CFGBlock::const_iterator BI = Block->begin(), BE = Block->end();
61259701Sdim       BI != BE; ++BI) {
62259701Sdim    if (Optional<CFGStmt> CS = BI->getAs<CFGStmt>())
63259701Sdim      return CS->getStmt()->getLocStart();
64259701Sdim  }
65259701Sdim
66259701Sdim  // Block is empty.
67259701Sdim  // If we have one successor, return the first statement in that block
68259701Sdim  if (Block->succ_size() == 1 && *Block->succ_begin())
69259701Sdim    return getFirstStmtLoc(*Block->succ_begin());
70259701Sdim
71259701Sdim  return SourceLocation();
72259701Sdim}
73259701Sdim
74259701Sdimstatic SourceLocation getLastStmtLoc(const CFGBlock *Block) {
75259701Sdim  // Find the source location of the last statement in the block, if the block
76259701Sdim  // is not empty.
77259701Sdim  if (const Stmt *StmtNode = Block->getTerminator()) {
78259701Sdim    return StmtNode->getLocStart();
79259701Sdim  } else {
80259701Sdim    for (CFGBlock::const_reverse_iterator BI = Block->rbegin(),
81259701Sdim         BE = Block->rend(); BI != BE; ++BI) {
82259701Sdim      if (Optional<CFGStmt> CS = BI->getAs<CFGStmt>())
83259701Sdim        return CS->getStmt()->getLocStart();
84259701Sdim    }
85259701Sdim  }
86259701Sdim
87259701Sdim  // If we have one successor, return the first statement in that block
88259701Sdim  SourceLocation Loc;
89259701Sdim  if (Block->succ_size() == 1 && *Block->succ_begin())
90259701Sdim    Loc = getFirstStmtLoc(*Block->succ_begin());
91259701Sdim  if (Loc.isValid())
92259701Sdim    return Loc;
93259701Sdim
94259701Sdim  // If we have one predecessor, return the last statement in that block
95259701Sdim  if (Block->pred_size() == 1 && *Block->pred_begin())
96259701Sdim    return getLastStmtLoc(*Block->pred_begin());
97259701Sdim
98259701Sdim  return Loc;
99259701Sdim}
100259701Sdim
101259701Sdimstatic ConsumedState invertConsumedUnconsumed(ConsumedState State) {
102259701Sdim  switch (State) {
103259701Sdim  case CS_Unconsumed:
104259701Sdim    return CS_Consumed;
105259701Sdim  case CS_Consumed:
106259701Sdim    return CS_Unconsumed;
107259701Sdim  case CS_None:
108259701Sdim    return CS_None;
109259701Sdim  case CS_Unknown:
110259701Sdim    return CS_Unknown;
111259701Sdim  }
112259701Sdim  llvm_unreachable("invalid enum");
113259701Sdim}
114259701Sdim
115259701Sdimstatic bool isCallableInState(const CallableWhenAttr *CWAttr,
116259701Sdim                              ConsumedState State) {
117259701Sdim
118259701Sdim  CallableWhenAttr::callableState_iterator I = CWAttr->callableState_begin(),
119259701Sdim                                           E = CWAttr->callableState_end();
120259701Sdim
121259701Sdim  for (; I != E; ++I) {
122259701Sdim
123259701Sdim    ConsumedState MappedAttrState = CS_None;
124259701Sdim
125259701Sdim    switch (*I) {
126259701Sdim    case CallableWhenAttr::Unknown:
127259701Sdim      MappedAttrState = CS_Unknown;
128259701Sdim      break;
129259701Sdim
130259701Sdim    case CallableWhenAttr::Unconsumed:
131259701Sdim      MappedAttrState = CS_Unconsumed;
132259701Sdim      break;
133259701Sdim
134259701Sdim    case CallableWhenAttr::Consumed:
135259701Sdim      MappedAttrState = CS_Consumed;
136259701Sdim      break;
137259701Sdim    }
138259701Sdim
139259701Sdim    if (MappedAttrState == State)
140259701Sdim      return true;
141259701Sdim  }
142259701Sdim
143259701Sdim  return false;
144259701Sdim}
145259701Sdim
146259701Sdimstatic bool isConsumableType(const QualType &QT) {
147259701Sdim  if (QT->isPointerType() || QT->isReferenceType())
148259701Sdim    return false;
149259701Sdim
150259701Sdim  if (const CXXRecordDecl *RD = QT->getAsCXXRecordDecl())
151259701Sdim    return RD->hasAttr<ConsumableAttr>();
152259701Sdim
153259701Sdim  return false;
154259701Sdim}
155259701Sdim
156259701Sdimstatic bool isKnownState(ConsumedState State) {
157259701Sdim  switch (State) {
158259701Sdim  case CS_Unconsumed:
159259701Sdim  case CS_Consumed:
160259701Sdim    return true;
161259701Sdim  case CS_None:
162259701Sdim  case CS_Unknown:
163259701Sdim    return false;
164259701Sdim  }
165259701Sdim  llvm_unreachable("invalid enum");
166259701Sdim}
167259701Sdim
168259701Sdimstatic bool isRValueRefish(QualType ParamType) {
169259701Sdim  return ParamType->isRValueReferenceType() ||
170259701Sdim        (ParamType->isLValueReferenceType() &&
171259701Sdim         !cast<LValueReferenceType>(
172259701Sdim           ParamType.getCanonicalType())->isSpelledAsLValue());
173259701Sdim}
174259701Sdim
175259701Sdimstatic bool isTestingFunction(const FunctionDecl *FunDecl) {
176259701Sdim  return FunDecl->hasAttr<TestTypestateAttr>();
177259701Sdim}
178259701Sdim
179259701Sdimstatic bool isValueType(QualType ParamType) {
180259701Sdim  return !(ParamType->isPointerType() || ParamType->isReferenceType());
181259701Sdim}
182259701Sdim
183259701Sdimstatic ConsumedState mapConsumableAttrState(const QualType QT) {
184259701Sdim  assert(isConsumableType(QT));
185259701Sdim
186259701Sdim  const ConsumableAttr *CAttr =
187259701Sdim      QT->getAsCXXRecordDecl()->getAttr<ConsumableAttr>();
188259701Sdim
189259701Sdim  switch (CAttr->getDefaultState()) {
190259701Sdim  case ConsumableAttr::Unknown:
191259701Sdim    return CS_Unknown;
192259701Sdim  case ConsumableAttr::Unconsumed:
193259701Sdim    return CS_Unconsumed;
194259701Sdim  case ConsumableAttr::Consumed:
195259701Sdim    return CS_Consumed;
196259701Sdim  }
197259701Sdim  llvm_unreachable("invalid enum");
198259701Sdim}
199259701Sdim
200259701Sdimstatic ConsumedState
201259701SdimmapParamTypestateAttrState(const ParamTypestateAttr *PTAttr) {
202259701Sdim  switch (PTAttr->getParamState()) {
203259701Sdim  case ParamTypestateAttr::Unknown:
204259701Sdim    return CS_Unknown;
205259701Sdim  case ParamTypestateAttr::Unconsumed:
206259701Sdim    return CS_Unconsumed;
207259701Sdim  case ParamTypestateAttr::Consumed:
208259701Sdim    return CS_Consumed;
209259701Sdim  }
210259701Sdim  llvm_unreachable("invalid_enum");
211259701Sdim}
212259701Sdim
213259701Sdimstatic ConsumedState
214259701SdimmapReturnTypestateAttrState(const ReturnTypestateAttr *RTSAttr) {
215259701Sdim  switch (RTSAttr->getState()) {
216259701Sdim  case ReturnTypestateAttr::Unknown:
217259701Sdim    return CS_Unknown;
218259701Sdim  case ReturnTypestateAttr::Unconsumed:
219259701Sdim    return CS_Unconsumed;
220259701Sdim  case ReturnTypestateAttr::Consumed:
221259701Sdim    return CS_Consumed;
222259701Sdim  }
223259701Sdim  llvm_unreachable("invalid enum");
224259701Sdim}
225259701Sdim
226259701Sdimstatic ConsumedState mapSetTypestateAttrState(const SetTypestateAttr *STAttr) {
227259701Sdim  switch (STAttr->getNewState()) {
228259701Sdim  case SetTypestateAttr::Unknown:
229259701Sdim    return CS_Unknown;
230259701Sdim  case SetTypestateAttr::Unconsumed:
231259701Sdim    return CS_Unconsumed;
232259701Sdim  case SetTypestateAttr::Consumed:
233259701Sdim    return CS_Consumed;
234259701Sdim  }
235259701Sdim  llvm_unreachable("invalid_enum");
236259701Sdim}
237259701Sdim
238259701Sdimstatic StringRef stateToString(ConsumedState State) {
239259701Sdim  switch (State) {
240259701Sdim  case consumed::CS_None:
241259701Sdim    return "none";
242259701Sdim
243259701Sdim  case consumed::CS_Unknown:
244259701Sdim    return "unknown";
245259701Sdim
246259701Sdim  case consumed::CS_Unconsumed:
247259701Sdim    return "unconsumed";
248259701Sdim
249259701Sdim  case consumed::CS_Consumed:
250259701Sdim    return "consumed";
251259701Sdim  }
252259701Sdim  llvm_unreachable("invalid enum");
253259701Sdim}
254259701Sdim
255259701Sdimstatic ConsumedState testsFor(const FunctionDecl *FunDecl) {
256259701Sdim  assert(isTestingFunction(FunDecl));
257259701Sdim  switch (FunDecl->getAttr<TestTypestateAttr>()->getTestState()) {
258259701Sdim  case TestTypestateAttr::Unconsumed:
259259701Sdim    return CS_Unconsumed;
260259701Sdim  case TestTypestateAttr::Consumed:
261259701Sdim    return CS_Consumed;
262259701Sdim  }
263259701Sdim  llvm_unreachable("invalid enum");
264259701Sdim}
265259701Sdim
266259701Sdimnamespace {
267259701Sdimstruct VarTestResult {
268259701Sdim  const VarDecl *Var;
269259701Sdim  ConsumedState TestsFor;
270259701Sdim};
271259701Sdim} // end anonymous::VarTestResult
272259701Sdim
273259701Sdimnamespace clang {
274259701Sdimnamespace consumed {
275259701Sdim
276259701Sdimenum EffectiveOp {
277259701Sdim  EO_And,
278259701Sdim  EO_Or
279259701Sdim};
280259701Sdim
281259701Sdimclass PropagationInfo {
282259701Sdim  enum {
283259701Sdim    IT_None,
284259701Sdim    IT_State,
285259701Sdim    IT_VarTest,
286259701Sdim    IT_BinTest,
287259701Sdim    IT_Var,
288259701Sdim    IT_Tmp
289259701Sdim  } InfoType;
290259701Sdim
291259701Sdim  struct BinTestTy {
292259701Sdim    const BinaryOperator *Source;
293259701Sdim    EffectiveOp EOp;
294259701Sdim    VarTestResult LTest;
295259701Sdim    VarTestResult RTest;
296259701Sdim  };
297259701Sdim
298259701Sdim  union {
299259701Sdim    ConsumedState State;
300259701Sdim    VarTestResult VarTest;
301259701Sdim    const VarDecl *Var;
302259701Sdim    const CXXBindTemporaryExpr *Tmp;
303259701Sdim    BinTestTy BinTest;
304259701Sdim  };
305259701Sdim
306259701Sdimpublic:
307259701Sdim  PropagationInfo() : InfoType(IT_None) {}
308259701Sdim
309259701Sdim  PropagationInfo(const VarTestResult &VarTest)
310259701Sdim    : InfoType(IT_VarTest), VarTest(VarTest) {}
311259701Sdim
312259701Sdim  PropagationInfo(const VarDecl *Var, ConsumedState TestsFor)
313259701Sdim    : InfoType(IT_VarTest) {
314259701Sdim
315259701Sdim    VarTest.Var      = Var;
316259701Sdim    VarTest.TestsFor = TestsFor;
317259701Sdim  }
318259701Sdim
319259701Sdim  PropagationInfo(const BinaryOperator *Source, EffectiveOp EOp,
320259701Sdim                  const VarTestResult &LTest, const VarTestResult &RTest)
321259701Sdim    : InfoType(IT_BinTest) {
322259701Sdim
323259701Sdim    BinTest.Source  = Source;
324259701Sdim    BinTest.EOp     = EOp;
325259701Sdim    BinTest.LTest   = LTest;
326259701Sdim    BinTest.RTest   = RTest;
327259701Sdim  }
328259701Sdim
329259701Sdim  PropagationInfo(const BinaryOperator *Source, EffectiveOp EOp,
330259701Sdim                  const VarDecl *LVar, ConsumedState LTestsFor,
331259701Sdim                  const VarDecl *RVar, ConsumedState RTestsFor)
332259701Sdim    : InfoType(IT_BinTest) {
333259701Sdim
334259701Sdim    BinTest.Source         = Source;
335259701Sdim    BinTest.EOp            = EOp;
336259701Sdim    BinTest.LTest.Var      = LVar;
337259701Sdim    BinTest.LTest.TestsFor = LTestsFor;
338259701Sdim    BinTest.RTest.Var      = RVar;
339259701Sdim    BinTest.RTest.TestsFor = RTestsFor;
340259701Sdim  }
341259701Sdim
342259701Sdim  PropagationInfo(ConsumedState State)
343259701Sdim    : InfoType(IT_State), State(State) {}
344259701Sdim
345259701Sdim  PropagationInfo(const VarDecl *Var) : InfoType(IT_Var), Var(Var) {}
346259701Sdim  PropagationInfo(const CXXBindTemporaryExpr *Tmp)
347259701Sdim    : InfoType(IT_Tmp), Tmp(Tmp) {}
348259701Sdim
349259701Sdim  const ConsumedState & getState() const {
350259701Sdim    assert(InfoType == IT_State);
351259701Sdim    return State;
352259701Sdim  }
353259701Sdim
354259701Sdim  const VarTestResult & getVarTest() const {
355259701Sdim    assert(InfoType == IT_VarTest);
356259701Sdim    return VarTest;
357259701Sdim  }
358259701Sdim
359259701Sdim  const VarTestResult & getLTest() const {
360259701Sdim    assert(InfoType == IT_BinTest);
361259701Sdim    return BinTest.LTest;
362259701Sdim  }
363259701Sdim
364259701Sdim  const VarTestResult & getRTest() const {
365259701Sdim    assert(InfoType == IT_BinTest);
366259701Sdim    return BinTest.RTest;
367259701Sdim  }
368259701Sdim
369259701Sdim  const VarDecl * getVar() const {
370259701Sdim    assert(InfoType == IT_Var);
371259701Sdim    return Var;
372259701Sdim  }
373259701Sdim
374259701Sdim  const CXXBindTemporaryExpr * getTmp() const {
375259701Sdim    assert(InfoType == IT_Tmp);
376259701Sdim    return Tmp;
377259701Sdim  }
378259701Sdim
379259701Sdim  ConsumedState getAsState(const ConsumedStateMap *StateMap) const {
380259701Sdim    assert(isVar() || isTmp() || isState());
381259701Sdim
382259701Sdim    if (isVar())
383259701Sdim      return StateMap->getState(Var);
384259701Sdim    else if (isTmp())
385259701Sdim      return StateMap->getState(Tmp);
386259701Sdim    else if (isState())
387259701Sdim      return State;
388259701Sdim    else
389259701Sdim      return CS_None;
390259701Sdim  }
391259701Sdim
392259701Sdim  EffectiveOp testEffectiveOp() const {
393259701Sdim    assert(InfoType == IT_BinTest);
394259701Sdim    return BinTest.EOp;
395259701Sdim  }
396259701Sdim
397259701Sdim  const BinaryOperator * testSourceNode() const {
398259701Sdim    assert(InfoType == IT_BinTest);
399259701Sdim    return BinTest.Source;
400259701Sdim  }
401259701Sdim
402259701Sdim  inline bool isValid()   const { return InfoType != IT_None;    }
403259701Sdim  inline bool isState()   const { return InfoType == IT_State;   }
404259701Sdim  inline bool isVarTest() const { return InfoType == IT_VarTest; }
405259701Sdim  inline bool isBinTest() const { return InfoType == IT_BinTest; }
406259701Sdim  inline bool isVar()     const { return InfoType == IT_Var;     }
407259701Sdim  inline bool isTmp()     const { return InfoType == IT_Tmp;     }
408259701Sdim
409259701Sdim  bool isTest() const {
410259701Sdim    return InfoType == IT_VarTest || InfoType == IT_BinTest;
411259701Sdim  }
412259701Sdim
413259701Sdim  bool isPointerToValue() const {
414259701Sdim    return InfoType == IT_Var || InfoType == IT_Tmp;
415259701Sdim  }
416259701Sdim
417259701Sdim  PropagationInfo invertTest() const {
418259701Sdim    assert(InfoType == IT_VarTest || InfoType == IT_BinTest);
419259701Sdim
420259701Sdim    if (InfoType == IT_VarTest) {
421259701Sdim      return PropagationInfo(VarTest.Var,
422259701Sdim                             invertConsumedUnconsumed(VarTest.TestsFor));
423259701Sdim
424259701Sdim    } else if (InfoType == IT_BinTest) {
425259701Sdim      return PropagationInfo(BinTest.Source,
426259701Sdim        BinTest.EOp == EO_And ? EO_Or : EO_And,
427259701Sdim        BinTest.LTest.Var, invertConsumedUnconsumed(BinTest.LTest.TestsFor),
428259701Sdim        BinTest.RTest.Var, invertConsumedUnconsumed(BinTest.RTest.TestsFor));
429259701Sdim    } else {
430259701Sdim      return PropagationInfo();
431259701Sdim    }
432259701Sdim  }
433259701Sdim};
434259701Sdim
435259701Sdimstatic inline void
436259701SdimsetStateForVarOrTmp(ConsumedStateMap *StateMap, const PropagationInfo &PInfo,
437259701Sdim                    ConsumedState State) {
438259701Sdim
439259701Sdim  assert(PInfo.isVar() || PInfo.isTmp());
440259701Sdim
441259701Sdim  if (PInfo.isVar())
442259701Sdim    StateMap->setState(PInfo.getVar(), State);
443259701Sdim  else
444259701Sdim    StateMap->setState(PInfo.getTmp(), State);
445259701Sdim}
446259701Sdim
447259701Sdimclass ConsumedStmtVisitor : public ConstStmtVisitor<ConsumedStmtVisitor> {
448259701Sdim
449259701Sdim  typedef llvm::DenseMap<const Stmt *, PropagationInfo> MapType;
450259701Sdim  typedef std::pair<const Stmt *, PropagationInfo> PairType;
451259701Sdim  typedef MapType::iterator InfoEntry;
452259701Sdim  typedef MapType::const_iterator ConstInfoEntry;
453259701Sdim
454259701Sdim  AnalysisDeclContext &AC;
455259701Sdim  ConsumedAnalyzer &Analyzer;
456259701Sdim  ConsumedStateMap *StateMap;
457259701Sdim  MapType PropagationMap;
458259701Sdim  void forwardInfo(const Stmt *From, const Stmt *To);
459259701Sdim  bool isLikeMoveAssignment(const CXXMethodDecl *MethodDecl);
460259701Sdim  void propagateReturnType(const Stmt *Call, const FunctionDecl *Fun,
461259701Sdim                           QualType ReturnType);
462259701Sdim
463259701Sdimpublic:
464259701Sdim  void checkCallability(const PropagationInfo &PInfo,
465259701Sdim                        const FunctionDecl *FunDecl,
466259701Sdim                        SourceLocation BlameLoc);
467259701Sdim
468259701Sdim  void VisitBinaryOperator(const BinaryOperator *BinOp);
469259701Sdim  void VisitCallExpr(const CallExpr *Call);
470259701Sdim  void VisitCastExpr(const CastExpr *Cast);
471259701Sdim  void VisitCXXBindTemporaryExpr(const CXXBindTemporaryExpr *Temp);
472259701Sdim  void VisitCXXConstructExpr(const CXXConstructExpr *Call);
473259701Sdim  void VisitCXXMemberCallExpr(const CXXMemberCallExpr *Call);
474259701Sdim  void VisitCXXOperatorCallExpr(const CXXOperatorCallExpr *Call);
475259701Sdim  void VisitDeclRefExpr(const DeclRefExpr *DeclRef);
476259701Sdim  void VisitDeclStmt(const DeclStmt *DelcS);
477259701Sdim  void VisitMaterializeTemporaryExpr(const MaterializeTemporaryExpr *Temp);
478259701Sdim  void VisitMemberExpr(const MemberExpr *MExpr);
479259701Sdim  void VisitParmVarDecl(const ParmVarDecl *Param);
480259701Sdim  void VisitReturnStmt(const ReturnStmt *Ret);
481259701Sdim  void VisitUnaryOperator(const UnaryOperator *UOp);
482259701Sdim  void VisitVarDecl(const VarDecl *Var);
483259701Sdim
484259701Sdim  ConsumedStmtVisitor(AnalysisDeclContext &AC, ConsumedAnalyzer &Analyzer,
485259701Sdim                      ConsumedStateMap *StateMap)
486259701Sdim      : AC(AC), Analyzer(Analyzer), StateMap(StateMap) {}
487259701Sdim
488259701Sdim  PropagationInfo getInfo(const Stmt *StmtNode) const {
489259701Sdim    ConstInfoEntry Entry = PropagationMap.find(StmtNode);
490259701Sdim
491259701Sdim    if (Entry != PropagationMap.end())
492259701Sdim      return Entry->second;
493259701Sdim    else
494259701Sdim      return PropagationInfo();
495259701Sdim  }
496259701Sdim
497259701Sdim  void reset(ConsumedStateMap *NewStateMap) {
498259701Sdim    StateMap = NewStateMap;
499259701Sdim  }
500259701Sdim};
501259701Sdim
502259701Sdimvoid ConsumedStmtVisitor::checkCallability(const PropagationInfo &PInfo,
503259701Sdim                                           const FunctionDecl *FunDecl,
504259701Sdim                                           SourceLocation BlameLoc) {
505259701Sdim  assert(!PInfo.isTest());
506259701Sdim
507259701Sdim  if (!FunDecl->hasAttr<CallableWhenAttr>())
508259701Sdim    return;
509259701Sdim
510259701Sdim  const CallableWhenAttr *CWAttr = FunDecl->getAttr<CallableWhenAttr>();
511259701Sdim
512259701Sdim  if (PInfo.isVar()) {
513259701Sdim    ConsumedState VarState = StateMap->getState(PInfo.getVar());
514259701Sdim
515259701Sdim    if (VarState == CS_None || isCallableInState(CWAttr, VarState))
516259701Sdim      return;
517259701Sdim
518259701Sdim    Analyzer.WarningsHandler.warnUseInInvalidState(
519259701Sdim      FunDecl->getNameAsString(), PInfo.getVar()->getNameAsString(),
520259701Sdim      stateToString(VarState), BlameLoc);
521259701Sdim
522259701Sdim  } else {
523259701Sdim    ConsumedState TmpState = PInfo.getAsState(StateMap);
524259701Sdim
525259701Sdim    if (TmpState == CS_None || isCallableInState(CWAttr, TmpState))
526259701Sdim      return;
527259701Sdim
528259701Sdim    Analyzer.WarningsHandler.warnUseOfTempInInvalidState(
529259701Sdim      FunDecl->getNameAsString(), stateToString(TmpState), BlameLoc);
530259701Sdim  }
531259701Sdim}
532259701Sdim
533259701Sdimvoid ConsumedStmtVisitor::forwardInfo(const Stmt *From, const Stmt *To) {
534259701Sdim  InfoEntry Entry = PropagationMap.find(From);
535259701Sdim
536259701Sdim  if (Entry != PropagationMap.end())
537259701Sdim    PropagationMap.insert(PairType(To, Entry->second));
538259701Sdim}
539259701Sdim
540259701Sdimbool ConsumedStmtVisitor::isLikeMoveAssignment(
541259701Sdim  const CXXMethodDecl *MethodDecl) {
542259701Sdim
543259701Sdim  return MethodDecl->isMoveAssignmentOperator() ||
544259701Sdim         (MethodDecl->getOverloadedOperator() == OO_Equal &&
545259701Sdim          MethodDecl->getNumParams() == 1 &&
546259701Sdim          MethodDecl->getParamDecl(0)->getType()->isRValueReferenceType());
547259701Sdim}
548259701Sdim
549259701Sdimvoid ConsumedStmtVisitor::propagateReturnType(const Stmt *Call,
550259701Sdim                                              const FunctionDecl *Fun,
551259701Sdim                                              QualType ReturnType) {
552259701Sdim  if (isConsumableType(ReturnType)) {
553259701Sdim
554259701Sdim    ConsumedState ReturnState;
555259701Sdim
556259701Sdim    if (Fun->hasAttr<ReturnTypestateAttr>())
557259701Sdim      ReturnState = mapReturnTypestateAttrState(
558259701Sdim        Fun->getAttr<ReturnTypestateAttr>());
559259701Sdim    else
560259701Sdim      ReturnState = mapConsumableAttrState(ReturnType);
561259701Sdim
562259701Sdim    PropagationMap.insert(PairType(Call, PropagationInfo(ReturnState)));
563259701Sdim  }
564259701Sdim}
565259701Sdim
566259701Sdimvoid ConsumedStmtVisitor::VisitBinaryOperator(const BinaryOperator *BinOp) {
567259701Sdim  switch (BinOp->getOpcode()) {
568259701Sdim  case BO_LAnd:
569259701Sdim  case BO_LOr : {
570259701Sdim    InfoEntry LEntry = PropagationMap.find(BinOp->getLHS()),
571259701Sdim              REntry = PropagationMap.find(BinOp->getRHS());
572259701Sdim
573259701Sdim    VarTestResult LTest, RTest;
574259701Sdim
575259701Sdim    if (LEntry != PropagationMap.end() && LEntry->second.isVarTest()) {
576259701Sdim      LTest = LEntry->second.getVarTest();
577259701Sdim
578259701Sdim    } else {
579259701Sdim      LTest.Var      = NULL;
580259701Sdim      LTest.TestsFor = CS_None;
581259701Sdim    }
582259701Sdim
583259701Sdim    if (REntry != PropagationMap.end() && REntry->second.isVarTest()) {
584259701Sdim      RTest = REntry->second.getVarTest();
585259701Sdim
586259701Sdim    } else {
587259701Sdim      RTest.Var      = NULL;
588259701Sdim      RTest.TestsFor = CS_None;
589259701Sdim    }
590259701Sdim
591259701Sdim    if (!(LTest.Var == NULL && RTest.Var == NULL))
592259701Sdim      PropagationMap.insert(PairType(BinOp, PropagationInfo(BinOp,
593259701Sdim        static_cast<EffectiveOp>(BinOp->getOpcode() == BO_LOr), LTest, RTest)));
594259701Sdim
595259701Sdim    break;
596259701Sdim  }
597259701Sdim
598259701Sdim  case BO_PtrMemD:
599259701Sdim  case BO_PtrMemI:
600259701Sdim    forwardInfo(BinOp->getLHS(), BinOp);
601259701Sdim    break;
602259701Sdim
603259701Sdim  default:
604259701Sdim    break;
605259701Sdim  }
606259701Sdim}
607259701Sdim
608266715Sdimstatic bool isStdNamespace(const DeclContext *DC) {
609266715Sdim  if (!DC->isNamespace()) return false;
610266715Sdim  while (DC->getParent()->isNamespace())
611266715Sdim    DC = DC->getParent();
612266715Sdim  const NamespaceDecl *ND = dyn_cast<NamespaceDecl>(DC);
613266715Sdim
614266715Sdim  return ND && ND->getName() == "std" &&
615266715Sdim         ND->getDeclContext()->isTranslationUnit();
616266715Sdim}
617266715Sdim
618259701Sdimvoid ConsumedStmtVisitor::VisitCallExpr(const CallExpr *Call) {
619259701Sdim  if (const FunctionDecl *FunDecl =
620259701Sdim    dyn_cast_or_null<FunctionDecl>(Call->getDirectCallee())) {
621259701Sdim
622259701Sdim    // Special case for the std::move function.
623259701Sdim    // TODO: Make this more specific. (Deferred)
624266715Sdim    if (Call->getNumArgs() == 1 &&
625266715Sdim        FunDecl->getNameAsString() == "move" &&
626266715Sdim        isStdNamespace(FunDecl->getDeclContext())) {
627259701Sdim      forwardInfo(Call->getArg(0), Call);
628259701Sdim      return;
629259701Sdim    }
630259701Sdim
631259701Sdim    unsigned Offset = Call->getNumArgs() - FunDecl->getNumParams();
632259701Sdim
633259701Sdim    for (unsigned Index = Offset; Index < Call->getNumArgs(); ++Index) {
634259701Sdim      const ParmVarDecl *Param = FunDecl->getParamDecl(Index - Offset);
635259701Sdim      QualType ParamType = Param->getType();
636259701Sdim
637259701Sdim      InfoEntry Entry = PropagationMap.find(Call->getArg(Index));
638259701Sdim
639259701Sdim      if (Entry == PropagationMap.end() || Entry->second.isTest())
640259701Sdim        continue;
641259701Sdim
642259701Sdim      PropagationInfo PInfo = Entry->second;
643259701Sdim
644259701Sdim      // Check that the parameter is in the correct state.
645259701Sdim
646259701Sdim      if (Param->hasAttr<ParamTypestateAttr>()) {
647259701Sdim        ConsumedState ParamState = PInfo.getAsState(StateMap);
648259701Sdim
649259701Sdim        ConsumedState ExpectedState =
650259701Sdim          mapParamTypestateAttrState(Param->getAttr<ParamTypestateAttr>());
651259701Sdim
652259701Sdim        if (ParamState != ExpectedState)
653259701Sdim          Analyzer.WarningsHandler.warnParamTypestateMismatch(
654259701Sdim            Call->getArg(Index - Offset)->getExprLoc(),
655259701Sdim            stateToString(ExpectedState), stateToString(ParamState));
656259701Sdim      }
657259701Sdim
658259701Sdim      if (!(Entry->second.isVar() || Entry->second.isTmp()))
659259701Sdim        continue;
660259701Sdim
661259701Sdim      // Adjust state on the caller side.
662259701Sdim
663259701Sdim      if (isRValueRefish(ParamType)) {
664259701Sdim        setStateForVarOrTmp(StateMap, PInfo, consumed::CS_Consumed);
665259701Sdim
666259701Sdim      } else if (Param->hasAttr<ReturnTypestateAttr>()) {
667259701Sdim        setStateForVarOrTmp(StateMap, PInfo,
668259701Sdim          mapReturnTypestateAttrState(Param->getAttr<ReturnTypestateAttr>()));
669259701Sdim
670259701Sdim      } else if (!isValueType(ParamType) &&
671259701Sdim                 !ParamType->getPointeeType().isConstQualified()) {
672259701Sdim
673259701Sdim        setStateForVarOrTmp(StateMap, PInfo, consumed::CS_Unknown);
674259701Sdim      }
675259701Sdim    }
676259701Sdim
677259701Sdim    QualType RetType = FunDecl->getCallResultType();
678259701Sdim    if (RetType->isReferenceType())
679259701Sdim      RetType = RetType->getPointeeType();
680259701Sdim
681259701Sdim    propagateReturnType(Call, FunDecl, RetType);
682259701Sdim  }
683259701Sdim}
684259701Sdim
685259701Sdimvoid ConsumedStmtVisitor::VisitCastExpr(const CastExpr *Cast) {
686259701Sdim  forwardInfo(Cast->getSubExpr(), Cast);
687259701Sdim}
688259701Sdim
689259701Sdimvoid ConsumedStmtVisitor::VisitCXXBindTemporaryExpr(
690259701Sdim  const CXXBindTemporaryExpr *Temp) {
691259701Sdim
692259701Sdim  InfoEntry Entry = PropagationMap.find(Temp->getSubExpr());
693259701Sdim
694259701Sdim  if (Entry != PropagationMap.end() && !Entry->second.isTest()) {
695259701Sdim    StateMap->setState(Temp, Entry->second.getAsState(StateMap));
696259701Sdim    PropagationMap.insert(PairType(Temp, PropagationInfo(Temp)));
697259701Sdim  }
698259701Sdim}
699259701Sdim
700259701Sdimvoid ConsumedStmtVisitor::VisitCXXConstructExpr(const CXXConstructExpr *Call) {
701259701Sdim  CXXConstructorDecl *Constructor = Call->getConstructor();
702259701Sdim
703259701Sdim  ASTContext &CurrContext = AC.getASTContext();
704259701Sdim  QualType ThisType = Constructor->getThisType(CurrContext)->getPointeeType();
705259701Sdim
706259701Sdim  if (!isConsumableType(ThisType))
707259701Sdim    return;
708259701Sdim
709259701Sdim  // FIXME: What should happen if someone annotates the move constructor?
710259701Sdim  if (Constructor->hasAttr<ReturnTypestateAttr>()) {
711259701Sdim    // TODO: Adjust state of args appropriately.
712259701Sdim
713259701Sdim    ReturnTypestateAttr *RTAttr = Constructor->getAttr<ReturnTypestateAttr>();
714259701Sdim    ConsumedState RetState = mapReturnTypestateAttrState(RTAttr);
715259701Sdim    PropagationMap.insert(PairType(Call, PropagationInfo(RetState)));
716259701Sdim
717259701Sdim  } else if (Constructor->isDefaultConstructor()) {
718259701Sdim
719259701Sdim    PropagationMap.insert(PairType(Call,
720259701Sdim      PropagationInfo(consumed::CS_Consumed)));
721259701Sdim
722259701Sdim  } else if (Constructor->isMoveConstructor()) {
723259701Sdim
724259701Sdim    InfoEntry Entry = PropagationMap.find(Call->getArg(0));
725259701Sdim
726259701Sdim    if (Entry != PropagationMap.end()) {
727259701Sdim      PropagationInfo PInfo = Entry->second;
728259701Sdim
729259701Sdim      if (PInfo.isVar()) {
730259701Sdim        const VarDecl* Var = PInfo.getVar();
731259701Sdim
732259701Sdim        PropagationMap.insert(PairType(Call,
733259701Sdim          PropagationInfo(StateMap->getState(Var))));
734259701Sdim
735259701Sdim        StateMap->setState(Var, consumed::CS_Consumed);
736259701Sdim
737259701Sdim      } else if (PInfo.isTmp()) {
738259701Sdim        const CXXBindTemporaryExpr *Tmp = PInfo.getTmp();
739259701Sdim
740259701Sdim        PropagationMap.insert(PairType(Call,
741259701Sdim          PropagationInfo(StateMap->getState(Tmp))));
742259701Sdim
743259701Sdim        StateMap->setState(Tmp, consumed::CS_Consumed);
744259701Sdim
745259701Sdim      } else {
746259701Sdim        PropagationMap.insert(PairType(Call, PInfo));
747259701Sdim      }
748259701Sdim    }
749259701Sdim  } else if (Constructor->isCopyConstructor()) {
750259701Sdim    forwardInfo(Call->getArg(0), Call);
751259701Sdim
752259701Sdim  } else {
753259701Sdim    // TODO: Adjust state of args appropriately.
754259701Sdim
755259701Sdim    ConsumedState RetState = mapConsumableAttrState(ThisType);
756259701Sdim    PropagationMap.insert(PairType(Call, PropagationInfo(RetState)));
757259701Sdim  }
758259701Sdim}
759259701Sdim
760259701Sdimvoid ConsumedStmtVisitor::VisitCXXMemberCallExpr(
761259701Sdim  const CXXMemberCallExpr *Call) {
762259701Sdim
763259701Sdim  VisitCallExpr(Call);
764259701Sdim
765259701Sdim  InfoEntry Entry = PropagationMap.find(Call->getCallee()->IgnoreParens());
766259701Sdim
767259701Sdim  if (Entry != PropagationMap.end()) {
768259701Sdim    PropagationInfo PInfo = Entry->second;
769259701Sdim    const CXXMethodDecl *MethodDecl = Call->getMethodDecl();
770259701Sdim
771259701Sdim    checkCallability(PInfo, MethodDecl, Call->getExprLoc());
772259701Sdim
773259701Sdim    if (PInfo.isVar()) {
774259701Sdim      if (isTestingFunction(MethodDecl))
775259701Sdim        PropagationMap.insert(PairType(Call,
776259701Sdim          PropagationInfo(PInfo.getVar(), testsFor(MethodDecl))));
777259701Sdim      else if (MethodDecl->hasAttr<SetTypestateAttr>())
778259701Sdim        StateMap->setState(PInfo.getVar(),
779259701Sdim          mapSetTypestateAttrState(MethodDecl->getAttr<SetTypestateAttr>()));
780259701Sdim    } else if (PInfo.isTmp() && MethodDecl->hasAttr<SetTypestateAttr>()) {
781259701Sdim      StateMap->setState(PInfo.getTmp(),
782259701Sdim        mapSetTypestateAttrState(MethodDecl->getAttr<SetTypestateAttr>()));
783259701Sdim    }
784259701Sdim  }
785259701Sdim}
786259701Sdim
787259701Sdimvoid ConsumedStmtVisitor::VisitCXXOperatorCallExpr(
788259701Sdim  const CXXOperatorCallExpr *Call) {
789259701Sdim
790259701Sdim  const FunctionDecl *FunDecl =
791259701Sdim    dyn_cast_or_null<FunctionDecl>(Call->getDirectCallee());
792259701Sdim
793259701Sdim  if (!FunDecl) return;
794259701Sdim
795259701Sdim  if (isa<CXXMethodDecl>(FunDecl) &&
796259701Sdim      isLikeMoveAssignment(cast<CXXMethodDecl>(FunDecl))) {
797259701Sdim
798259701Sdim    InfoEntry LEntry = PropagationMap.find(Call->getArg(0));
799259701Sdim    InfoEntry REntry = PropagationMap.find(Call->getArg(1));
800259701Sdim
801259701Sdim    PropagationInfo LPInfo, RPInfo;
802259701Sdim
803259701Sdim    if (LEntry != PropagationMap.end() &&
804259701Sdim        REntry != PropagationMap.end()) {
805259701Sdim
806259701Sdim      LPInfo = LEntry->second;
807259701Sdim      RPInfo = REntry->second;
808259701Sdim
809259701Sdim      if (LPInfo.isPointerToValue() && RPInfo.isPointerToValue()) {
810259701Sdim        setStateForVarOrTmp(StateMap, LPInfo, RPInfo.getAsState(StateMap));
811259701Sdim        PropagationMap.insert(PairType(Call, LPInfo));
812259701Sdim        setStateForVarOrTmp(StateMap, RPInfo, consumed::CS_Consumed);
813259701Sdim
814259701Sdim      } else if (RPInfo.isState()) {
815259701Sdim        setStateForVarOrTmp(StateMap, LPInfo, RPInfo.getState());
816259701Sdim        PropagationMap.insert(PairType(Call, LPInfo));
817259701Sdim
818259701Sdim      } else {
819259701Sdim        setStateForVarOrTmp(StateMap, RPInfo, consumed::CS_Consumed);
820259701Sdim      }
821259701Sdim
822259701Sdim    } else if (LEntry != PropagationMap.end() &&
823259701Sdim               REntry == PropagationMap.end()) {
824259701Sdim
825259701Sdim      LPInfo = LEntry->second;
826259701Sdim
827259701Sdim      assert(!LPInfo.isTest());
828259701Sdim
829259701Sdim      if (LPInfo.isPointerToValue()) {
830259701Sdim        setStateForVarOrTmp(StateMap, LPInfo, consumed::CS_Unknown);
831259701Sdim        PropagationMap.insert(PairType(Call, LPInfo));
832259701Sdim
833259701Sdim      } else {
834259701Sdim        PropagationMap.insert(PairType(Call,
835259701Sdim          PropagationInfo(consumed::CS_Unknown)));
836259701Sdim      }
837259701Sdim
838259701Sdim    } else if (LEntry == PropagationMap.end() &&
839259701Sdim               REntry != PropagationMap.end()) {
840259701Sdim
841259701Sdim      RPInfo = REntry->second;
842259701Sdim
843259701Sdim      if (RPInfo.isPointerToValue())
844259701Sdim        setStateForVarOrTmp(StateMap, RPInfo, consumed::CS_Consumed);
845259701Sdim    }
846259701Sdim
847259701Sdim  } else {
848259701Sdim
849259701Sdim    VisitCallExpr(Call);
850259701Sdim
851259701Sdim    InfoEntry Entry = PropagationMap.find(Call->getArg(0));
852259701Sdim
853259701Sdim    if (Entry != PropagationMap.end()) {
854259701Sdim      PropagationInfo PInfo = Entry->second;
855259701Sdim
856259701Sdim      checkCallability(PInfo, FunDecl, Call->getExprLoc());
857259701Sdim
858259701Sdim      if (PInfo.isVar()) {
859259701Sdim        if (isTestingFunction(FunDecl))
860259701Sdim          PropagationMap.insert(PairType(Call,
861259701Sdim            PropagationInfo(PInfo.getVar(), testsFor(FunDecl))));
862259701Sdim        else if (FunDecl->hasAttr<SetTypestateAttr>())
863259701Sdim          StateMap->setState(PInfo.getVar(),
864259701Sdim            mapSetTypestateAttrState(FunDecl->getAttr<SetTypestateAttr>()));
865259701Sdim
866259701Sdim      } else if (PInfo.isTmp() && FunDecl->hasAttr<SetTypestateAttr>()) {
867259701Sdim        StateMap->setState(PInfo.getTmp(),
868259701Sdim          mapSetTypestateAttrState(FunDecl->getAttr<SetTypestateAttr>()));
869259701Sdim    }
870259701Sdim    }
871259701Sdim  }
872259701Sdim}
873259701Sdim
874259701Sdimvoid ConsumedStmtVisitor::VisitDeclRefExpr(const DeclRefExpr *DeclRef) {
875259701Sdim  if (const VarDecl *Var = dyn_cast_or_null<VarDecl>(DeclRef->getDecl()))
876259701Sdim    if (StateMap->getState(Var) != consumed::CS_None)
877259701Sdim      PropagationMap.insert(PairType(DeclRef, PropagationInfo(Var)));
878259701Sdim}
879259701Sdim
880259701Sdimvoid ConsumedStmtVisitor::VisitDeclStmt(const DeclStmt *DeclS) {
881259701Sdim  for (DeclStmt::const_decl_iterator DI = DeclS->decl_begin(),
882259701Sdim       DE = DeclS->decl_end(); DI != DE; ++DI) {
883259701Sdim
884259701Sdim    if (isa<VarDecl>(*DI)) VisitVarDecl(cast<VarDecl>(*DI));
885259701Sdim  }
886259701Sdim
887259701Sdim  if (DeclS->isSingleDecl())
888259701Sdim    if (const VarDecl *Var = dyn_cast_or_null<VarDecl>(DeclS->getSingleDecl()))
889259701Sdim      PropagationMap.insert(PairType(DeclS, PropagationInfo(Var)));
890259701Sdim}
891259701Sdim
892259701Sdimvoid ConsumedStmtVisitor::VisitMaterializeTemporaryExpr(
893259701Sdim  const MaterializeTemporaryExpr *Temp) {
894259701Sdim
895259701Sdim  forwardInfo(Temp->GetTemporaryExpr(), Temp);
896259701Sdim}
897259701Sdim
898259701Sdimvoid ConsumedStmtVisitor::VisitMemberExpr(const MemberExpr *MExpr) {
899259701Sdim  forwardInfo(MExpr->getBase(), MExpr);
900259701Sdim}
901259701Sdim
902259701Sdim
903259701Sdimvoid ConsumedStmtVisitor::VisitParmVarDecl(const ParmVarDecl *Param) {
904259701Sdim  QualType ParamType = Param->getType();
905259701Sdim  ConsumedState ParamState = consumed::CS_None;
906259701Sdim
907259701Sdim  if (Param->hasAttr<ParamTypestateAttr>()) {
908259701Sdim    const ParamTypestateAttr *PTAttr = Param->getAttr<ParamTypestateAttr>();
909259701Sdim    ParamState = mapParamTypestateAttrState(PTAttr);
910259701Sdim
911259701Sdim  } else if (isConsumableType(ParamType)) {
912259701Sdim    ParamState = mapConsumableAttrState(ParamType);
913259701Sdim
914259701Sdim  } else if (isRValueRefish(ParamType) &&
915259701Sdim             isConsumableType(ParamType->getPointeeType())) {
916259701Sdim
917259701Sdim    ParamState = mapConsumableAttrState(ParamType->getPointeeType());
918259701Sdim
919259701Sdim  } else if (ParamType->isReferenceType() &&
920259701Sdim             isConsumableType(ParamType->getPointeeType())) {
921259701Sdim    ParamState = consumed::CS_Unknown;
922259701Sdim  }
923259701Sdim
924259701Sdim  if (ParamState != CS_None)
925259701Sdim    StateMap->setState(Param, ParamState);
926259701Sdim}
927259701Sdim
928259701Sdimvoid ConsumedStmtVisitor::VisitReturnStmt(const ReturnStmt *Ret) {
929259701Sdim  ConsumedState ExpectedState = Analyzer.getExpectedReturnState();
930259701Sdim
931259701Sdim  if (ExpectedState != CS_None) {
932259701Sdim    InfoEntry Entry = PropagationMap.find(Ret->getRetValue());
933259701Sdim
934259701Sdim    if (Entry != PropagationMap.end()) {
935259701Sdim      ConsumedState RetState = Entry->second.getAsState(StateMap);
936259701Sdim
937259701Sdim      if (RetState != ExpectedState)
938259701Sdim        Analyzer.WarningsHandler.warnReturnTypestateMismatch(
939259701Sdim          Ret->getReturnLoc(), stateToString(ExpectedState),
940259701Sdim          stateToString(RetState));
941259701Sdim    }
942259701Sdim  }
943259701Sdim
944259701Sdim  StateMap->checkParamsForReturnTypestate(Ret->getLocStart(),
945259701Sdim                                          Analyzer.WarningsHandler);
946259701Sdim}
947259701Sdim
948259701Sdimvoid ConsumedStmtVisitor::VisitUnaryOperator(const UnaryOperator *UOp) {
949259701Sdim  InfoEntry Entry = PropagationMap.find(UOp->getSubExpr()->IgnoreParens());
950259701Sdim  if (Entry == PropagationMap.end()) return;
951259701Sdim
952259701Sdim  switch (UOp->getOpcode()) {
953259701Sdim  case UO_AddrOf:
954259701Sdim    PropagationMap.insert(PairType(UOp, Entry->second));
955259701Sdim    break;
956259701Sdim
957259701Sdim  case UO_LNot:
958259701Sdim    if (Entry->second.isTest())
959259701Sdim      PropagationMap.insert(PairType(UOp, Entry->second.invertTest()));
960259701Sdim    break;
961259701Sdim
962259701Sdim  default:
963259701Sdim    break;
964259701Sdim  }
965259701Sdim}
966259701Sdim
967259701Sdim// TODO: See if I need to check for reference types here.
968259701Sdimvoid ConsumedStmtVisitor::VisitVarDecl(const VarDecl *Var) {
969259701Sdim  if (isConsumableType(Var->getType())) {
970259701Sdim    if (Var->hasInit()) {
971259701Sdim      MapType::iterator VIT = PropagationMap.find(
972259701Sdim        Var->getInit()->IgnoreImplicit());
973259701Sdim      if (VIT != PropagationMap.end()) {
974259701Sdim        PropagationInfo PInfo = VIT->second;
975259701Sdim        ConsumedState St = PInfo.getAsState(StateMap);
976259701Sdim
977259701Sdim        if (St != consumed::CS_None) {
978259701Sdim          StateMap->setState(Var, St);
979259701Sdim          return;
980259701Sdim        }
981259701Sdim      }
982259701Sdim    }
983259701Sdim    // Otherwise
984259701Sdim    StateMap->setState(Var, consumed::CS_Unknown);
985259701Sdim  }
986259701Sdim}
987259701Sdim}} // end clang::consumed::ConsumedStmtVisitor
988259701Sdim
989259701Sdimnamespace clang {
990259701Sdimnamespace consumed {
991259701Sdim
992259701Sdimvoid splitVarStateForIf(const IfStmt * IfNode, const VarTestResult &Test,
993259701Sdim                        ConsumedStateMap *ThenStates,
994259701Sdim                        ConsumedStateMap *ElseStates) {
995259701Sdim
996259701Sdim  ConsumedState VarState = ThenStates->getState(Test.Var);
997259701Sdim
998259701Sdim  if (VarState == CS_Unknown) {
999259701Sdim    ThenStates->setState(Test.Var, Test.TestsFor);
1000259701Sdim    ElseStates->setState(Test.Var, invertConsumedUnconsumed(Test.TestsFor));
1001259701Sdim
1002259701Sdim  } else if (VarState == invertConsumedUnconsumed(Test.TestsFor)) {
1003259701Sdim    ThenStates->markUnreachable();
1004259701Sdim
1005259701Sdim  } else if (VarState == Test.TestsFor) {
1006259701Sdim    ElseStates->markUnreachable();
1007259701Sdim  }
1008259701Sdim}
1009259701Sdim
1010259701Sdimvoid splitVarStateForIfBinOp(const PropagationInfo &PInfo,
1011259701Sdim  ConsumedStateMap *ThenStates, ConsumedStateMap *ElseStates) {
1012259701Sdim
1013259701Sdim  const VarTestResult &LTest = PInfo.getLTest(),
1014259701Sdim                      &RTest = PInfo.getRTest();
1015259701Sdim
1016259701Sdim  ConsumedState LState = LTest.Var ? ThenStates->getState(LTest.Var) : CS_None,
1017259701Sdim                RState = RTest.Var ? ThenStates->getState(RTest.Var) : CS_None;
1018259701Sdim
1019259701Sdim  if (LTest.Var) {
1020259701Sdim    if (PInfo.testEffectiveOp() == EO_And) {
1021259701Sdim      if (LState == CS_Unknown) {
1022259701Sdim        ThenStates->setState(LTest.Var, LTest.TestsFor);
1023259701Sdim
1024259701Sdim      } else if (LState == invertConsumedUnconsumed(LTest.TestsFor)) {
1025259701Sdim        ThenStates->markUnreachable();
1026259701Sdim
1027259701Sdim      } else if (LState == LTest.TestsFor && isKnownState(RState)) {
1028259701Sdim        if (RState == RTest.TestsFor)
1029259701Sdim          ElseStates->markUnreachable();
1030259701Sdim        else
1031259701Sdim          ThenStates->markUnreachable();
1032259701Sdim      }
1033259701Sdim
1034259701Sdim    } else {
1035259701Sdim      if (LState == CS_Unknown) {
1036259701Sdim        ElseStates->setState(LTest.Var,
1037259701Sdim                             invertConsumedUnconsumed(LTest.TestsFor));
1038259701Sdim
1039259701Sdim      } else if (LState == LTest.TestsFor) {
1040259701Sdim        ElseStates->markUnreachable();
1041259701Sdim
1042259701Sdim      } else if (LState == invertConsumedUnconsumed(LTest.TestsFor) &&
1043259701Sdim                 isKnownState(RState)) {
1044259701Sdim
1045259701Sdim        if (RState == RTest.TestsFor)
1046259701Sdim          ElseStates->markUnreachable();
1047259701Sdim        else
1048259701Sdim          ThenStates->markUnreachable();
1049259701Sdim      }
1050259701Sdim    }
1051259701Sdim  }
1052259701Sdim
1053259701Sdim  if (RTest.Var) {
1054259701Sdim    if (PInfo.testEffectiveOp() == EO_And) {
1055259701Sdim      if (RState == CS_Unknown)
1056259701Sdim        ThenStates->setState(RTest.Var, RTest.TestsFor);
1057259701Sdim      else if (RState == invertConsumedUnconsumed(RTest.TestsFor))
1058259701Sdim        ThenStates->markUnreachable();
1059259701Sdim
1060259701Sdim    } else {
1061259701Sdim      if (RState == CS_Unknown)
1062259701Sdim        ElseStates->setState(RTest.Var,
1063259701Sdim                             invertConsumedUnconsumed(RTest.TestsFor));
1064259701Sdim      else if (RState == RTest.TestsFor)
1065259701Sdim        ElseStates->markUnreachable();
1066259701Sdim    }
1067259701Sdim  }
1068259701Sdim}
1069259701Sdim
1070259701Sdimbool ConsumedBlockInfo::allBackEdgesVisited(const CFGBlock *CurrBlock,
1071259701Sdim                                            const CFGBlock *TargetBlock) {
1072259701Sdim
1073259701Sdim  assert(CurrBlock && "Block pointer must not be NULL");
1074259701Sdim  assert(TargetBlock && "TargetBlock pointer must not be NULL");
1075259701Sdim
1076259701Sdim  unsigned int CurrBlockOrder = VisitOrder[CurrBlock->getBlockID()];
1077259701Sdim  for (CFGBlock::const_pred_iterator PI = TargetBlock->pred_begin(),
1078259701Sdim       PE = TargetBlock->pred_end(); PI != PE; ++PI) {
1079259701Sdim    if (*PI && CurrBlockOrder < VisitOrder[(*PI)->getBlockID()] )
1080259701Sdim      return false;
1081259701Sdim  }
1082259701Sdim  return true;
1083259701Sdim}
1084259701Sdim
1085259701Sdimvoid ConsumedBlockInfo::addInfo(const CFGBlock *Block,
1086259701Sdim                                ConsumedStateMap *StateMap,
1087259701Sdim                                bool &AlreadyOwned) {
1088259701Sdim
1089259701Sdim  assert(Block && "Block pointer must not be NULL");
1090259701Sdim
1091259701Sdim  ConsumedStateMap *Entry = StateMapsArray[Block->getBlockID()];
1092259701Sdim
1093259701Sdim  if (Entry) {
1094259701Sdim    Entry->intersect(StateMap);
1095259701Sdim
1096259701Sdim  } else if (AlreadyOwned) {
1097259701Sdim    StateMapsArray[Block->getBlockID()] = new ConsumedStateMap(*StateMap);
1098259701Sdim
1099259701Sdim  } else {
1100259701Sdim    StateMapsArray[Block->getBlockID()] = StateMap;
1101259701Sdim    AlreadyOwned = true;
1102259701Sdim  }
1103259701Sdim}
1104259701Sdim
1105259701Sdimvoid ConsumedBlockInfo::addInfo(const CFGBlock *Block,
1106259701Sdim                                ConsumedStateMap *StateMap) {
1107259701Sdim
1108259701Sdim  assert(Block != NULL && "Block pointer must not be NULL");
1109259701Sdim
1110259701Sdim  ConsumedStateMap *Entry = StateMapsArray[Block->getBlockID()];
1111259701Sdim
1112259701Sdim  if (Entry) {
1113259701Sdim    Entry->intersect(StateMap);
1114259701Sdim    delete StateMap;
1115259701Sdim
1116259701Sdim  } else {
1117259701Sdim    StateMapsArray[Block->getBlockID()] = StateMap;
1118259701Sdim  }
1119259701Sdim}
1120259701Sdim
1121259701SdimConsumedStateMap* ConsumedBlockInfo::borrowInfo(const CFGBlock *Block) {
1122259701Sdim  assert(Block && "Block pointer must not be NULL");
1123259701Sdim  assert(StateMapsArray[Block->getBlockID()] && "Block has no block info");
1124259701Sdim
1125259701Sdim  return StateMapsArray[Block->getBlockID()];
1126259701Sdim}
1127259701Sdim
1128259701Sdimvoid ConsumedBlockInfo::discardInfo(const CFGBlock *Block) {
1129259701Sdim  unsigned int BlockID = Block->getBlockID();
1130259701Sdim  delete StateMapsArray[BlockID];
1131259701Sdim  StateMapsArray[BlockID] = NULL;
1132259701Sdim}
1133259701Sdim
1134259701SdimConsumedStateMap* ConsumedBlockInfo::getInfo(const CFGBlock *Block) {
1135259701Sdim  assert(Block && "Block pointer must not be NULL");
1136259701Sdim
1137259701Sdim  ConsumedStateMap *StateMap = StateMapsArray[Block->getBlockID()];
1138259701Sdim  if (isBackEdgeTarget(Block)) {
1139259701Sdim    return new ConsumedStateMap(*StateMap);
1140259701Sdim  } else {
1141259701Sdim    StateMapsArray[Block->getBlockID()] = NULL;
1142259701Sdim    return StateMap;
1143259701Sdim  }
1144259701Sdim}
1145259701Sdim
1146259701Sdimbool ConsumedBlockInfo::isBackEdge(const CFGBlock *From, const CFGBlock *To) {
1147259701Sdim  assert(From && "From block must not be NULL");
1148259701Sdim  assert(To   && "From block must not be NULL");
1149259701Sdim
1150259701Sdim  return VisitOrder[From->getBlockID()] > VisitOrder[To->getBlockID()];
1151259701Sdim}
1152259701Sdim
1153259701Sdimbool ConsumedBlockInfo::isBackEdgeTarget(const CFGBlock *Block) {
1154259701Sdim  assert(Block != NULL && "Block pointer must not be NULL");
1155259701Sdim
1156259701Sdim  // Anything with less than two predecessors can't be the target of a back
1157259701Sdim  // edge.
1158259701Sdim  if (Block->pred_size() < 2)
1159259701Sdim    return false;
1160259701Sdim
1161259701Sdim  unsigned int BlockVisitOrder = VisitOrder[Block->getBlockID()];
1162259701Sdim  for (CFGBlock::const_pred_iterator PI = Block->pred_begin(),
1163259701Sdim       PE = Block->pred_end(); PI != PE; ++PI) {
1164259701Sdim    if (*PI && BlockVisitOrder < VisitOrder[(*PI)->getBlockID()])
1165259701Sdim      return true;
1166259701Sdim  }
1167259701Sdim  return false;
1168259701Sdim}
1169259701Sdim
1170259701Sdimvoid ConsumedStateMap::checkParamsForReturnTypestate(SourceLocation BlameLoc,
1171259701Sdim  ConsumedWarningsHandlerBase &WarningsHandler) const {
1172259701Sdim
1173259701Sdim  ConsumedState ExpectedState;
1174259701Sdim
1175259701Sdim  for (VarMapType::const_iterator DMI = VarMap.begin(), DME = VarMap.end();
1176259701Sdim       DMI != DME; ++DMI) {
1177259701Sdim
1178259701Sdim    if (isa<ParmVarDecl>(DMI->first)) {
1179259701Sdim      const ParmVarDecl *Param = cast<ParmVarDecl>(DMI->first);
1180259701Sdim
1181259701Sdim      if (!Param->hasAttr<ReturnTypestateAttr>()) continue;
1182259701Sdim
1183259701Sdim      ExpectedState =
1184259701Sdim        mapReturnTypestateAttrState(Param->getAttr<ReturnTypestateAttr>());
1185259701Sdim
1186259701Sdim      if (DMI->second != ExpectedState) {
1187259701Sdim        WarningsHandler.warnParamReturnTypestateMismatch(BlameLoc,
1188259701Sdim          Param->getNameAsString(), stateToString(ExpectedState),
1189259701Sdim          stateToString(DMI->second));
1190259701Sdim      }
1191259701Sdim    }
1192259701Sdim  }
1193259701Sdim}
1194259701Sdim
1195259701Sdimvoid ConsumedStateMap::clearTemporaries() {
1196259701Sdim  TmpMap.clear();
1197259701Sdim}
1198259701Sdim
1199259701SdimConsumedState ConsumedStateMap::getState(const VarDecl *Var) const {
1200259701Sdim  VarMapType::const_iterator Entry = VarMap.find(Var);
1201259701Sdim
1202259701Sdim  if (Entry != VarMap.end())
1203259701Sdim    return Entry->second;
1204259701Sdim
1205259701Sdim  return CS_None;
1206259701Sdim}
1207259701Sdim
1208259701SdimConsumedState
1209259701SdimConsumedStateMap::getState(const CXXBindTemporaryExpr *Tmp) const {
1210259701Sdim  TmpMapType::const_iterator Entry = TmpMap.find(Tmp);
1211259701Sdim
1212259701Sdim  if (Entry != TmpMap.end())
1213259701Sdim    return Entry->second;
1214259701Sdim
1215259701Sdim  return CS_None;
1216259701Sdim}
1217259701Sdim
1218259701Sdimvoid ConsumedStateMap::intersect(const ConsumedStateMap *Other) {
1219259701Sdim  ConsumedState LocalState;
1220259701Sdim
1221259701Sdim  if (this->From && this->From == Other->From && !Other->Reachable) {
1222259701Sdim    this->markUnreachable();
1223259701Sdim    return;
1224259701Sdim  }
1225259701Sdim
1226259701Sdim  for (VarMapType::const_iterator DMI = Other->VarMap.begin(),
1227259701Sdim       DME = Other->VarMap.end(); DMI != DME; ++DMI) {
1228259701Sdim
1229259701Sdim    LocalState = this->getState(DMI->first);
1230259701Sdim
1231259701Sdim    if (LocalState == CS_None)
1232259701Sdim      continue;
1233259701Sdim
1234259701Sdim    if (LocalState != DMI->second)
1235259701Sdim       VarMap[DMI->first] = CS_Unknown;
1236259701Sdim  }
1237259701Sdim}
1238259701Sdim
1239259701Sdimvoid ConsumedStateMap::intersectAtLoopHead(const CFGBlock *LoopHead,
1240259701Sdim  const CFGBlock *LoopBack, const ConsumedStateMap *LoopBackStates,
1241259701Sdim  ConsumedWarningsHandlerBase &WarningsHandler) {
1242259701Sdim
1243259701Sdim  ConsumedState LocalState;
1244259701Sdim  SourceLocation BlameLoc = getLastStmtLoc(LoopBack);
1245259701Sdim
1246259701Sdim  for (VarMapType::const_iterator DMI = LoopBackStates->VarMap.begin(),
1247259701Sdim       DME = LoopBackStates->VarMap.end(); DMI != DME; ++DMI) {
1248259701Sdim
1249259701Sdim    LocalState = this->getState(DMI->first);
1250259701Sdim
1251259701Sdim    if (LocalState == CS_None)
1252259701Sdim      continue;
1253259701Sdim
1254259701Sdim    if (LocalState != DMI->second) {
1255259701Sdim      VarMap[DMI->first] = CS_Unknown;
1256259701Sdim      WarningsHandler.warnLoopStateMismatch(
1257259701Sdim        BlameLoc, DMI->first->getNameAsString());
1258259701Sdim    }
1259259701Sdim  }
1260259701Sdim}
1261259701Sdim
1262259701Sdimvoid ConsumedStateMap::markUnreachable() {
1263259701Sdim  this->Reachable = false;
1264259701Sdim  VarMap.clear();
1265259701Sdim  TmpMap.clear();
1266259701Sdim}
1267259701Sdim
1268259701Sdimvoid ConsumedStateMap::setState(const VarDecl *Var, ConsumedState State) {
1269259701Sdim  VarMap[Var] = State;
1270259701Sdim}
1271259701Sdim
1272259701Sdimvoid ConsumedStateMap::setState(const CXXBindTemporaryExpr *Tmp,
1273259701Sdim                                ConsumedState State) {
1274259701Sdim  TmpMap[Tmp] = State;
1275259701Sdim}
1276259701Sdim
1277259701Sdimvoid ConsumedStateMap::remove(const VarDecl *Var) {
1278259701Sdim  VarMap.erase(Var);
1279259701Sdim}
1280259701Sdim
1281259701Sdimbool ConsumedStateMap::operator!=(const ConsumedStateMap *Other) const {
1282259701Sdim  for (VarMapType::const_iterator DMI = Other->VarMap.begin(),
1283259701Sdim       DME = Other->VarMap.end(); DMI != DME; ++DMI) {
1284259701Sdim
1285259701Sdim    if (this->getState(DMI->first) != DMI->second)
1286259701Sdim      return true;
1287259701Sdim  }
1288259701Sdim
1289259701Sdim  return false;
1290259701Sdim}
1291259701Sdim
1292259701Sdimvoid ConsumedAnalyzer::determineExpectedReturnState(AnalysisDeclContext &AC,
1293259701Sdim                                                    const FunctionDecl *D) {
1294259701Sdim  QualType ReturnType;
1295259701Sdim  if (const CXXConstructorDecl *Constructor = dyn_cast<CXXConstructorDecl>(D)) {
1296259701Sdim    ASTContext &CurrContext = AC.getASTContext();
1297259701Sdim    ReturnType = Constructor->getThisType(CurrContext)->getPointeeType();
1298259701Sdim  } else
1299259701Sdim    ReturnType = D->getCallResultType();
1300259701Sdim
1301259701Sdim  if (D->hasAttr<ReturnTypestateAttr>()) {
1302259701Sdim    const ReturnTypestateAttr *RTSAttr = D->getAttr<ReturnTypestateAttr>();
1303259701Sdim
1304259701Sdim    const CXXRecordDecl *RD = ReturnType->getAsCXXRecordDecl();
1305259701Sdim    if (!RD || !RD->hasAttr<ConsumableAttr>()) {
1306259701Sdim      // FIXME: This should be removed when template instantiation propagates
1307259701Sdim      //        attributes at template specialization definition, not
1308259701Sdim      //        declaration. When it is removed the test needs to be enabled
1309259701Sdim      //        in SemaDeclAttr.cpp.
1310259701Sdim      WarningsHandler.warnReturnTypestateForUnconsumableType(
1311259701Sdim          RTSAttr->getLocation(), ReturnType.getAsString());
1312259701Sdim      ExpectedReturnState = CS_None;
1313259701Sdim    } else
1314259701Sdim      ExpectedReturnState = mapReturnTypestateAttrState(RTSAttr);
1315259701Sdim  } else if (isConsumableType(ReturnType))
1316259701Sdim    ExpectedReturnState = mapConsumableAttrState(ReturnType);
1317259701Sdim  else
1318259701Sdim    ExpectedReturnState = CS_None;
1319259701Sdim}
1320259701Sdim
1321259701Sdimbool ConsumedAnalyzer::splitState(const CFGBlock *CurrBlock,
1322259701Sdim                                  const ConsumedStmtVisitor &Visitor) {
1323259701Sdim
1324259701Sdim  OwningPtr<ConsumedStateMap> FalseStates(new ConsumedStateMap(*CurrStates));
1325259701Sdim  PropagationInfo PInfo;
1326259701Sdim
1327259701Sdim  if (const IfStmt *IfNode =
1328259701Sdim    dyn_cast_or_null<IfStmt>(CurrBlock->getTerminator().getStmt())) {
1329259701Sdim
1330259701Sdim    const Stmt *Cond = IfNode->getCond();
1331259701Sdim
1332259701Sdim    PInfo = Visitor.getInfo(Cond);
1333259701Sdim    if (!PInfo.isValid() && isa<BinaryOperator>(Cond))
1334259701Sdim      PInfo = Visitor.getInfo(cast<BinaryOperator>(Cond)->getRHS());
1335259701Sdim
1336259701Sdim    if (PInfo.isVarTest()) {
1337259701Sdim      CurrStates->setSource(Cond);
1338259701Sdim      FalseStates->setSource(Cond);
1339259701Sdim      splitVarStateForIf(IfNode, PInfo.getVarTest(), CurrStates,
1340259701Sdim                         FalseStates.get());
1341259701Sdim
1342259701Sdim    } else if (PInfo.isBinTest()) {
1343259701Sdim      CurrStates->setSource(PInfo.testSourceNode());
1344259701Sdim      FalseStates->setSource(PInfo.testSourceNode());
1345259701Sdim      splitVarStateForIfBinOp(PInfo, CurrStates, FalseStates.get());
1346259701Sdim
1347259701Sdim    } else {
1348259701Sdim      return false;
1349259701Sdim    }
1350259701Sdim
1351259701Sdim  } else if (const BinaryOperator *BinOp =
1352259701Sdim    dyn_cast_or_null<BinaryOperator>(CurrBlock->getTerminator().getStmt())) {
1353259701Sdim
1354259701Sdim    PInfo = Visitor.getInfo(BinOp->getLHS());
1355259701Sdim    if (!PInfo.isVarTest()) {
1356259701Sdim      if ((BinOp = dyn_cast_or_null<BinaryOperator>(BinOp->getLHS()))) {
1357259701Sdim        PInfo = Visitor.getInfo(BinOp->getRHS());
1358259701Sdim
1359259701Sdim        if (!PInfo.isVarTest())
1360259701Sdim          return false;
1361259701Sdim
1362259701Sdim      } else {
1363259701Sdim        return false;
1364259701Sdim      }
1365259701Sdim    }
1366259701Sdim
1367259701Sdim    CurrStates->setSource(BinOp);
1368259701Sdim    FalseStates->setSource(BinOp);
1369259701Sdim
1370259701Sdim    const VarTestResult &Test = PInfo.getVarTest();
1371259701Sdim    ConsumedState VarState = CurrStates->getState(Test.Var);
1372259701Sdim
1373259701Sdim    if (BinOp->getOpcode() == BO_LAnd) {
1374259701Sdim      if (VarState == CS_Unknown)
1375259701Sdim        CurrStates->setState(Test.Var, Test.TestsFor);
1376259701Sdim      else if (VarState == invertConsumedUnconsumed(Test.TestsFor))
1377259701Sdim        CurrStates->markUnreachable();
1378259701Sdim
1379259701Sdim    } else if (BinOp->getOpcode() == BO_LOr) {
1380259701Sdim      if (VarState == CS_Unknown)
1381259701Sdim        FalseStates->setState(Test.Var,
1382259701Sdim                              invertConsumedUnconsumed(Test.TestsFor));
1383259701Sdim      else if (VarState == Test.TestsFor)
1384259701Sdim        FalseStates->markUnreachable();
1385259701Sdim    }
1386259701Sdim
1387259701Sdim  } else {
1388259701Sdim    return false;
1389259701Sdim  }
1390259701Sdim
1391259701Sdim  CFGBlock::const_succ_iterator SI = CurrBlock->succ_begin();
1392259701Sdim
1393259701Sdim  if (*SI)
1394259701Sdim    BlockInfo.addInfo(*SI, CurrStates);
1395259701Sdim  else
1396259701Sdim    delete CurrStates;
1397259701Sdim
1398259701Sdim  if (*++SI)
1399259701Sdim    BlockInfo.addInfo(*SI, FalseStates.take());
1400259701Sdim
1401259701Sdim  CurrStates = NULL;
1402259701Sdim  return true;
1403259701Sdim}
1404259701Sdim
1405259701Sdimvoid ConsumedAnalyzer::run(AnalysisDeclContext &AC) {
1406259701Sdim  const FunctionDecl *D = dyn_cast_or_null<FunctionDecl>(AC.getDecl());
1407259701Sdim  if (!D)
1408259701Sdim    return;
1409259701Sdim
1410259701Sdim  CFG *CFGraph = AC.getCFG();
1411259701Sdim  if (!CFGraph)
1412259701Sdim    return;
1413259701Sdim
1414259701Sdim  determineExpectedReturnState(AC, D);
1415259701Sdim
1416259701Sdim  PostOrderCFGView *SortedGraph = AC.getAnalysis<PostOrderCFGView>();
1417259701Sdim  // AC.getCFG()->viewCFG(LangOptions());
1418259701Sdim
1419259701Sdim  BlockInfo = ConsumedBlockInfo(CFGraph->getNumBlockIDs(), SortedGraph);
1420259701Sdim
1421259701Sdim  CurrStates = new ConsumedStateMap();
1422259701Sdim  ConsumedStmtVisitor Visitor(AC, *this, CurrStates);
1423259701Sdim
1424259701Sdim  // Add all trackable parameters to the state map.
1425259701Sdim  for (FunctionDecl::param_const_iterator PI = D->param_begin(),
1426259701Sdim       PE = D->param_end(); PI != PE; ++PI) {
1427259701Sdim    Visitor.VisitParmVarDecl(*PI);
1428259701Sdim  }
1429259701Sdim
1430259701Sdim  // Visit all of the function's basic blocks.
1431259701Sdim  for (PostOrderCFGView::iterator I = SortedGraph->begin(),
1432259701Sdim       E = SortedGraph->end(); I != E; ++I) {
1433259701Sdim
1434259701Sdim    const CFGBlock *CurrBlock = *I;
1435259701Sdim
1436259701Sdim    if (CurrStates == NULL)
1437259701Sdim      CurrStates = BlockInfo.getInfo(CurrBlock);
1438259701Sdim
1439259701Sdim    if (!CurrStates) {
1440259701Sdim      continue;
1441259701Sdim
1442259701Sdim    } else if (!CurrStates->isReachable()) {
1443259701Sdim      delete CurrStates;
1444259701Sdim      CurrStates = NULL;
1445259701Sdim      continue;
1446259701Sdim    }
1447259701Sdim
1448259701Sdim    Visitor.reset(CurrStates);
1449259701Sdim
1450259701Sdim    // Visit all of the basic block's statements.
1451259701Sdim    for (CFGBlock::const_iterator BI = CurrBlock->begin(),
1452259701Sdim         BE = CurrBlock->end(); BI != BE; ++BI) {
1453259701Sdim
1454259701Sdim      switch (BI->getKind()) {
1455259701Sdim      case CFGElement::Statement:
1456259701Sdim        Visitor.Visit(BI->castAs<CFGStmt>().getStmt());
1457259701Sdim        break;
1458259701Sdim
1459259701Sdim      case CFGElement::TemporaryDtor: {
1460259701Sdim        const CFGTemporaryDtor DTor = BI->castAs<CFGTemporaryDtor>();
1461259701Sdim        const CXXBindTemporaryExpr *BTE = DTor.getBindTemporaryExpr();
1462259701Sdim
1463259701Sdim        Visitor.checkCallability(PropagationInfo(BTE),
1464259701Sdim                                 DTor.getDestructorDecl(AC.getASTContext()),
1465259701Sdim                                 BTE->getExprLoc());
1466259701Sdim        break;
1467259701Sdim      }
1468259701Sdim
1469259701Sdim      case CFGElement::AutomaticObjectDtor: {
1470259701Sdim        const CFGAutomaticObjDtor DTor = BI->castAs<CFGAutomaticObjDtor>();
1471259701Sdim        SourceLocation Loc = DTor.getTriggerStmt()->getLocEnd();
1472259701Sdim        const VarDecl *Var = DTor.getVarDecl();
1473259701Sdim
1474259701Sdim        Visitor.checkCallability(PropagationInfo(Var),
1475259701Sdim                                 DTor.getDestructorDecl(AC.getASTContext()),
1476259701Sdim                                 Loc);
1477259701Sdim        break;
1478259701Sdim      }
1479259701Sdim
1480259701Sdim      default:
1481259701Sdim        break;
1482259701Sdim      }
1483259701Sdim    }
1484259701Sdim
1485259701Sdim    CurrStates->clearTemporaries();
1486259701Sdim
1487259701Sdim    // TODO: Handle other forms of branching with precision, including while-
1488259701Sdim    //       and for-loops. (Deferred)
1489259701Sdim    if (!splitState(CurrBlock, Visitor)) {
1490259701Sdim      CurrStates->setSource(NULL);
1491259701Sdim
1492259701Sdim      if (CurrBlock->succ_size() > 1 ||
1493259701Sdim          (CurrBlock->succ_size() == 1 &&
1494259701Sdim           (*CurrBlock->succ_begin())->pred_size() > 1)) {
1495259701Sdim
1496259701Sdim        bool OwnershipTaken = false;
1497259701Sdim
1498259701Sdim        for (CFGBlock::const_succ_iterator SI = CurrBlock->succ_begin(),
1499259701Sdim             SE = CurrBlock->succ_end(); SI != SE; ++SI) {
1500259701Sdim
1501259701Sdim          if (*SI == NULL) continue;
1502259701Sdim
1503259701Sdim          if (BlockInfo.isBackEdge(CurrBlock, *SI)) {
1504259701Sdim            BlockInfo.borrowInfo(*SI)->intersectAtLoopHead(*SI, CurrBlock,
1505259701Sdim                                                           CurrStates,
1506259701Sdim                                                           WarningsHandler);
1507259701Sdim
1508259701Sdim            if (BlockInfo.allBackEdgesVisited(*SI, CurrBlock))
1509259701Sdim              BlockInfo.discardInfo(*SI);
1510259701Sdim          } else {
1511259701Sdim            BlockInfo.addInfo(*SI, CurrStates, OwnershipTaken);
1512259701Sdim          }
1513259701Sdim        }
1514259701Sdim
1515259701Sdim        if (!OwnershipTaken)
1516259701Sdim          delete CurrStates;
1517259701Sdim
1518259701Sdim        CurrStates = NULL;
1519259701Sdim      }
1520259701Sdim    }
1521259701Sdim
1522259701Sdim    if (CurrBlock == &AC.getCFG()->getExit() &&
1523259701Sdim        D->getCallResultType()->isVoidType())
1524259701Sdim      CurrStates->checkParamsForReturnTypestate(D->getLocation(),
1525259701Sdim                                                WarningsHandler);
1526259701Sdim  } // End of block iterator.
1527259701Sdim
1528259701Sdim  // Delete the last existing state map.
1529259701Sdim  delete CurrStates;
1530259701Sdim
1531259701Sdim  WarningsHandler.emitDiagnostics();
1532259701Sdim}
1533259701Sdim}} // end namespace clang::consumed
1534