[PATCH] Mostly correct conditional handling for Consumed analysis

Christian Wailes chriswailes at google.com
Mon Aug 26 14:12:44 PDT 2013


All of the ConsumedStateMap objects are freed either in the second version
of `addInfo` or in the `run` function.  You are correct about the
conditional at the end of `splitState` however.  That was added to handle
null successor blocks after the ConsumedStateMap propagation was
implemented.  I'll fix that in an updated patch.

More comments bellow.

Thanks for the feedback,
Chris

On Mon, Aug 26, 2013 at 1:43 PM, Aaron Ballman <aaron.ballman at gmail.com>wrote:

> On Mon, Aug 26, 2013 at 2:23 PM, Christian Wailes
> <chriswailes at google.com> wrote:
> > If you are referring to the handling of the ConsumedStateMap objects,
> these
> > are guaranteed to be freed.  A map can either be passed to another block
> > (and re-used) via CurrStates, passed to another block using the BlockInfo
> > manager, or merged into an existing block.  The last case is the one
> where
> > we need to free a map.  This occurs in the `run` method and the end of
> > `splitState` via a call to overloaded `addInfo`.  The last existing map
> is
> > always deleted as the second to last statement in `run`.
>
> I must still be missing something.  ConsumedBlockInfo::addInfo either
> takes ownership of a pointer, or creates a new pointer.  Yet it has no
> destructor, so everything in StateMapsArray is being leaked as far as
> I can tell.  What's more, there are cases where you create a new
> object, but it may not be added to the BlockInfo manager (such as not
> an IfStmt or BinOp in splitState, or *++SI evaluates to false, also in
> splitState).
>
> I agree with David that this patch is not gated on fixing these
> issues, but I think a subsequent patch removing the naked pointers
> would be a Very Good Thing.  The ownership strategy here is rather
> hard to follow.  For instance, ConsumedBlockInfo::addInfo will
> sometimes (but not always) delete the argument passed into it.  That
> means the caller has to understand that sometimes the pointer they
> passed in will be invalidated on return.  But that depends on which
> addInfo gets called.  One addInfo deletes the state map if it's
> already set, the other does not.
>
> > There are responses to your comments bellow, and I am submitting a patch
> > with fixes now.
> >
> > - Chris
> >
> >
> > On Sat, Aug 24, 2013 at 8:03 AM, Aaron Ballman <aaron.ballman at gmail.com>
> > wrote:
> >>
> >> Mostly nits about formatting and whitespace, but my biggest concern
> >> has to do with memory leaks involving naked pointers (not really
> >> introduced in this patch).  Also, there is an conversion to bool
> >> operator that kind of scares me (we don't have access to explicit
> >> operator conversions in C++11 yet, otherwise I would simply suggest
> >> marking that as explicit).
> >>
> >> ~Aaron
> >>
> >> > Index: include/clang/Analysis/Analyses/Consumed.h
> >> > ===================================================================
> >> > --- include/clang/Analysis/Analyses/Consumed.h
> >> > +++ include/clang/Analysis/Analyses/Consumed.h
> >> > @@ -24,7 +24,9 @@
> >> >
> >> >  namespace clang {
> >> >  namespace consumed {
> >> > -
> >> > +
> >> > +  class ConsumedStmtVisitor;
> >> > +
> >> >    typedef SmallVector<PartialDiagnosticAt, 1> OptionalNotes;
> >> >    typedef std::pair<PartialDiagnosticAt, OptionalNotes> DelayedDiag;
> >> >    typedef std::list<DelayedDiag> DiagList;
> >> > @@ -38,7 +40,7 @@
> >> >      /// \brief Emit the warnings and notes left by the analysis.
> >> >      virtual void emitDiagnostics() {}
> >> >
> >> > -    /// Warn about unnecessary-test errors.
> >> > +    /// \brief Warn about unnecessary-test errors.
> >> >      /// \param VariableName -- The name of the variable that holds
> the
> >> > unique
> >> >      /// value.
> >> >      ///
> >> > @@ -47,7 +49,7 @@
> >> >                                       StringRef VariableState,
> >> >                                       SourceLocation Loc) {}
> >> >
> >> > -    /// Warn about use-while-consumed errors.
> >> > +    /// \brief Warn about use-while-consumed errors.
> >> >      /// \param MethodName -- The name of the method that was
> >> > incorrectly
> >> >      /// invoked.
> >> >      ///
> >> > @@ -55,7 +57,7 @@
> >> >      virtual void warnUseOfTempWhileConsumed(StringRef MethodName,
> >> >                                              SourceLocation Loc) {}
> >> >
> >> > -    /// Warn about use-in-unknown-state errors.
> >> > +    /// \brief Warn about use-in-unknown-state errors.
> >> >      /// \param MethodName -- The name of the method that was
> >> > incorrectly
> >> >      /// invoked.
> >> >      ///
> >> > @@ -63,7 +65,7 @@
> >> >      virtual void warnUseOfTempInUnknownState(StringRef MethodName,
> >> >                                               SourceLocation Loc) {}
> >> >
> >> > -    /// Warn about use-while-consumed errors.
> >> > +    /// \brief Warn about use-while-consumed errors.
> >> >      /// \param MethodName -- The name of the method that was
> >> > incorrectly
> >> >      /// invoked.
> >> >      ///
> >> > @@ -75,7 +77,7 @@
> >> >                                        StringRef VariableName,
> >> >                                        SourceLocation Loc) {}
> >> >
> >> > -    /// Warn about use-in-unknown-state errors.
> >> > +    /// \brief Warn about use-in-unknown-state errors.
> >> >      /// \param MethodName -- The name of the method that was
> >> > incorrectly
> >> >      /// invoked.
> >> >      ///
> >> > @@ -90,7 +92,7 @@
> >> >
> >> >    enum ConsumedState {
> >> >      // No state information for the given variable.
> >> > -    CS_None,
> >> > +    CS_None = 0,
> >>
> >> Any particular reason for this change?
> >
> >
> > So that CS_None evaluates to false if a ConsumedState value is used in a
> > conditional.
>
> It would still evaluate to false; it's value when unspecified will be zero.
>
>
Sure, but wouldn't that change if someone added a new enum value in the
wrong place?


> >
> >>
> >>
> >> >
> >> >      CS_Unknown,
> >> >      CS_Unconsumed,
> >> > @@ -104,18 +106,35 @@
> >> >
> >> >    protected:
> >> >
> >> > +    bool Reachable;
> >> > +    const Stmt *From;
> >> >      MapType Map;
> >> >
> >> >    public:
> >> > +    ConsumedStateMap() : Reachable(true), From(NULL) {}
> >> > +    ConsumedStateMap(const ConsumedStateMap &Other)
> >> > +      : Reachable(Other.Reachable), From(Other.From), Map(Other.Map)
> {}
> >> > +
> >> >      /// \brief Get the consumed state of a given variable.
> >> >      ConsumedState getState(const VarDecl *Var);
> >> >
> >> >      /// \brief Merge this state map with another map.
> >> >      void intersect(const ConsumedStateMap *Other);
> >> >
> >> > +    /// \brief Return true if this block is reachable.
> >> > +    bool isReachable();
> >>
> >> This method should be const and can be inlined.
> >>
> >> > +
> >> >      /// \brief Mark all variables as unknown.
> >> >      void makeUnknown();
> >> >
> >> > +    /// \brief Mark the block as unreachable.
> >> > +    void markUnreachable();
> >> > +
> >> > +    /// \brief Set the source for a decision about the branching of
> >> > states.
> >> > +    /// \param Source -- The statement that was the origin of a
> >> > branching
> >> > +    /// decision.
> >> > +    void setSource(const Stmt *Source);
> >>
> >> This one can be inlined as well.
> >>
> >> > +
> >> >      /// \brief Set the consumed state of a given variable.
> >> >      void setState(const VarDecl *Var, ConsumedState State);
> >> >
> >> > @@ -144,18 +163,6 @@
> >> >
> >> >      void markVisited(const CFGBlock *Block);
> >> >    };
> >> > -
> >> > -  struct VarTestResult {
> >> > -    const VarDecl *Var;
> >> > -    SourceLocation Loc;
> >> > -    bool UnconsumedInTrueBranch;
> >> > -
> >> > -    VarTestResult() : Var(NULL), Loc(), UnconsumedInTrueBranch(true)
> {}
> >> > -
> >> > -    VarTestResult(const VarDecl *Var, SourceLocation Loc,
> >> > -                  bool UnconsumedInTrueBranch)
> >> > -      : Var(Var), Loc(Loc),
> >> > UnconsumedInTrueBranch(UnconsumedInTrueBranch) {}
> >> > -  };
> >> >
> >> >    /// A class that handles the analysis of uniqueness violations.
> >> >    class ConsumedAnalyzer {
> >> > @@ -169,7 +176,8 @@
> >> >      CacheMapType ConsumableTypeCache;
> >> >
> >> >      bool hasConsumableAttributes(const CXXRecordDecl *RD);
> >> > -    void splitState(const CFGBlock *CurrBlock, const IfStmt
> >> > *Terminator);
> >> > +    bool splitState(const CFGBlock *CurrBlock,
> >> > +                    const ConsumedStmtVisitor &Visitor);
> >>
> >> Formatting for the Visitor.
> >
> >
> > Again, this isn't showing up in my patch or my editor.  Are you viewing
> the
> > patch with non-fixed-width fonts?
>
> Yes, this is my mail client being tricky.  Sorry for that.
>
> >
> >>
> >>
> >> >
> >> >    public:
> >> >
> >> > Index: lib/Analysis/Consumed.cpp
> >> > ===================================================================
> >> > --- lib/Analysis/Consumed.cpp
> >> > +++ lib/Analysis/Consumed.cpp
> >> > @@ -30,8 +30,13 @@
> >> >  #include "llvm/ADT/SmallVector.h"
> >> >  #include "llvm/Support/raw_ostream.h"
> >> >
> >> > +// TODO: Correctly identify unreachable blocks when chaining boolean
> >> > operators.
> >> > +// TODO: Warn about unreachable code.
> >> > +// TODO: Switch to using a bitmap to track unreachable blocks.
> >> >  // TODO: Mark variables as Unknown going into while- or for-loops
> only
> >> > if they
> >> >  //       are referenced inside that block. (Deferred)
> >> > +// TODO: Handle variable definitions, e.g. bool valid = x.isValid();
> >> > +//       if (valid) ...; (Deferred)
> >> >  // TODO: Add a method(s) to identify which method calls perform what
> >> > state
> >> >  //       transitions. (Deferred)
> >> >  // TODO: Take notes on state transitions to provide better warning
> >> > messages.
> >> > @@ -47,6 +52,29 @@
> >> >  // Key method definition
> >> >  ConsumedWarningsHandlerBase::~ConsumedWarningsHandlerBase() {}
> >> >
> >> > +static ConsumedState invertConsumedUnconsumed(ConsumedState State) {
> >> > +  switch (State) {
> >> > +  case CS_Unconsumed:
> >> > +    return CS_Consumed;
> >> > +  case CS_Consumed:
> >> > +    return CS_Unconsumed;
> >> > +  case CS_None:
> >> > +  case CS_Unknown:
> >> > +    llvm_unreachable("invalid enum");
> >> > +  }
> >> > +}
> >> > +
> >> > +static bool isKnownState(ConsumedState State) {
> >> > +  switch (State) {
> >> > +  case CS_Unconsumed:
> >> > +  case CS_Consumed:
> >> > +    return true;
> >> > +  case CS_None:
> >> > +  case CS_Unknown:
> >> > +    return false;
> >> > +  }
> >> > +}
> >> > +
> >> >  static bool isTestingFunction(const FunctionDecl *FunDecl) {
> >> >    return FunDecl->hasAttr<TestsUnconsumedAttr>();
> >> >  }
> >> > @@ -69,40 +97,143 @@
> >> >  }
> >> >
> >> >  namespace {
> >> > -class ConsumedStmtVisitor : public
> >> > ConstStmtVisitor<ConsumedStmtVisitor> {
> >> > +struct VarTestResult {
> >> > +  const VarDecl *Var;
> >> > +  ConsumedState TestsFor;
> >> > +};
> >> > +} // end anonymous::VarTestResult
> >> > +
> >> > +namespace clang {
> >> > +namespace consumed {
> >> > +
> >> > +enum EffectiveOp {
> >> > +  EO_And,
> >> > +  EO_Or
> >> > +};
> >> > +
> >> > +class PropagationInfo {
> >> > +  enum {
> >> > +    IT_None,
> >> > +    IT_State,
> >> > +    IT_Test,
> >> > +    IT_BinTest,
> >> > +    IT_Var
> >> > +  } InfoType;
> >> >
> >> > -  union PropagationUnion {
> >> > +  union {
> >> >      ConsumedState State;
> >> > +    VarTestResult Test;
> >> >      const VarDecl *Var;
> >> > +    struct {
> >> > +      const BinaryOperator *Source;
> >> > +      EffectiveOp EOp;
> >> > +      VarTestResult LTest;
> >> > +      VarTestResult RTest;
> >> > +    } BinTest;
> >> >    };
> >> >
> >> > -  class PropagationInfo {
> >> > -    PropagationUnion StateOrVar;
> >> > +public:
> >> > +  PropagationInfo() : InfoType(IT_None) {}
> >> >
> >> > -  public:
> >> > -    bool IsVar;
> >> > +  PropagationInfo(const VarTestResult &Test) : InfoType(IT_Test),
> >> > Test(Test) {}
> >> > +  PropagationInfo(const VarDecl *Var, ConsumedState TestsFor)
> >> > +    : InfoType(IT_Test) {
> >> >
> >> > -    PropagationInfo() : IsVar(false) {
> >> > -      StateOrVar.State = consumed::CS_None;
> >> > -    }
> >> > +    Test.Var      = Var;
> >>
> >> Whitespace
> >>
> >> > +    Test.TestsFor = TestsFor;
> >> > +  }
> >> > +
> >> > +  PropagationInfo(const BinaryOperator *Source, EffectiveOp EOp,
> >> > +                  const VarTestResult &LTest, const VarTestResult
> >> > &RTest)
> >> > +    : InfoType(IT_BinTest) {
> >> >
> >> > -    PropagationInfo(ConsumedState State) : IsVar(false) {
> >> > -      StateOrVar.State = State;
> >> > -    }
> >> > +    BinTest.Source  = Source;
> >> > +    BinTest.EOp     = EOp;
> >> > +    BinTest.LTest   = LTest;
> >> > +    BinTest.RTest   = RTest;
> >> > +  }
> >> > +
> >> > +  PropagationInfo(const BinaryOperator *Source, EffectiveOp EOp,
> >> > +                  const VarDecl *LVar, ConsumedState LTestsFor,
> >> > +                  const VarDecl *RVar, ConsumedState RTestsFor)
> >>
> >> Code formatting
> >
> >
> > Same comment as above concerning fonts.
> >
> >>
> >>
> >> > +    : InfoType(IT_BinTest) {
> >> >
> >> > -    PropagationInfo(const VarDecl *Var) : IsVar(true) {
> >> > -      StateOrVar.Var = Var;
> >> > -    }
> >> > +    BinTest.Source         = Source;
> >> > +    BinTest.EOp            = EOp;
> >> > +    BinTest.LTest.Var      = LVar;
> >> > +    BinTest.LTest.TestsFor = LTestsFor;
> >> > +    BinTest.RTest.Var      = RVar;
> >> > +    BinTest.RTest.TestsFor = RTestsFor;
> >>
> >> Hopefully this looks better in the editor than it does in my mail
> >> client.  ;-)  I don't think there's a rule against it, but I don't
> >> think many places in the code base attempt to line up assignments.
> >> The only place I've ever really seen this is with case statement
> >> bodies, and even there it's not consistent.
> >>
> >> > +  }
> >> > +
> >> > +  PropagationInfo(ConsumedState State) : InfoType(IT_State),
> >> > State(State) {}
> >> > +  PropagationInfo(const VarDecl *Var) : InfoType(IT_Var), Var(Var) {}
> >> > +
> >> > +  const ConsumedState & getState() const {
> >> > +    assert(InfoType == IT_State);
> >> > +    return State;
> >> > +  }
> >> > +
> >> > +  const VarTestResult & getTest() const {
> >> > +    assert(InfoType == IT_Test);
> >> > +    return Test;
> >> > +  }
> >> > +
> >> > +  const VarTestResult & getLTest() const {
> >> > +    assert(InfoType == IT_BinTest);
> >> > +    return BinTest.LTest;
> >> > +  }
> >> > +
> >> > +  const VarTestResult & getRTest() const {
> >> > +    assert(InfoType == IT_BinTest);
> >> > +    return BinTest.RTest;
> >> > +  }
> >> > +
> >> > +  const VarDecl * getVar() const {
> >> > +    assert(InfoType == IT_Var);
> >> > +    return Var;
> >> > +  }
> >> > +
> >> > +  EffectiveOp testEffectiveOp() const {
> >> > +    assert(InfoType == IT_BinTest);
> >> > +    return BinTest.EOp;
> >> > +  }
> >> > +
> >> > +  const BinaryOperator * testSourceNode() const {
> >> > +    assert(InfoType == IT_BinTest);
> >> > +    return BinTest.Source;
> >> > +  }
> >> > +
> >> > +  operator bool()  const { return InfoType != IT_None;     }
> >>
> >> Implicit conversions to bool are kind of terrifying.  This makes the
> >> class now assignable as an integer, which can lead to obscure bugs.
> >> I'd prefer to avoid this.
> >>
> >
> > I think "is there information or not" is a pretty obvious test, so I
> would
> > like to go with David's suggestion and use LLVM_EXPLICIT if there aren't
> any
> > objections.
>
> My gut feeling is that there's basically no harm in naming the
> operation, especially given that there's only one usage of it in the
> entire source file.
>
>
I personally find the Boolean conversion operator clearer and easier to use
when programming than a named function, and from what I understand the
LLVM_EXPLICIT macro fixes the issue with PropagationInfo objects being
assigned to integers.  I'm also not sure what name would be clearer;
`isValid()` doesn't convey any more information in my mind.  That being
said, I can change it if people want.


> >
> >>
> >> > +  bool isState()   const { return InfoType == IT_State;    }
> >> > +  bool isTest()    const { return InfoType == IT_Test;     }
> >> > +  bool isBinTest() const { return InfoType == IT_BinTest;  }
> >> > +  bool isVar()     const { return InfoType == IT_Var;      }
> >>
> >> Whitespace between the end of the parameter list and the const.
> >>
> >> > +
> >> > +  PropagationInfo invertTest() const {
> >> > +    assert(InfoType == IT_Test || InfoType == IT_BinTest);
> >> >
> >> > -    ConsumedState getState() const { return StateOrVar.State; }
> >> > +    if (InfoType == IT_Test) {
> >> > +      return PropagationInfo(Test.Var,
> >> > invertConsumedUnconsumed(Test.TestsFor));
> >> >
> >> > -    const VarDecl * getVar() const { return IsVar ? StateOrVar.Var :
> >> > NULL; }
> >> > -  };
> >> > +    } else if (InfoType == IT_BinTest) {
> >> > +      return PropagationInfo(BinTest.Source,
> >> > +        BinTest.EOp == EO_And ? EO_Or : EO_And,
> >> > +        BinTest.LTest.Var,
> >> > invertConsumedUnconsumed(BinTest.LTest.TestsFor),
> >> > +        BinTest.RTest.Var,
> >> > invertConsumedUnconsumed(BinTest.RTest.TestsFor));
> >> > +    } else {
> >> > +      return PropagationInfo();
> >> > +    }
> >> > +  }
> >> > +};
> >> > +
> >> > +class ConsumedStmtVisitor : public
> >> > ConstStmtVisitor<ConsumedStmtVisitor> {
> >> >
> >> >    typedef llvm::DenseMap<const Stmt *, PropagationInfo> MapType;
> >> >    typedef std::pair<const Stmt *, PropagationInfo> PairType;
> >> >    typedef MapType::iterator InfoEntry;
> >> > -
> >> > +  typedef MapType::const_iterator ConstInfoEntry;
> >> > +
> >> >    AnalysisDeclContext &AC;
> >> >    ConsumedAnalyzer &Analyzer;
> >> >    ConsumedStateMap *StateMap;
> >> > @@ -112,10 +243,11 @@
> >> >                          const FunctionDecl *FunDecl,
> >> >                          const CallExpr *Call);
> >> >    void forwardInfo(const Stmt *From, const Stmt *To);
> >> > +  void handleTestingFunctionCall(const CallExpr *Call, const VarDecl
> >> > *Var);
> >> >    bool isLikeMoveAssignment(const CXXMethodDecl *MethodDecl);
> >> >
> >> >  public:
> >> > -
> >> > +
> >> >    void Visit(const Stmt *StmtNode);
> >> >
> >> >    void VisitBinaryOperator(const BinaryOperator *BinOp);
> >> > @@ -131,12 +263,20 @@
> >> >    void VisitUnaryOperator(const UnaryOperator *UOp);
> >> >    void VisitVarDecl(const VarDecl *Var);
> >> >
> >> > -  ConsumedStmtVisitor(AnalysisDeclContext &AC, ConsumedAnalyzer
> >> > &Analyzer,
> >> > -                      ConsumedStateMap *StateMap)
> >> > -      : AC(AC), Analyzer(Analyzer), StateMap(StateMap) {}
> >> > -
> >> > -  void reset() {
> >> > -    PropagationMap.clear();
> >> > +  ConsumedStmtVisitor(AnalysisDeclContext &AC, ConsumedAnalyzer
> >> > &Analyzer)
> >> > +      : AC(AC), Analyzer(Analyzer), StateMap(NULL) {}
> >> > +
> >> > +  PropagationInfo getInfo(const Stmt *StmtNode) const {
> >> > +    ConstInfoEntry Entry = PropagationMap.find(StmtNode);
> >> > +
> >> > +    if (Entry != PropagationMap.end())
> >> > +      return Entry->second;
> >> > +    else
> >> > +      return PropagationInfo();
> >> > +  }
> >> > +
> >> > +  void reset(ConsumedStateMap *NewStateMap) {
> >> > +    StateMap = NewStateMap;
> >> >    }
> >> >  };
> >> >
> >> > @@ -148,7 +288,7 @@
> >> >
> >> >    if (!FunDecl->hasAttr<CallableWhenUnconsumedAttr>()) return;
> >> >
> >> > -  if (PInfo.IsVar) {
> >> > +  if (PInfo.isVar()) {
> >> >      const VarDecl *Var = PInfo.getVar();
> >> >
> >> >      switch (StateMap->getState(Var)) {
> >> > @@ -191,9 +331,24 @@
> >> >  void ConsumedStmtVisitor::forwardInfo(const Stmt *From, const Stmt
> *To)
> >> > {
> >> >    InfoEntry Entry = PropagationMap.find(From);
> >> >
> >> > -  if (Entry != PropagationMap.end()) {
> >> > -    PropagationMap.insert(PairType(To,
> >> > PropagationInfo(Entry->second)));
> >> > +  if (Entry != PropagationMap.end())
> >> > +    PropagationMap.insert(PairType(To, Entry->second));
> >> > +}
> >> > +
> >> > +void ConsumedStmtVisitor::handleTestingFunctionCall(const CallExpr
> >> > *Call,
> >> > +                                                    const VarDecl
> >> > *Var) {
> >> > +
> >> > +  ConsumedState VarState = StateMap->getState(Var);
> >> > +
> >> > +  if (VarState != CS_Unknown) {
> >> > +    SourceLocation CallLoc = Call->getExprLoc();
> >> > +
> >> > +    if (!CallLoc.isMacroID())
> >> > +
> >> > Analyzer.WarningsHandler.warnUnnecessaryTest(Var->getNameAsString(),
> >> > +        stateToString(VarState), CallLoc);
> >> >    }
> >> > +
> >> > +  PropagationMap.insert(PairType(Call, PropagationInfo(Var,
> >> > CS_Unconsumed)));
> >> >  }
> >> >
> >> >  bool ConsumedStmtVisitor::isLikeMoveAssignment(
> >> > @@ -205,8 +360,43 @@
> >> >
> >> > MethodDecl->getParamDecl(0)->getType()->isRValueReferenceType());
> >> >  }
> >> >
> >> > +void ConsumedStmtVisitor::Visit(const Stmt *StmtNode) {
> >> > +
> >> > +  ConstStmtVisitor<ConsumedStmtVisitor>::Visit(StmtNode);
> >> > +
> >> > +  for (Stmt::const_child_iterator CI = StmtNode->child_begin(),
> >> > +       CE = StmtNode->child_end(); CI != CE; ++CI) {
> >> > +
> >> > +    PropagationMap.erase(*CI);
> >> > +  }
> >> > +}
> >> > +
> >> >  void ConsumedStmtVisitor::VisitBinaryOperator(const BinaryOperator
> >> > *BinOp) {
> >> >    switch (BinOp->getOpcode()) {
> >> > +  case BO_LAnd:
> >> > +  case BO_LOr : {
> >> > +    InfoEntry LEntry = PropagationMap.find(BinOp->getLHS()),
> >> > +              REntry = PropagationMap.find(BinOp->getRHS());
> >> > +
> >> > +    VarTestResult LTest, RTest;
> >> > +
> >> > +    if (LEntry != PropagationMap.end() && LEntry->second.isTest())
> >> > +      LTest = LEntry->second.getTest();
> >> > +    else
> >> > +      LTest.Var = NULL;
> >> > +
> >> > +    if (REntry != PropagationMap.end() && REntry->second.isTest())
> >> > +      RTest = REntry->second.getTest();
> >> > +    else
> >> > +      RTest.Var = NULL;
> >> > +
> >> > +    if (!(LTest.Var == NULL && RTest.Var == NULL))
> >> > +      PropagationMap.insert(PairType(BinOp, PropagationInfo(BinOp,
> >> > +        static_cast<EffectiveOp>(BinOp->getOpcode() == BO_LOr),
> LTest,
> >> > RTest)));
> >> > +
> >> > +    break;
> >> > +  }
> >> > +
> >> >    case BO_PtrMemD:
> >> >    case BO_PtrMemI:
> >> >      forwardInfo(BinOp->getLHS(), BinOp);
> >> > @@ -217,16 +407,6 @@
> >> >    }
> >> >  }
> >> >
> >> > -void ConsumedStmtVisitor::Visit(const Stmt *StmtNode) {
> >> > -  ConstStmtVisitor<ConsumedStmtVisitor>::Visit(StmtNode);
> >> > -
> >> > -  for (Stmt::const_child_iterator CI = StmtNode->child_begin(),
> >> > -       CE = StmtNode->child_end(); CI != CE; ++CI) {
> >> > -
> >> > -    PropagationMap.erase(*CI);
> >> > -  }
> >> > -}
> >> > -
> >> >  void ConsumedStmtVisitor::VisitCallExpr(const CallExpr *Call) {
> >> >    if (const FunctionDecl *FunDecl =
> >> >      dyn_cast_or_null<FunctionDecl>(Call->getDirectCallee())) {
> >> > @@ -250,7 +430,7 @@
> >> >
> >> >        InfoEntry Entry = PropagationMap.find(Call->getArg(Index));
> >> >
> >> > -      if (Entry == PropagationMap.end() || !Entry->second.IsVar) {
> >> > +      if (Entry == PropagationMap.end() || !Entry->second.isVar()) {
> >> >          continue;
> >> >        }
> >> >
> >> > @@ -274,10 +454,7 @@
> >> >  }
> >> >
> >> >  void ConsumedStmtVisitor::VisitCastExpr(const CastExpr *Cast) {
> >> > -  InfoEntry Entry = PropagationMap.find(Cast->getSubExpr());
> >> > -
> >> > -  if (Entry != PropagationMap.end())
> >> > -    PropagationMap.insert(PairType(Cast, Entry->second));
> >> > +  forwardInfo(Cast->getSubExpr(), Cast);
> >> >  }
> >> >
> >> >  void ConsumedStmtVisitor::VisitCXXConstructExpr(const
> CXXConstructExpr
> >> > *Call) {
> >> > @@ -298,7 +475,7 @@
> >> >        PropagationInfo PInfo =
> >> >          PropagationMap.find(Call->getArg(0))->second;
> >> >
> >> > -      if (PInfo.IsVar) {
> >> > +      if (PInfo.isVar()) {
> >> >          const VarDecl* Var = PInfo.getVar();
> >> >
> >> >          PropagationMap.insert(PairType(Call,
> >> > @@ -336,8 +513,10 @@
> >> >
> >> >      checkCallability(PInfo, MethodDecl, Call);
> >> >
> >> > -    if (PInfo.IsVar) {
> >> > -      if (MethodDecl->hasAttr<ConsumesAttr>())
> >> > +    if (PInfo.isVar()) {
> >> > +      if (isTestingFunction(MethodDecl))
> >> > +        handleTestingFunctionCall(Call, PInfo.getVar());
> >> > +      else if (MethodDecl->hasAttr<ConsumesAttr>())
> >> >          StateMap->setState(PInfo.getVar(), consumed::CS_Consumed);
> >> >        else if (!MethodDecl->isConst())
> >> >          StateMap->setState(PInfo.getVar(), consumed::CS_Unknown);
> >> > @@ -367,7 +546,7 @@
> >> >        LPInfo = LEntry->second;
> >> >        RPInfo = REntry->second;
> >> >
> >> > -      if (LPInfo.IsVar && RPInfo.IsVar) {
> >> > +      if (LPInfo.isVar() && RPInfo.isVar()) {
> >> >          StateMap->setState(LPInfo.getVar(),
> >> >            StateMap->getState(RPInfo.getVar()));
> >> >
> >> > @@ -375,12 +554,12 @@
> >> >
> >> >          PropagationMap.insert(PairType(Call, LPInfo));
> >> >
> >> > -      } else if (LPInfo.IsVar && !RPInfo.IsVar) {
> >> > +      } else if (LPInfo.isVar() && !RPInfo.isVar()) {
> >> >          StateMap->setState(LPInfo.getVar(), RPInfo.getState());
> >> >
> >> >          PropagationMap.insert(PairType(Call, LPInfo));
> >> >
> >> > -      } else if (!LPInfo.IsVar && RPInfo.IsVar) {
> >> > +      } else if (!LPInfo.isVar() && RPInfo.isVar()) {
> >> >          PropagationMap.insert(PairType(Call,
> >> >            PropagationInfo(StateMap->getState(RPInfo.getVar()))));
> >> >
> >> > @@ -395,7 +574,7 @@
> >> >
> >> >        LPInfo = LEntry->second;
> >> >
> >> > -      if (LPInfo.IsVar) {
> >> > +      if (LPInfo.isVar()) {
> >> >          StateMap->setState(LPInfo.getVar(), consumed::CS_Unknown);
> >> >
> >> >          PropagationMap.insert(PairType(Call, LPInfo));
> >> > @@ -410,7 +589,7 @@
> >> >
> >> >        RPInfo = REntry->second;
> >> >
> >> > -      if (RPInfo.IsVar) {
> >> > +      if (RPInfo.isVar()) {
> >> >          const VarDecl *Var = RPInfo.getVar();
> >> >
> >> >          PropagationMap.insert(PairType(Call,
> >> > @@ -434,14 +613,16 @@
> >> >
> >> >        checkCallability(PInfo, FunDecl, Call);
> >> >
> >> > -      if (PInfo.IsVar) {
> >> > -        if (FunDecl->hasAttr<ConsumesAttr>()) {
> >> > -          // Handle consuming operators.
> >> > +      if (PInfo.isVar()) {
> >> > +        if (isTestingFunction(FunDecl)) {
> >> > +          handleTestingFunctionCall(Call, PInfo.getVar());
> >> > +
> >> > +        } else if (FunDecl->hasAttr<ConsumesAttr>()) {
> >> >            StateMap->setState(PInfo.getVar(), consumed::CS_Consumed);
> >> > +
> >> >          } else if (const CXXMethodDecl *MethodDecl =
> >> > -          dyn_cast_or_null<CXXMethodDecl>(FunDecl)) {
> >> > +                     dyn_cast_or_null<CXXMethodDecl>(FunDecl)) {
> >> >
> >> > -          // Handle non-constant member operators.
> >> >            if (!MethodDecl->isConst())
> >> >              StateMap->setState(PInfo.getVar(), consumed::CS_Unknown);
> >> >          }
> >> > @@ -482,11 +663,21 @@
> >> >  }
> >> >
> >> >  void ConsumedStmtVisitor::VisitUnaryOperator(const UnaryOperator
> *UOp)
> >> > {
> >> > -  if (UOp->getOpcode() == UO_AddrOf) {
> >> > -    InfoEntry Entry = PropagationMap.find(UOp->getSubExpr());
> >> > -
> >> > -    if (Entry != PropagationMap.end())
> >> > -      PropagationMap.insert(PairType(UOp, Entry->second));
> >> > +  InfoEntry Entry =
> >> > PropagationMap.find(UOp->getSubExpr()->IgnoreParens());
> >> > +  if (Entry == PropagationMap.end()) return;
> >> > +
> >> > +  switch (UOp->getOpcode()) {
> >> > +  case UO_AddrOf:
> >> > +    PropagationMap.insert(PairType(UOp, Entry->second));
> >> > +    break;
> >> > +
> >> > +  case UO_LNot:
> >> > +    if (Entry->second.isTest() || Entry->second.isBinTest())
> >> > +      PropagationMap.insert(PairType(UOp,
> Entry->second.invertTest()));
> >> > +    break;
> >> > +
> >> > +  default:
> >> > +    break;
> >> >    }
> >> >  }
> >> >
> >> > @@ -495,74 +686,97 @@
> >> >      PropagationInfo PInfo =
> >> >        PropagationMap.find(Var->getInit())->second;
> >> >
> >> > -    StateMap->setState(Var, PInfo.IsVar ?
> >> > +    StateMap->setState(Var, PInfo.isVar() ?
> >> >        StateMap->getState(PInfo.getVar()) : PInfo.getState());
> >> >    }
> >> >  }
> >> > -} // end anonymous::ConsumedStmtVisitor
> >> > +}} // end clang::consumed::ConsumedStmtVisitor
> >>
> >> Two curly braces on the same line.
> >
> >
> > I've seen this happen in other places in the Clang codebase.  It seems
> > better than wasting a newline to me.
>
> Newlines are cheap, but it's not a huge issue.
>
> >
> >>
> >>
> >> >
> >> > -namespace {
> >> > +namespace clang {
> >> > +namespace consumed {
> >> >
> >> > -// TODO: Handle variable definitions, e.g. bool valid = x.isValid();
> >> > -//       if (valid) ...; (Deferred)
> >> > -class TestedVarsVisitor : public
> RecursiveASTVisitor<TestedVarsVisitor>
> >> > {
> >> > -
> >> > -  bool Invert;
> >> > -  SourceLocation CurrTestLoc;
> >> > -
> >> > -  ConsumedStateMap *StateMap;
> >> > -
> >> > -public:
> >> > -  bool IsUsefulConditional;
> >> > -  VarTestResult Test;
> >> > +void splitVarStateForIf(const IfStmt * IfNode, const VarTestResult
> >> > &Test,
> >> > +                        ConsumedStateMap *ThenStates,
> >> > +                        ConsumedStateMap *ElseStates) {
> >> > +
> >> > +  ConsumedState VarState = ThenStates->getState(Test.Var);
> >> >
> >> > -  TestedVarsVisitor(ConsumedStateMap *StateMap) : Invert(false),
> >> > -    StateMap(StateMap), IsUsefulConditional(false) {}
> >> > +  if (VarState == CS_Unknown) {
> >> > +    ThenStates->setState(Test.Var, Test.TestsFor);
> >> > +    if (ElseStates)
> >> > +      ElseStates->setState(Test.Var,
> >> > invertConsumedUnconsumed(Test.TestsFor));
> >> >
> >> > -  bool VisitCallExpr(CallExpr *Call);
> >> > -  bool VisitDeclRefExpr(DeclRefExpr *DeclRef);
> >> > -  bool VisitUnaryOperator(UnaryOperator *UnaryOp);
> >> > -};
> >> > -
> >> > -bool TestedVarsVisitor::VisitCallExpr(CallExpr *Call) {
> >> > -  if (const FunctionDecl *FunDecl =
> >> > -    dyn_cast_or_null<FunctionDecl>(Call->getDirectCallee())) {
> >> > -
> >> > -    if (isTestingFunction(FunDecl)) {
> >> > -      CurrTestLoc = Call->getExprLoc();
> >> > -      IsUsefulConditional = true;
> >> > -      return true;
> >> > -    }
> >> > +  } else if (VarState == invertConsumedUnconsumed(Test.TestsFor)) {
> >> > +    ThenStates->markUnreachable();
> >> >
> >> > -    IsUsefulConditional = false;
> >> > +  } else if (VarState == Test.TestsFor && ElseStates) {
> >> > +    ElseStates->markUnreachable();
> >> >    }
> >> > -
> >> > -  return false;
> >> >  }
> >> >
> >> > -bool TestedVarsVisitor::VisitDeclRefExpr(DeclRefExpr *DeclRef) {
> >> > -  if (const VarDecl *Var =
> >> > dyn_cast_or_null<VarDecl>(DeclRef->getDecl()))
> >> > -    if (StateMap->getState(Var) != consumed::CS_None)
> >> > -      Test = VarTestResult(Var, CurrTestLoc, !Invert);
> >> > +void splitVarStateForIfBinOp(const PropagationInfo &PInfo,
> >> > +  ConsumedStateMap *ThenStates, ConsumedStateMap *ElseStates) {
> >> >
> >> > -  return true;
> >> > -}
> >> > -
> >> > -bool TestedVarsVisitor::VisitUnaryOperator(UnaryOperator *UnaryOp) {
> >> > -  if (UnaryOp->getOpcode() == UO_LNot) {
> >> > -    Invert = true;
> >> > -    TraverseStmt(UnaryOp->getSubExpr());
> >> > -
> >> > -  } else {
> >> > -    IsUsefulConditional = false;
> >> > +  const VarTestResult &LTest = PInfo.getLTest(),
> >> > +                      &RTest = PInfo.getRTest();
> >> > +
> >> > +  ConsumedState LState = LTest.Var ? ThenStates->getState(LTest.Var)
> :
> >> > CS_None,
> >> > +                RState = RTest.Var ? ThenStates->getState(RTest.Var)
> :
> >> > CS_None;
> >> > +
> >> > +  if (LTest.Var) {
> >> > +    if (PInfo.testEffectiveOp() == EO_And) {
> >> > +      if (LState == CS_Unknown) {
> >> > +        ThenStates->setState(LTest.Var, LTest.TestsFor);
> >> > +
> >> > +      } else if (LState == invertConsumedUnconsumed(LTest.TestsFor))
> {
> >> > +        ThenStates->markUnreachable();
> >> > +
> >> > +      } else if (LState == LTest.TestsFor && isKnownState(RState)) {
> >> > +        if (RState == RTest.TestsFor) {
> >> > +          if (ElseStates)
> >> > +            ElseStates->markUnreachable();
> >> > +        } else {
> >> > +          ThenStates->markUnreachable();
> >> > +        }
> >> > +      }
> >> > +
> >> > +    } else {
> >> > +      if (LState == CS_Unknown && ElseStates) {
> >> > +        ElseStates->setState(LTest.Var,
> >> > +
> invertConsumedUnconsumed(LTest.TestsFor));
> >> > +
> >> > +      } else if (LState == LTest.TestsFor && ElseStates) {
> >> > +        ElseStates->markUnreachable();
> >> > +
> >> > +      } else if (LState == invertConsumedUnconsumed(LTest.TestsFor)
> &&
> >> > +                 isKnownState(RState)) {
> >> > +
> >> > +        if (RState == RTest.TestsFor) {
> >> > +          if (ElseStates)
> >> > +            ElseStates->markUnreachable();
> >> > +        } else {
> >> > +          ThenStates->markUnreachable();
> >> > +        }
> >> > +      }
> >> > +    }
> >> >    }
> >> >
> >> > -  return false;
> >> > +  if (RTest.Var) {
> >> > +    if (PInfo.testEffectiveOp() == EO_And) {
> >> > +      if (RState == CS_Unknown)
> >> > +        ThenStates->setState(RTest.Var, RTest.TestsFor);
> >> > +      else if (RState == invertConsumedUnconsumed(RTest.TestsFor))
> >> > +        ThenStates->markUnreachable();
> >> > +
> >> > +    } else if (ElseStates) {
> >> > +      if (RState == CS_Unknown)
> >> > +        ElseStates->setState(RTest.Var,
> >> > +
> invertConsumedUnconsumed(RTest.TestsFor));
> >> > +      else if (RState == RTest.TestsFor)
> >> > +        ElseStates->markUnreachable();
> >> > +    }
> >> > +  }
> >> >  }
> >> > -} // end anonymouse::TestedVarsVisitor
> >> > -
> >> > -namespace clang {
> >> > -namespace consumed {
> >> >
> >> >  void ConsumedBlockInfo::addInfo(const CFGBlock *Block,
> >> >                                  ConsumedStateMap *StateMap,
> >> > @@ -625,29 +839,44 @@
> >> >  void ConsumedStateMap::intersect(const ConsumedStateMap *Other) {
> >> >    ConsumedState LocalState;
> >> >
> >> > +  if (this->From && this->From == Other->From && !Other->Reachable) {
> >> > +    this->markUnreachable();
> >> > +    return;
> >> > +  }
> >> > +
> >> >    for (MapType::const_iterator DMI = Other->Map.begin(),
> >> >         DME = Other->Map.end(); DMI != DME; ++DMI) {
> >> >
> >> >      LocalState = this->getState(DMI->first);
> >> >
> >> > -    if (LocalState != CS_None && LocalState != DMI->second)
> >> > -      setState(DMI->first, CS_Unknown);
> >> > +    if (LocalState == CS_None) continue;
> >>
> >> Body should be on a new line.
> >>
> >> > +
> >> > +    if (LocalState != DMI->second)
> >> > +       Map[DMI->first] = CS_Unknown;
> >> >    }
> >> >  }
> >> >
> >> > +bool ConsumedStateMap::isReachable() {
> >> > +  return Reachable;
> >> > +}
> >>
> >> Function should be const and inline.
> >>
> >> > +
> >> > +void ConsumedStateMap::markUnreachable() {
> >> > +  this->Reachable = false;
> >> > +  Map.clear();
> >> > +}
> >> > +
> >> >  void ConsumedStateMap::makeUnknown() {
> >> > -  PairType Pair;
> >> > -
> >> >    for (MapType::const_iterator DMI = Map.begin(), DME = Map.end();
> DMI
> >> > != DME;
> >> >         ++DMI) {
> >> >
> >> > -    Pair = *DMI;
> >> > -
> >> > -    Map.erase(Pair.first);
> >> > -    Map.insert(PairType(Pair.first, CS_Unknown));
> >> > +    Map[DMI->first] = CS_Unknown;
> >> >    }
> >> >  }
> >> >
> >> > +void ConsumedStateMap::setSource(const Stmt *Source) {
> >> > +  this->From = Source;
> >> > +}
> >> > +
> >> >  void ConsumedStateMap::setState(const VarDecl *Var, ConsumedState
> >> > State) {
> >> >    Map[Var] = State;
> >> >  }
> >> > @@ -694,42 +923,84 @@
> >> >    return false;
> >> >  }
> >> >
> >> > -// TODO: Handle other forms of branching with precision, including
> >> > while- and
> >> > -//       for-loops. (Deferred)
> >> > -void ConsumedAnalyzer::splitState(const CFGBlock *CurrBlock,
> >> > -                                  const IfStmt *Terminator) {
> >> > -
> >> > -  TestedVarsVisitor Visitor(CurrStates);
> >> > -  Visitor.TraverseStmt(const_cast<Expr*>(Terminator->getCond()));
> >> > -
> >> > -  bool HasElse = Terminator->getElse() != NULL;
> >> > +bool ConsumedAnalyzer::splitState(const CFGBlock *CurrBlock,
> >> > +                                  const ConsumedStmtVisitor
> &Visitor) {
> >>
> >> Formatting
> >
> >
> > Non-fixed-width font issue.
> >
> >>
> >>
> >> >
> >> > -  ConsumedStateMap *ElseOrMergeStates = new
> >> > ConsumedStateMap(*CurrStates);
> >> > +  ConsumedStateMap *FalseStates = new ConsumedStateMap(*CurrStates);
> >>
> >> I am still concerted about the leaks with naked pointers.
> >>
> >> > +  PropagationInfo   PInfo;
> >> >
> >> > -  if (Visitor.IsUsefulConditional) {
> >> > -    ConsumedState VarState = CurrStates->getState(Visitor.Test.Var);
> >> > +  if (const IfStmt *IfNode =
> >> > +    dyn_cast_or_null<IfStmt>(CurrBlock->getTerminator().getStmt())) {
> >> >
> >> > -    if (VarState != CS_Unknown) {
> >> > -      // FIXME: Make this not warn if the test is from a macro
> >> > expansion.
> >> > -      //        (Deferred)
> >> > -
> >> >
> WarningsHandler.warnUnnecessaryTest(Visitor.Test.Var->getNameAsString(),
> >> > -        stateToString(VarState), Visitor.Test.Loc);
> >> > -    }
> >> > +    bool HasElse = IfNode->getElse() != NULL;
> >> > +    const Stmt *Cond = IfNode->getCond();
> >> > +
> >> > +    PInfo = Visitor.getInfo(Cond);
> >> > +    if (!PInfo && isa<BinaryOperator>(Cond))
> >> > +      PInfo = Visitor.getInfo(cast<BinaryOperator>(Cond)->getRHS());
> >> >
> >> > -    if (Visitor.Test.UnconsumedInTrueBranch) {
> >> > -      CurrStates->setState(Visitor.Test.Var, CS_Unconsumed);
> >> > -      if (HasElse) ElseOrMergeStates->setState(Visitor.Test.Var,
> >> > CS_Consumed);
> >> > +    if (PInfo.isTest()) {
> >> > +      CurrStates->setSource(Cond);
> >> > +      FalseStates->setSource(Cond);
> >> > +      splitVarStateForIf(IfNode, PInfo.getTest(), CurrStates,
> >> > +                         HasElse ? FalseStates : NULL);
> >> > +
> >> > +    } else if (PInfo.isBinTest()) {
> >> > +      CurrStates->setSource(PInfo.testSourceNode());
> >> > +      FalseStates->setSource(PInfo.testSourceNode());
> >> > +      splitVarStateForIfBinOp(PInfo, CurrStates, HasElse ?
> FalseStates
> >> > :NULL);
> >> >
> >> >      } else {
> >> > -      CurrStates->setState(Visitor.Test.Var, CS_Consumed);
> >> > -      if (HasElse) ElseOrMergeStates->setState(Visitor.Test.Var,
> >> > CS_Unconsumed);
> >> > +      return false;
> >> > +    }
> >> > +
> >> > +  } else if (const BinaryOperator *BinOp =
> >> > +
> >> >
> dyn_cast_or_null<BinaryOperator>(CurrBlock->getTerminator().getStmt())) {
> >> > +
> >> > +    PInfo = Visitor.getInfo(BinOp->getLHS());
> >> > +    if (!PInfo.isTest()) {
> >> > +      if ((BinOp =
> dyn_cast_or_null<BinaryOperator>(BinOp->getLHS())))
> >> > {
> >> > +        PInfo = Visitor.getInfo(BinOp->getRHS());
> >> > +
> >> > +        if (!PInfo.isTest())
> >> > +          return false;
> >> > +
> >> > +      } else {
> >> > +        return false;
> >> > +      }
> >> > +    }
> >> > +
> >> > +    CurrStates->setSource(BinOp);
> >> > +    FalseStates->setSource(BinOp);
> >> > +
> >> > +    const VarTestResult &Test = PInfo.getTest();
> >> > +    ConsumedState VarState = CurrStates->getState(Test.Var);
> >> > +
> >> > +    if (BinOp->getOpcode() == BO_LAnd) {
> >> > +      if (VarState == CS_Unknown)
> >> > +        CurrStates->setState(Test.Var, Test.TestsFor);
> >> > +      else if (VarState == invertConsumedUnconsumed(Test.TestsFor))
> >> > +        CurrStates->markUnreachable();
> >> > +
> >> > +    } else if (BinOp->getOpcode() == BO_LOr) {
> >> > +      if (VarState == CS_Unknown)
> >> > +        FalseStates->setState(Test.Var,
> >> > +
>  invertConsumedUnconsumed(Test.TestsFor));
> >> > +      else if (VarState == Test.TestsFor)
> >> > +        FalseStates->markUnreachable();
> >> >      }
> >> > -  }
> >> >
> >> > +  } else {
> >> > +    return false;
> >> > +  }
> >> > +
> >> >    CFGBlock::const_succ_iterator SI = CurrBlock->succ_begin();
> >> >
> >> > -  if (*SI)   BlockInfo.addInfo(*SI,        CurrStates);
> >> > -  if (*++SI) BlockInfo.addInfo(*SI, ElseOrMergeStates);
> >> > +  if (*SI)   BlockInfo.addInfo(*SI,  CurrStates);
> >> > +  if (*++SI) BlockInfo.addInfo(*SI, FalseStates);
> >> > +
> >> > +  CurrStates = NULL;
> >> > +  return true;
> >> >  }
> >> >
> >> >  void ConsumedAnalyzer::run(AnalysisDeclContext &AC) {
> >> > @@ -742,6 +1013,7 @@
> >> >    PostOrderCFGView *SortedGraph = AC.getAnalysis<PostOrderCFGView>();
> >> >
> >> >    CurrStates = new ConsumedStateMap();
> >> > +  ConsumedStmtVisitor Visitor(AC, *this);
> >> >
> >> >    // Visit all of the function's basic blocks.
> >> >    for (PostOrderCFGView::iterator I = SortedGraph->begin(),
> >> > @@ -752,9 +1024,19 @@
> >> >
> >> >      if (CurrStates == NULL)
> >> >        CurrStates = BlockInfo.getInfo(CurrBlock);
> >> > -
> >> > -    ConsumedStmtVisitor Visitor(AC, *this, CurrStates);
> >> > -
> >> > +
> >> > +    if (!CurrStates) {
> >> > +      CurrStates = NULL;
> >> > +      continue;
> >> > +
> >> > +    } else if (!CurrStates->isReachable()) {
> >> > +      delete CurrStates;
> >> > +      CurrStates = NULL;
> >> > +      continue;
> >> > +    }
> >> > +
> >> > +    Visitor.reset(CurrStates);
> >> > +
> >> >      // Visit all of the basic block's statements.
> >> >      for (CFGBlock::const_iterator BI = CurrBlock->begin(),
> >> >           BE = CurrBlock->end(); BI != BE; ++BI) {
> >> > @@ -770,36 +1052,34 @@
> >> >        }
> >> >      }
> >> >
> >> > -    if (const IfStmt *Terminator =
> >> > -
>  dyn_cast_or_null<IfStmt>(CurrBlock->getTerminator().getStmt())) {
> >> > +    // TODO: Handle other forms of branching with precision,
> including
> >> > while-
> >> > +    //       and for-loops. (Deferred)
> >> > +    if (!splitState(CurrBlock, Visitor)) {
> >> > +      CurrStates->setSource(NULL);
> >> >
> >> > -      splitState(CurrBlock, Terminator);
> >> > -      CurrStates = NULL;
> >> > -
> >> > -    } else if (CurrBlock->succ_size() > 1) {
> >> > -      CurrStates->makeUnknown();
> >> > -
> >> > -      bool OwnershipTaken = false;
> >> > -
> >> > -      for (CFGBlock::const_succ_iterator SI =
> CurrBlock->succ_begin(),
> >> > -           SE = CurrBlock->succ_end(); SI != SE; ++SI) {
> >> > +      if (CurrBlock->succ_size() > 1) {
> >> > +        CurrStates->makeUnknown();
> >> > +
> >> > +        bool OwnershipTaken = false;
> >> >
> >> > -        if (*SI) BlockInfo.addInfo(*SI, CurrStates, OwnershipTaken);
> >> > +        for (CFGBlock::const_succ_iterator SI =
> >> > CurrBlock->succ_begin(),
> >> > +             SE = CurrBlock->succ_end(); SI != SE; ++SI) {
> >> > +
> >> > +          if (*SI) BlockInfo.addInfo(*SI, CurrStates,
> OwnershipTaken);
> >> > +        }
> >> > +
> >> > +        if (!OwnershipTaken)
> >> > +          delete CurrStates;
> >> > +
> >> > +        CurrStates = NULL;
> >> > +
> >> > +      } else if (CurrBlock->succ_size() == 1 &&
> >> > +                 (*CurrBlock->succ_begin())->pred_size() > 1) {
> >> > +
> >> > +        BlockInfo.addInfo(*CurrBlock->succ_begin(), CurrStates);
> >> > +        CurrStates = NULL;
> >> >        }
> >> > -
> >> > -      if (!OwnershipTaken)
> >> > -        delete CurrStates;
> >> > -
> >> > -      CurrStates = NULL;
> >> > -
> >> > -    } else if (CurrBlock->succ_size() == 1 &&
> >> > -               (*CurrBlock->succ_begin())->pred_size() > 1) {
> >> > -
> >> > -      BlockInfo.addInfo(*CurrBlock->succ_begin(), CurrStates);
> >> > -      CurrStates = NULL;
> >> >      }
> >> > -
> >> > -    Visitor.reset();
> >> >    } // End of block iterator.
> >> >
> >> >    // Delete the last existing state map.
> >> > Index: test/SemaCXX/warn-consumed-analysis-strict.cpp
> >> > ===================================================================
> >> > --- test/SemaCXX/warn-consumed-analysis-strict.cpp
> >> > +++ test/SemaCXX/warn-consumed-analysis-strict.cpp
> >> > @@ -4,6 +4,8 @@
> >> >  #define CONSUMES __attribute__ ((consumes))
> >> >  #define TESTS_UNCONSUMED __attribute__ ((tests_unconsumed))
> >> >
> >> > +#define TEST_VAR(Var) Var.isValid()
> >> > +
> >> >  typedef decltype(nullptr) nullptr_t;
> >> >
> >> >  template <typename T>
> >> > @@ -11,7 +13,7 @@
> >> >    T var;
> >> >
> >> >    public:
> >> > -  ConsumableClass(void);
> >> > +  ConsumableClass();
> >> >    ConsumableClass(T val);
> >> >    ConsumableClass(ConsumableClass<T> &other);
> >> >    ConsumableClass(ConsumableClass<T> &&other);
> >> > @@ -26,20 +28,24 @@
> >> >    template <typename U>
> >> >    ConsumableClass<T>& operator=(ConsumableClass<U> &&other);
> >> >
> >> > -  void operator*(void) const CALLABLE_WHEN_UNCONSUMED;
> >> > +  void operator()(int a) CONSUMES;
> >> > +  void operator*() const CALLABLE_WHEN_UNCONSUMED;
> >> > +  void unconsumedCall() const CALLABLE_WHEN_UNCONSUMED;
> >> >
> >> > -  bool isValid(void) const TESTS_UNCONSUMED;
> >> > +  bool isValid() const TESTS_UNCONSUMED;
> >> > +  operator bool() const TESTS_UNCONSUMED;
> >> > +  bool operator!=(nullptr_t) const TESTS_UNCONSUMED;
> >> >
> >> > -  void constCall(void) const;
> >> > -  void nonconstCall(void);
> >> > +  void constCall() const;
> >> > +  void nonconstCall();
> >> >
> >> > -  void consume(void) CONSUMES;
> >> > +  void consume() CONSUMES;
> >> >  };
> >> >
> >> >  void baf0(ConsumableClass<int>  &var);
> >> >  void baf1(ConsumableClass<int>  *var);
> >> >
> >> > -void testIfStmt(void) {
> >> > +void testIfStmt() {
> >> >    ConsumableClass<int> var;
> >> >
> >> >    if (var.isValid()) { // expected-warning {{unnecessary test.
> Variable
> >> > 'var' is known to be in the 'consumed' state}}
> >> > @@ -51,27 +57,113 @@
> >> >    }
> >> >  }
> >> >
> >> > -void testConditionalMerge(void) {
> >> > -  ConsumableClass<int> var;
> >> > +void testComplexConditionals() {
> >> > +  ConsumableClass<int> var0, var1, var2;
> >> > +
> >> > +  // Coerce all variables into the unknown state.
> >> > +  baf0(var0);
> >> > +  baf0(var1);
> >> > +  baf0(var2);
> >> >
> >> > -  if (var.isValid()) {// expected-warning {{unnecessary test.
> Variable
> >> > 'var' is known to be in the 'consumed' state}}
> >> > +  if (var0 && var1) {
> >> > +    *var0;
> >> > +    *var1;
> >> >
> >> > -    // Empty
> >> > +  } else {
> >> > +    *var0; // expected-warning {{invocation of method 'operator*' on
> >> > object 'var0' while it is in an unknown state}}
> >> > +    *var1; // expected-warning {{invocation of method 'operator*' on
> >> > object 'var1' while it is in an unknown state}}
> >> >    }
> >> >
> >> > -  *var; // expected-warning {{invocation of method 'operator*' on
> >> > object 'var' while it is in an unknown state}}
> >> > +  if (var0 || var1) {
> >> > +    *var0; // expected-warning {{invocation of method 'operator*' on
> >> > object 'var0' while it is in an unknown state}}
> >> > +    *var1; // expected-warning {{invocation of method 'operator*' on
> >> > object 'var1' while it is in an unknown state}}
> >> > +
> >> > +  } else {
> >> > +    *var0; // expected-warning {{invocation of method 'operator*' on
> >> > object 'var0' while it is in the 'consumed' state}}
> >> > +    *var1; // expected-warning {{invocation of method 'operator*' on
> >> > object 'var1' while it is in the 'consumed' state}}
> >> > +  }
> >> >
> >> > -  if (var.isValid()) {
> >> > -    // Empty
> >> > +  if (var0 && !var1) {
> >> > +    *var0;
> >> > +    *var1; // expected-warning {{invocation of method 'operator*' on
> >> > object 'var1' while it is in the 'consumed' state}}
> >> >
> >> >    } else {
> >> > -    // Empty
> >> > +    *var0; // expected-warning {{invocation of method 'operator*' on
> >> > object 'var0' while it is in an unknown state}}
> >> > +    *var1; // expected-warning {{invocation of method 'operator*' on
> >> > object 'var1' while it is in an unknown state}}
> >> >    }
> >> >
> >> > -  *var; // expected-warning {{invocation of method 'operator*' on
> >> > object 'var' while it is in an unknown state}}
> >> > +  if (var0 || !var1) {
> >> > +    *var0; // expected-warning {{invocation of method 'operator*' on
> >> > object 'var0' while it is in an unknown state}}
> >> > +    *var1; // expected-warning {{invocation of method 'operator*' on
> >> > object 'var1' while it is in an unknown state}}
> >> > +
> >> > +  } else {
> >> > +    *var0; // expected-warning {{invocation of method 'operator*' on
> >> > object 'var0' while it is in the 'consumed' state}}
> >> > +    *var1;
> >> > +  }
> >> > +
> >> > +  if (!var0 && !var1) {
> >> > +    *var0; // expected-warning {{invocation of method 'operator*' on
> >> > object 'var0' while it is in the 'consumed' state}}
> >> > +    *var1; // expected-warning {{invocation of method 'operator*' on
> >> > object 'var1' while it is in the 'consumed' state}}
> >> > +
> >> > +  } else {
> >> > +    *var0; // expected-warning {{invocation of method 'operator*' on
> >> > object 'var0' while it is in an unknown state}}
> >> > +    *var1; // expected-warning {{invocation of method 'operator*' on
> >> > object 'var1' while it is in an unknown state}}
> >> > +  }
> >> > +
> >> > +  if (!(var0 || var1)) {
> >> > +    *var0; // expected-warning {{invocation of method 'operator*' on
> >> > object 'var0' while it is in the 'consumed' state}}
> >> > +    *var1; // expected-warning {{invocation of method 'operator*' on
> >> > object 'var1' while it is in the 'consumed' state}}
> >> > +
> >> > +  } else {
> >> > +    *var0; // expected-warning {{invocation of method 'operator*' on
> >> > object 'var0' while it is in an unknown state}}
> >> > +    *var1; // expected-warning {{invocation of method 'operator*' on
> >> > object 'var1' while it is in an unknown state}}
> >> > +  }
> >> > +
> >> > +  if (!var0 || !var1) {
> >> > +    *var0; // expected-warning {{invocation of method 'operator*' on
> >> > object 'var0' while it is in an unknown state}}
> >> > +    *var1; // expected-warning {{invocation of method 'operator*' on
> >> > object 'var1' while it is in an unknown state}}
> >> > +
> >> > +  } else {
> >> > +    *var0;
> >> > +    *var1;
> >> > +  }
> >> > +
> >> > +  if (!(var0 && var1)) {
> >> > +    *var0; // expected-warning {{invocation of method 'operator*' on
> >> > object 'var0' while it is in an unknown state}}
> >> > +    *var1; // expected-warning {{invocation of method 'operator*' on
> >> > object 'var1' while it is in an unknown state}}
> >> > +
> >> > +  } else {
> >> > +    *var0;
> >> > +    *var1;
> >> > +  }
> >> > +
> >> > +  if (var0 && var1 && var2) {
> >> > +    *var0;
> >> > +    *var1;
> >> > +    *var2;
> >> > +
> >> > +  } else {
> >> > +    *var0; // expected-warning {{invocation of method 'operator*' on
> >> > object 'var0' while it is in an unknown state}}
> >> > +    *var1; // expected-warning {{invocation of method 'operator*' on
> >> > object 'var1' while it is in an unknown state}}
> >> > +    *var2; // expected-warning {{invocation of method 'operator*' on
> >> > object 'var2' while it is in an unknown state}}
> >> > +  }
> >> > +
> >> > +#if 0
> >> > +  // FIXME: Get this test to pass.
> >> > +  if (var0 || var1 || var2) {
> >> > +    *var0; // expected-warning {{invocation of method 'operator*' on
> >> > object 'var0' while it is in an unknown state}}
> >> > +    *var1; // expected-warning {{invocation of method 'operator*' on
> >> > object 'var1' while it is in an unknown state}}
> >> > +    *var2; // expected-warning {{invocation of method 'operator*' on
> >> > object 'var2' while it is in an unknown state}}
> >> > +
> >> > +  } else {
> >> > +    *var0; // expected-warning {{invocation of method 'operator*' on
> >> > object 'var0' while it is in the 'consumed' state}}
> >> > +    *var1; // expected-warning {{invocation of method 'operator*' on
> >> > object 'var1' while it is in the 'consumed' state}}
> >> > +    *var2; // expected-warning {{invocation of method 'operator*' on
> >> > object 'var2' while it is in the 'consumed' state}}
> >> > +  }
> >> > +#endif
> >> >  }
> >> >
> >> > -void testCallingConventions(void) {
> >> > +void testCallingConventions() {
> >> >    ConsumableClass<int> var(42);
> >> >
> >> >    baf0(var);
> >> > @@ -82,14 +174,14 @@
> >> >    *var; // expected-warning {{invocation of method 'operator*' on
> >> > object 'var' while it is in an unknown state}}
> >> >  }
> >> >
> >> > -void testMoveAsignmentish(void) {
> >> > +void testMoveAsignmentish() {
> >> >    ConsumableClass<int> var;
> >> >
> >> >    var = nullptr;
> >> >    *var; // expected-warning {{invocation of method 'operator*' on
> >> > object 'var' while it is in an unknown state}}
> >> >  }
> >> >
> >> > -void testConstAndNonConstMemberFunctions(void) {
> >> > +void testConstAndNonConstMemberFunctions() {
> >> >    ConsumableClass<int> var(42);
> >> >
> >> >    var.constCall();
> >> > @@ -99,7 +191,15 @@
> >> >    *var; // expected-warning {{invocation of method 'operator*' on
> >> > object 'var' while it is in an unknown state}}
> >> >  }
> >> >
> >> > -void testSimpleForLoop(void) {
> >> > +void testNoWarnTestFromMacroExpansion() {
> >> > +  ConsumableClass<int> var(42);
> >> > +
> >> > +  if (TEST_VAR(var)) {
> >> > +    *var;
> >> > +  }
> >> > +}
> >> > +
> >> > +void testSimpleForLoop() {
> >> >    ConsumableClass<int> var;
> >> >
> >> >    for (int i = 0; i < 10; ++i) {
> >> > @@ -109,7 +209,7 @@
> >> >    *var; // expected-warning {{invocation of method 'operator*' on
> >> > object 'var' while it is in an unknown state}}
> >> >  }
> >> >
> >> > -void testSimpleWhileLoop(void) {
> >> > +void testSimpleWhileLoop() {
> >> >    int i = 0;
> >> >
> >> >    ConsumableClass<int> var;
> >> > Index: test/SemaCXX/warn-consumed-analysis.cpp
> >> > ===================================================================
> >> > --- test/SemaCXX/warn-consumed-analysis.cpp
> >> > +++ test/SemaCXX/warn-consumed-analysis.cpp
> >> > @@ -11,7 +11,7 @@
> >> >    T var;
> >> >
> >> >    public:
> >> > -  ConsumableClass(void);
> >> > +  ConsumableClass();
> >> >    ConsumableClass(nullptr_t p) CONSUMES;
> >> >    ConsumableClass(T val);
> >> >    ConsumableClass(ConsumableClass<T> &other);
> >> > @@ -28,17 +28,17 @@
> >> >    ConsumableClass<T>& operator=(ConsumableClass<U> &&other);
> >> >
> >> >    void operator()(int a) CONSUMES;
> >> > -  void operator*(void) const CALLABLE_WHEN_UNCONSUMED;
> >> > -  void unconsumedCall(void) const CALLABLE_WHEN_UNCONSUMED;
> >> > +  void operator*() const CALLABLE_WHEN_UNCONSUMED;
> >> > +  void unconsumedCall() const CALLABLE_WHEN_UNCONSUMED;
> >> >
> >> > -  bool isValid(void) const TESTS_UNCONSUMED;
> >> > +  bool isValid() const TESTS_UNCONSUMED;
> >> >    operator bool() const TESTS_UNCONSUMED;
> >> >    bool operator!=(nullptr_t) const TESTS_UNCONSUMED;
> >> >
> >> > -  void constCall(void) const;
> >> > -  void nonconstCall(void);
> >> > +  void constCall() const;
> >> > +  void nonconstCall();
> >> >
> >> > -  void consume(void) CONSUMES;
> >> > +  void consume() CONSUMES;
> >> >  };
> >> >
> >> >  void baf0(const ConsumableClass<int>  var);
> >> > @@ -47,7 +47,7 @@
> >> >
> >> >  void baf3(ConsumableClass<int> &&var);
> >> >
> >> > -void testInitialization(void) {
> >> > +void testInitialization() {
> >> >    ConsumableClass<int> var0;
> >> >    ConsumableClass<int> var1 = ConsumableClass<int>();
> >> >
> >> > @@ -58,10 +58,10 @@
> >> >
> >> >    if (var0.isValid()) {
> >> >      *var0;
> >> > -    *var1;  // expected-warning {{invocation of method 'operator*' on
> >> > object 'var1' while it is in the 'consumed' state}}
> >> > +    *var1;
> >> >
> >> >    } else {
> >> > -    *var0;  // expected-warning {{invocation of method 'operator*' on
> >> > object 'var0' while it is in the 'consumed' state}}
> >> > +    *var0; // expected-warning {{invocation of method 'operator*' on
> >> > object 'var0' while it is in the 'consumed' state}}
> >> >    }
> >> >  }
> >> >
> >> > @@ -69,7 +69,7 @@
> >> >    *ConsumableClass<int>(); // expected-warning {{invocation of method
> >> > 'operator*' on a temporary object while it is in the 'consumed'
> state}}
> >> >  }
> >> >
> >> > -void testSimpleRValueRefs(void) {
> >> > +void testSimpleRValueRefs() {
> >> >    ConsumableClass<int> var0;
> >> >    ConsumableClass<int> var1(42);
> >> >
> >> > @@ -82,39 +82,149 @@
> >> >    *var1; // expected-warning {{invocation of method 'operator*' on
> >> > object 'var1' while it is in the 'consumed' state}}
> >> >  }
> >> >
> >> > -void testIfStmt(void) {
> >> > +void testIfStmt() {
> >> >    ConsumableClass<int> var;
> >> >
> >> >    if (var.isValid()) {
> >> > -    // Empty
> >> > -
> >> > +    *var;
> >> >    } else {
> >> >      *var; // expected-warning {{invocation of method 'operator*' on
> >> > object 'var' while it is in the 'consumed' state}}
> >> >    }
> >> >
> >> >    if (!var.isValid()) {
> >> >      *var; // expected-warning {{invocation of method 'operator*' on
> >> > object 'var' while it is in the 'consumed' state}}
> >> > -
> >> >    } else {
> >> >      *var;
> >> >    }
> >> >
> >> >    if (var) {
> >> >      // Empty
> >> > -
> >> >    } else {
> >> >      *var; // expected-warning {{invocation of method 'operator*' on
> >> > object 'var' while it is in the 'consumed' state}}
> >> >    }
> >> >
> >> >    if (var != nullptr) {
> >> >      // Empty
> >> > -
> >> >    } else {
> >> >      *var; // expected-warning {{invocation of method 'operator*' on
> >> > object 'var' while it is in the 'consumed' state}}
> >> >    }
> >> >  }
> >> >
> >> > -void testCallingConventions(void) {
> >> > +void testComplexConditionals() {
> >> > +  ConsumableClass<int> var0, var1, var2;
> >> > +
> >> > +  if (var0 && var1) {
> >> > +    *var0;
> >> > +    *var1;
> >> > +
> >> > +  } else {
> >> > +    *var0; // expected-warning {{invocation of method 'operator*' on
> >> > object 'var0' while it is in the 'consumed' state}}
> >> > +    *var1; // expected-warning {{invocation of method 'operator*' on
> >> > object 'var1' while it is in the 'consumed' state}}
> >> > +  }
> >> > +
> >> > +  if (var0 || var1) {
> >> > +    *var0;
> >> > +    *var1;
> >> > +
> >> > +  } else {
> >> > +    *var0; // expected-warning {{invocation of method 'operator*' on
> >> > object 'var0' while it is in the 'consumed' state}}
> >> > +    *var1; // expected-warning {{invocation of method 'operator*' on
> >> > object 'var1' while it is in the 'consumed' state}}
> >> > +  }
> >> > +
> >> > +  if (var0 && !var1) {
> >> > +    *var0;
> >> > +    *var1;
> >> > +
> >> > +  } else {
> >> > +    *var0; // expected-warning {{invocation of method 'operator*' on
> >> > object 'var0' while it is in the 'consumed' state}}
> >> > +    *var1; // expected-warning {{invocation of method 'operator*' on
> >> > object 'var1' while it is in the 'consumed' state}}
> >> > +  }
> >> > +
> >> > +  if (var0 || !var1) {
> >> > +    *var0; // expected-warning {{invocation of method 'operator*' on
> >> > object 'var0' while it is in the 'consumed' state}}
> >> > +    *var1; // expected-warning {{invocation of method 'operator*' on
> >> > object 'var1' while it is in the 'consumed' state}}
> >> > +
> >> > +  } else {
> >> > +    *var0;
> >> > +    *var1;
> >> > +  }
> >> > +
> >> > +  if (!var0 && !var1) {
> >> > +    *var0; // expected-warning {{invocation of method 'operator*' on
> >> > object 'var0' while it is in the 'consumed' state}}
> >> > +    *var1; // expected-warning {{invocation of method 'operator*' on
> >> > object 'var1' while it is in the 'consumed' state}}
> >> > +
> >> > +  } else {
> >> > +    *var0;
> >> > +    *var1;
> >> > +  }
> >> > +
> >> > +  if (!var0 || !var1) {
> >> > +    *var0; // expected-warning {{invocation of method 'operator*' on
> >> > object 'var0' while it is in the 'consumed' state}}
> >> > +    *var1; // expected-warning {{invocation of method 'operator*' on
> >> > object 'var1' while it is in the 'consumed' state}}
> >> > +
> >> > +  } else {
> >> > +    *var0;
> >> > +    *var1;
> >> > +  }
> >> > +
> >> > +  if (!(var0 && var1)) {
> >> > +    *var0; // expected-warning {{invocation of method 'operator*' on
> >> > object 'var0' while it is in the 'consumed' state}}
> >> > +    *var1; // expected-warning {{invocation of method 'operator*' on
> >> > object 'var1' while it is in the 'consumed' state}}
> >> > +
> >> > +  } else {
> >> > +    *var0;
> >> > +    *var1;
> >> > +  }
> >> > +
> >> > +  if (!(var0 || var1)) {
> >> > +    *var0; // expected-warning {{invocation of method 'operator*' on
> >> > object 'var0' while it is in the 'consumed' state}}
> >> > +    *var1; // expected-warning {{invocation of method 'operator*' on
> >> > object 'var1' while it is in the 'consumed' state}}
> >> > +
> >> > +  } else {
> >> > +    *var0;
> >> > +    *var1;
> >> > +  }
> >> > +
> >> > +  if (var0 && var1 && var2) {
> >> > +    *var0;
> >> > +    *var1;
> >> > +    *var2;
> >> > +
> >> > +  } else {
> >> > +    *var0; // expected-warning {{invocation of method 'operator*' on
> >> > object 'var0' while it is in the 'consumed' state}}
> >> > +    *var1; // expected-warning {{invocation of method 'operator*' on
> >> > object 'var1' while it is in the 'consumed' state}}
> >> > +    *var2; // expected-warning {{invocation of method 'operator*' on
> >> > object 'var2' while it is in the 'consumed' state}}
> >> > +  }
> >> > +
> >> > +#if 0
> >> > +  // FIXME: Get this test to pass.
> >> > +  if (var0 || var1 || var2) {
> >> > +    *var0;
> >> > +    *var1;
> >> > +    *var2;
> >> > +
> >> > +  } else {
> >> > +    *var0; // expected-warning {{invocation of method 'operator*' on
> >> > object 'var0' while it is in the 'consumed' state}}
> >> > +    *var1; // expected-warning {{invocation of method 'operator*' on
> >> > object 'var1' while it is in the 'consumed' state}}
> >> > +    *var2; // expected-warning {{invocation of method 'operator*' on
> >> > object 'var2' while it is in the 'consumed' state}}
> >> > +  }
> >> > +#endif
> >> > +}
> >> > +
> >> > +void testStateChangeInBranch() {
> >> > +  ConsumableClass<int> var;
> >> > +
> >> > +  // Make var enter the 'unknown' state.
> >> > +  baf1(var);
> >> > +
> >> > +  if (!var) {
> >> > +    var = ConsumableClass<int>(42);
> >> > +  }
> >> > +
> >> > +  *var;
> >> > +}
> >> > +
> >> > +void testCallingConventions() {
> >> >    ConsumableClass<int> var(42);
> >> >
> >> >    baf0(var);
> >> > @@ -130,7 +240,7 @@
> >> >    *var; // expected-warning {{invocation of method 'operator*' on
> >> > object 'var' while it is in the 'consumed' state}}
> >> >  }
> >> >
> >> > -void testMoveAsignmentish(void) {
> >> > +void testMoveAsignmentish() {
> >> >    ConsumableClass<int>  var0;
> >> >    ConsumableClass<long> var1(42);
> >> >
> >> > @@ -143,26 +253,25 @@
> >> >    *var1; // expected-warning {{invocation of method 'operator*' on
> >> > object 'var1' while it is in the 'consumed' state}}
> >> >  }
> >> >
> >> > -void testConditionalMerge(void) {
> >> > +void testConditionalMerge() {
> >> >    ConsumableClass<int> var;
> >> >
> >> >    if (var.isValid()) {
> >> >      // Empty
> >> >    }
> >> >
> >> > -  *var;
> >> > +  *var; // expected-warning {{invocation of method 'operator*' on
> >> > object 'var' while it is in the 'consumed' state}}
> >> >
> >> >    if (var.isValid()) {
> >> >      // Empty
> >> > -
> >> >    } else {
> >> >      // Empty
> >> >    }
> >> >
> >> > -  *var;
> >> > +  *var; // expected-warning {{invocation of method 'operator*' on
> >> > object 'var' while it is in the 'consumed' state}}
> >> >  }
> >> >
> >> > -void testConsumes0(void) {
> >> > +void testConsumes0() {
> >> >    ConsumableClass<int> var(42);
> >> >
> >> >    *var;
> >> > @@ -172,13 +281,13 @@
> >> >    *var; // expected-warning {{invocation of method 'operator*' on
> >> > object 'var' while it is in the 'consumed' state}}
> >> >  }
> >> >
> >> > -void testConsumes1(void) {
> >> > +void testConsumes1() {
> >> >    ConsumableClass<int> var(nullptr);
> >> >
> >> >    *var; // expected-warning {{invocation of method 'operator*' on
> >> > object 'var' while it is in the 'consumed' state}}
> >> >  }
> >> >
> >> > -void testConsumes2(void) {
> >> > +void testConsumes2() {
> >> >    ConsumableClass<int> var(42);
> >> >
> >> >    var.unconsumedCall();
> >> > @@ -187,7 +296,19 @@
> >> >    var.unconsumedCall(); // expected-warning {{invocation of method
> >> > 'unconsumedCall' on object 'var' while it is in the 'consumed' state}}
> >> >  }
> >> >
> >> > -void testSimpleForLoop(void) {
> >> > +void testNonsenseState() {
> >> > +  ConsumableClass<int> var(42);
> >> > +
> >> > +  if (var) {
> >> > +    *var;
> >> > +  } else {
> >> > +    *var;
> >> > +  }
> >> > +
> >> > +  *var;
> >> > +}
> >> > +
> >> > +void testSimpleForLoop() {
> >> >    ConsumableClass<int> var;
> >> >
> >> >    for (int i = 0; i < 10; ++i) {
> >> > @@ -197,7 +318,7 @@
> >> >    *var;
> >> >  }
> >> >
> >> > -void testSimpleWhileLoop(void) {
> >> > +void testSimpleWhileLoop() {
> >> >    int i = 0;
> >> >
> >> >    ConsumableClass<int> var;
> >> >
> >>
> >> On Fri, Aug 23, 2013 at 8:58 PM, Christian Wailes
> >> <chriswailes at google.com> wrote:
> >> >
> >> >   This depends on patch http://llvm-reviews.chandlerc.com/D1500
> >> >
> >> > http://llvm-reviews.chandlerc.com/D1501
> >
> >
>
> ~Aaron
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.llvm.org/pipermail/cfe-commits/attachments/20130826/992f11c9/attachment.html>


More information about the cfe-commits mailing list