Marc Mutz - mmutz
Marc Mutz - mmutz

Reputation: 25293

Clang AST Matchers: How to find calls to a perfectly forwarding function called with rvalues?

Given a function template such as:

template <typename T> void function(T &&t) { /*...*/ }

how do I find calls to the function that pass rvalues:

function(1); // MATCH
int i;
function(i); // SKIP
int foo();
function(foo()); // MATCH
...

You get the idea.

I was thinking about something like:

callExpr(callee(functionDecl(
                    hasName("function"),
                    unless(hasTemplateArgument(0,
                        refersToType(references(anything()))))))

to filter out the case where T is deduced as a reference type (indicating an lvalue was passed), but I don't see how I can connect the Matcher<FunctionDecl> expected by functionDecl to the Matcher<TemplateSpecializationType> returned from hasTemplateArgument.

I'm using Clang 3.8, in case it matters (the online docs seem to be at 5.0.0, and http://releases.llvm.org/3.8.0/tools/clang/docs/LibASTMatchersReference.html gives a 404).

Upvotes: 1

Views: 2262

Answers (2)

Some Who Call Me Tim
Some Who Call Me Tim

Reputation: 1111

Here's a slightly different approach that interrogates the type of the parameter:

callExpr(
  callee(
    functionDecl(           // could also narrow on name, param count etc
      hasAnyParameter(      // could also use hasParameter(n,...)
        parmVarDecl(
          hasType(
            rValueReferenceType()
          )
        ).bind("pdecl")
      ) 
    ).bind("fdecl")
  )
)

On this test code:

template <typename T> void g(T &&t){}

template <typename T> void g(T &t){}

void g(){
  int i = 2;
  g<int>(i);
  g<int>(2);
}

clang-query shows that the matcher matches the first (rval) call, and not the second (lval):

Match #1:

test_input_rval_call.cc:1:23: note: "fdecl" binds here
template <typename T> void g(T &&t){}
                      ^~~~~~~~~~~~~~~
test_input_rval_call.cc:1:30: note: "pdecl" binds here
template <typename T> void g(T &&t){}
                             ^~~~~
test_input_rval_call.cc:8:3: note: "root" binds here
  g<int>(2);
  ^~~~~~~~~
1 match.

Upvotes: 1

Marc Mutz - mmutz
Marc Mutz - mmutz

Reputation: 25293

This appears to work:

callExpr(hasDeclaration(functionDecl(hasName("function"))),
         hasArgument(0, cxxBindTemporaryExpr()))

though I'm sure it misses some scenarios.

Upvotes: 0

Related Questions