behaviour of EvaluatedExprVisitor vs. StmtVisitor

I am trying to visit each expression inside of a CFGBlock exactly
once. To do this, I iterate through the statements in a block, and
then call a visitor on each statement.

    for (CFGBlock::const_iterator I = CurrBlock->begin(), E = CurrBlock->end();
         I != E; ++I) {
      if (const CFGStmt *CfgStmt = dyn_cast<CFGStmt>(&*I)) {
        MyVisitor.Visit(CfgStmt->getStmt());
      }
    }

However, I have been having some strange behaviour. When MyVisitor is
a subclass of EvaluatedExprVisitor<MyVisitor>, I end up seeing some
DeclRefExprs multiple times. In contrast, when MyVisitor is a subclass
of StmtVisitor<MyVisitor>, I see the DeclRefExprs just once. Is this
expected behaviour?

For a more specific example, it looks like when visiting the CFGBlocks
inside this function:

void myFunction() {
  int x = y;
}

with EvaluatedExprVisitor<MyVisitor>, I visit the DeclRefExpr for y three times.

Cheers,

Caitlin

EvaluatedExprVisitor does a recursive visit of an expression:

/// \begin Given a potentially-evaluated expression, this visitor visits all
/// of its potentially-evaluated subexpressions, recursively.

The CFG is linearized, which means every expression appears in the CFG. There are three expressions in the CFG: the reference to 'y', and implicit lvalue-to-rvalue cast around 'y', and the initialization of 'x'. The latter two have the reference to 'y' as a nested expression. This means if you visit each one, you will see 'y' three times.

Ted,

Ok, that makes sense.

Here is my impression: For example,if I want to be sure to visit each
DeclRefExpr inside of a CFGBlock, I can just iterate over the CFGStmts
making up the block and use the (nonrecursive) StmtVisitor to visit
DeclRefExprs (as in the code above). Is this true, or are there any
corner cases I should be concerned about? I am a bit confused by the
"AlwaysAdd" vs. "NotAlwaysAdd" enum in AddStmtChoice in CFG.cpp.

Cheers,

Caitlin

Ah, good point. You have flexibility in how the CFG is constructed.

For the static analyzer, we currently linearize all expressions/statements for the constructed CFG. That’s what I was referring to before.

For the compiler warnings, we only put in a limited subset of the expressions into the CFG, because the current analyses in the frontend (e.g., -Wuninitialized) only look at some of the expressions, thus putting all expressions in the CFG would make those analyses a bit slower. DeclRefExprs happen to be one of them that is always in the CFG in both cases.

So, yes, you just need to iterate over CFGStmts in the CFG to find all DeclRefExprs. You don’t even need a StmtVisitor. Just use a dyn_cast<> on the Stmt*.