1

I'm trying to match arguments passed to a templated member function invocation using clang-query as a precursor to writing a clang-tidy check. Whilst I can get non-templated member functions to match, I'm unable to get templated member functions to. I'm using clang-14.0.0.

Consider:

#include <string>

class BaseTrace {
public:
  template <typename... Args>
  void Templated(const char *fmt, Args &&...args) {
  }
  void NotTemplated(const char *fmt, const char *s1, const char *s2) {
  }
};

BaseTrace TRACE;

void trace1(const std::string &s1, const std::string &s2)
{
  TRACE.Templated("One:{} Two:{}\n", s1.c_str(), s2.c_str());
}

void trace2(const std::string &s1, const std::string &s2)
{
  TRACE.NotTemplated("One:{} Two:{}\n", s1.c_str(), s2.c_str());
}

Querying for normal member invocation function matches as expected:

clang-query> match match cxxMemberCallExpr(callee(functionDecl(hasName("NotTemplated"))), on(expr(hasType(cxxRecordDecl(hasName("::BaseTrace"))))), hasAnyArgument(cxxMemberCallExpr()))

Match #1:

/home/mac/git/llvm-project/build/../clang-tools-extra/test/clang-tidy/checkers/minimal-cstr.cpp:23:3: note: "root" binds here
  TRACE.NotTemplated("One:{} Two:{}\n", s1.c_str(), s2.c_str());
  ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1 match.

But querying for a templated member function invocation does not:

clang-query> match match cxxMemberCallExpr(callee(functionDecl(hasName("Templated"), isTemplateInstantiation())), on(expr(hasType(cxxRecordDecl(hasName("::BaseTrace"))))), hasAnyArgument(cxxMemberCallExpr()))
0 matches.

The answer to this question implies that this ought to work. If I don't try to match the arguments then I am able to get a match:

clang-query> match cxxMemberCallExpr(callee(functionDecl(hasName("Templated"), isTemplateInstantiation())), on(anyOf(expr(hasType(cxxRecordDecl(hasName("::BaseTrace")))), expr(hasType(cxxRecordDecl(isDerivedFrom("::BaseTrace")))))))

Match #1:

/home/mac/git/llvm-project/build/../clang-tools-extra/test/clang-tidy/checkers/minimal-cstr.cpp:16:3: note: "root" binds here
  TRACE.Templated("One:{} Two:{}\n", s1.c_str(), s2.c_str());
  ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1 match.

I don't seem to have the same problem when matching non-member functions.

Am I missing something, or is this potentially a clang limitation.

Mike Crowe
  • 642
  • 6
  • 18
  • It seems that the problem is caused by the universal reference. If I remove the && from the second parameter to `Templated` then I get the expected match. – Mike Crowe Apr 03 '22 at 14:57

1 Answers1

0

I'm not convinced that I've got to the bottom of this, but it appears that I can make the Templated method match by either using:

clang-query> set traversal IgnoreUnlessSpelledInSource

rather than the AsIs default with the match expression in the question, or by modifying the match expression to be:

clang-query> match cxxMemberCallExpr(callee(functionDecl(hasName("Templated"), isTemplateInstantiation())), on(expr(hasType(cxxRecordDecl(hasName("::BaseTrace"))))), hasAnyArgument(materializeTemporaryExpr(has(cxxMemberCallExpr()))))

to match the extra MaterializeTemporaryExpr node in the syntax tree.

Mike Crowe
  • 642
  • 6
  • 18