c++clang-tidyclang-ast-matchers

How to get the source location for a ref qualifier when writing clang-tidy checks and fixes?


I would like to write a clang-tidy check that adds an lvalue ref qualifier to certain C++ methods. I have managed to detect the methods to fix, but I have troubles finding the right source location to add the qualifier to.

My attempt is this one:

void MyCheck::check(const MatchFinder::MatchResult &Result) {
  const auto *MatchedDecl = Result.Nodes.getNodeAs<CXXMethodDecl>("x");

  if (RQ_None != MatchedDecl->getRefQualifier()) {
    return;
  }

  diag(MatchedDecl->getBeginLoc(),
       "Member Function %0 should have lvalue ref qualifier.")
      << MatchedDecl
      << FixItHint::CreateInsertion(
             MatchedDecl->getTypeSpecEndLoc().getLocWithOffset(1), " &");
  diag(MatchedDecl->getTypeSpecEndLoc().getLocWithOffset(1),
       "Add ref-qualifier for lvalues.", DiagnosticIDs::Note);
}

It works well, if the C++ method that has been found specifies the return value before the method name:

Test& modify1();

is being fixed like this:

Test& modify1() &;

But if the method has a trailing return type, my source location for the insertion is not correct.

auto modify2() -> Test&

is being fixed like this:

auto modify2() -> Test& &;

which has a syntax error.

Instead of getTypeSpecEndLoc().getLocWithOffset(1) I should use the source code location where ref qualifiers are expected. How to figure out the location from the AST matcher?


Solution

  • As specified in section dcl.fct of the C++ standard, the ref-qualifier goes after the cv-qualifiers:

    D1 ( parameter-declaration-clause ) cv-qualifier-seqopt ref-qualifieropt noexcept-specifieropt attribute-specifier-seqopt trailing-return-typeopt

    Clang does not record a source location that precisely identifies the location "after cv-qualifiers", i.e., where the ref-qualifier would go. The closest it records is the location of the right parenthesis at the end of the parameter list. This can be retrieved from a FunctionDecl by calling getFunctionTypeLoc() to get a FunctionTypeLoc, and calling getRParenLoc() on that object.

    From there, you have to inspect the tokens at that location to check for and skip const and volatile. See the question Getting the token sequence from clang AST node for information about how to retrieve and inspect tokens.